agentix-cli 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (191) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +102 -103
  3. package/agentx.example.json +9 -3
  4. package/dist/App-6Y7B3YQH.js +8 -0
  5. package/dist/App-6Y7B3YQH.js.map +1 -0
  6. package/dist/ChatApp-KIEUJ2DS.js +7 -0
  7. package/dist/ChatApp-KIEUJ2DS.js.map +1 -0
  8. package/dist/{agent-FSE4KRLX.js → agent-QGZIAZMY.js} +2 -2
  9. package/dist/architect-SEPWMWS4.js +2 -0
  10. package/dist/{board-dashboard-PUACKWAN.js → board-dashboard-CQ5F4GPD.js} +3756 -1010
  11. package/dist/board-dashboard-CQ5F4GPD.js.map +1 -0
  12. package/dist/builtin-FRKAQMFS.js +2 -0
  13. package/dist/chunk-2WVX3LEC.js +2 -0
  14. package/dist/chunk-2WVX3LEC.js.map +1 -0
  15. package/dist/chunk-3WQFCIB6.js +24 -0
  16. package/dist/chunk-3WQFCIB6.js.map +1 -0
  17. package/dist/chunk-53GCFZPR.js +116 -0
  18. package/dist/chunk-53GCFZPR.js.map +1 -0
  19. package/dist/{chunk-IKBHHTIT.js → chunk-5U35WAZF.js} +2 -2
  20. package/dist/chunk-67Q4DCPS.js +54 -0
  21. package/dist/chunk-67Q4DCPS.js.map +1 -0
  22. package/dist/chunk-76Z4VHDX.js +8 -0
  23. package/dist/chunk-76Z4VHDX.js.map +1 -0
  24. package/dist/{chunk-7P5QRX5W.js → chunk-BFXONUUN.js} +2 -2
  25. package/dist/{chunk-DXTIG5YV.js → chunk-FDMWOPEL.js} +2 -2
  26. package/dist/chunk-FDWOECJY.js +54 -0
  27. package/dist/chunk-FDWOECJY.js.map +1 -0
  28. package/dist/chunk-G57RZCXM.js +5 -0
  29. package/dist/chunk-G57RZCXM.js.map +1 -0
  30. package/dist/{chunk-NOF4HSGK.js → chunk-H5D7ATXS.js} +2 -2
  31. package/dist/chunk-HA3XI7Z2.js +155 -0
  32. package/dist/chunk-HA3XI7Z2.js.map +1 -0
  33. package/dist/chunk-HR4JSRZS.js +3 -0
  34. package/dist/{chunk-A6BKSZQA.js.map → chunk-HR4JSRZS.js.map} +1 -1
  35. package/dist/chunk-I3SIXTJO.js +44 -0
  36. package/dist/chunk-I3SIXTJO.js.map +1 -0
  37. package/dist/chunk-ISEBLHWX.js +118 -0
  38. package/dist/chunk-ISEBLHWX.js.map +1 -0
  39. package/dist/{chunk-4ITQP3XL.js → chunk-JP5IEXR7.js} +2 -2
  40. package/dist/chunk-JXWAGMI2.js +13 -0
  41. package/dist/chunk-JXWAGMI2.js.map +1 -0
  42. package/dist/chunk-KUCCW2VG.js +431 -0
  43. package/dist/chunk-KUCCW2VG.js.map +1 -0
  44. package/dist/chunk-LDHPMJ7M.js +257 -0
  45. package/dist/chunk-LDHPMJ7M.js.map +1 -0
  46. package/dist/{chunk-KWUWZQGV.js → chunk-LNXCJWOU.js} +29 -30
  47. package/dist/chunk-LNXCJWOU.js.map +1 -0
  48. package/dist/chunk-M4AMWP3H.js +178 -0
  49. package/dist/chunk-M4AMWP3H.js.map +1 -0
  50. package/dist/chunk-NGSIRE4X.js +126 -0
  51. package/dist/chunk-NGSIRE4X.js.map +1 -0
  52. package/dist/{chunk-CNNLREHD.js → chunk-P4VWIVKN.js} +268 -410
  53. package/dist/chunk-P4VWIVKN.js.map +1 -0
  54. package/dist/chunk-Q6YO7LYE.js +47 -0
  55. package/dist/chunk-Q6YO7LYE.js.map +1 -0
  56. package/dist/chunk-TMKLHTCG.js +971 -0
  57. package/dist/chunk-TMKLHTCG.js.map +1 -0
  58. package/dist/chunk-TRJTTBE2.js +7 -0
  59. package/dist/chunk-TRJTTBE2.js.map +1 -0
  60. package/dist/chunk-U2CKL755.js +4 -0
  61. package/dist/chunk-U2CKL755.js.map +1 -0
  62. package/dist/chunk-UCQ2N3KO.js +9 -0
  63. package/dist/chunk-UCQ2N3KO.js.map +1 -0
  64. package/dist/chunk-W4YR2RO2.js +2 -0
  65. package/dist/chunk-W4YR2RO2.js.map +1 -0
  66. package/dist/cli.js +1 -1
  67. package/dist/cli.js.map +1 -1
  68. package/dist/client-GGVVLOOF.js +2 -0
  69. package/dist/{compaction-BTUJJ3LJ.js → compaction-NWEMOSHA.js} +3 -3
  70. package/dist/config-7FSZFRTQ.js +2 -0
  71. package/dist/config-mutator-POCOYYJ4.js +2 -0
  72. package/dist/{context-planner-I7ID7XF3.js → context-planner-45GEWKGL.js} +2 -2
  73. package/dist/doctor-DHLBEKA4.js +2 -0
  74. package/dist/heal-HZ3EQD52.js +2 -0
  75. package/dist/index.d.ts +1193 -225
  76. package/dist/index.js +12 -12
  77. package/dist/index.js.map +1 -1
  78. package/dist/memory-extract-AENFVVO5.js +2 -0
  79. package/dist/processes-UIC4XXA6.js +2 -0
  80. package/dist/program-WVAVJFND.js +2 -0
  81. package/dist/projects-api-KRME2YKZ.js +2 -0
  82. package/dist/projects-api-KRME2YKZ.js.map +1 -0
  83. package/dist/projects-mutate-3NHVG437.js +7 -0
  84. package/dist/projects-mutate-3NHVG437.js.map +1 -0
  85. package/dist/providers-L27BJ5A6.js +2 -0
  86. package/dist/registry-T36ZIAWV.js +2 -0
  87. package/dist/rotation-memo-B3EXXSEH.js +13 -0
  88. package/dist/rotation-memo-B3EXXSEH.js.map +1 -0
  89. package/dist/scheduler-GH7TIKYS.js +2 -0
  90. package/dist/surface-usage-PRL6Z4U3.js +2 -0
  91. package/dist/wiki-M6YNRFC4.js +2 -0
  92. package/dist/workflows/templates/branching.yaml +75 -0
  93. package/dist/workflows/templates/extract.yaml +59 -0
  94. package/dist/workflows/templates/linear.yaml +37 -0
  95. package/dist/workflows/templates/retry.yaml +61 -0
  96. package/dist/workspace-setup-TBQAHYHJ.js +2 -0
  97. package/package.json +10 -3
  98. package/dist/board-dashboard-PUACKWAN.js.map +0 -1
  99. package/dist/builtin-TQKMR42Z.js +0 -2
  100. package/dist/chunk-3SHMHYPD.js +0 -450
  101. package/dist/chunk-3SHMHYPD.js.map +0 -1
  102. package/dist/chunk-4INOOHND.js +0 -6
  103. package/dist/chunk-4INOOHND.js.map +0 -1
  104. package/dist/chunk-5F3J55LG.js +0 -2
  105. package/dist/chunk-5F3J55LG.js.map +0 -1
  106. package/dist/chunk-72PAN7UI.js +0 -103
  107. package/dist/chunk-72PAN7UI.js.map +0 -1
  108. package/dist/chunk-A6BKSZQA.js +0 -3
  109. package/dist/chunk-BHSPAP4U.js +0 -44
  110. package/dist/chunk-BHSPAP4U.js.map +0 -1
  111. package/dist/chunk-BOUKM2PS.js +0 -163
  112. package/dist/chunk-BOUKM2PS.js.map +0 -1
  113. package/dist/chunk-CDNWCYF7.js +0 -2
  114. package/dist/chunk-CDNWCYF7.js.map +0 -1
  115. package/dist/chunk-CNNLREHD.js.map +0 -1
  116. package/dist/chunk-EK37JHJI.js +0 -13
  117. package/dist/chunk-EK37JHJI.js.map +0 -1
  118. package/dist/chunk-EQPUB7CZ.js +0 -685
  119. package/dist/chunk-EQPUB7CZ.js.map +0 -1
  120. package/dist/chunk-EW27SCCC.js +0 -2
  121. package/dist/chunk-EW27SCCC.js.map +0 -1
  122. package/dist/chunk-GOLZ6NEL.js +0 -111
  123. package/dist/chunk-GOLZ6NEL.js.map +0 -1
  124. package/dist/chunk-JGXE2YNV.js +0 -46
  125. package/dist/chunk-JGXE2YNV.js.map +0 -1
  126. package/dist/chunk-KWUWZQGV.js.map +0 -1
  127. package/dist/chunk-MTRWXRFF.js +0 -380
  128. package/dist/chunk-MTRWXRFF.js.map +0 -1
  129. package/dist/chunk-N67JZAXU.js +0 -2
  130. package/dist/chunk-N67JZAXU.js.map +0 -1
  131. package/dist/chunk-PD5Q7D6Z.js +0 -7
  132. package/dist/chunk-PD5Q7D6Z.js.map +0 -1
  133. package/dist/chunk-Q6CAFGXT.js +0 -118
  134. package/dist/chunk-Q6CAFGXT.js.map +0 -1
  135. package/dist/chunk-WJXU5F4Z.js +0 -53
  136. package/dist/chunk-WJXU5F4Z.js.map +0 -1
  137. package/dist/chunk-ZZFXI3CS.js +0 -8
  138. package/dist/chunk-ZZFXI3CS.js.map +0 -1
  139. package/dist/config-ADS54KLC.js +0 -2
  140. package/dist/config-mutator-WTRMBZTO.js +0 -2
  141. package/dist/doctor-T6RB4Q5X.js +0 -2
  142. package/dist/editor-chat-RVLCKKB7.js +0 -137
  143. package/dist/editor-chat-RVLCKKB7.js.map +0 -1
  144. package/dist/heal-YEQYWX2L.js +0 -2
  145. package/dist/inbox-BIN2S5XN.js +0 -2
  146. package/dist/memory-extract-KRCHD3OP.js +0 -2
  147. package/dist/processes-RJ6RDP32.js +0 -2
  148. package/dist/program-V7NGFB2R.js +0 -2
  149. package/dist/providers-WVCMIW67.js +0 -2
  150. package/dist/registry-42HEWCLA.js +0 -2
  151. package/dist/rotation-memo-GMTZPMNE.js +0 -13
  152. package/dist/rotation-memo-GMTZPMNE.js.map +0 -1
  153. package/dist/scheduler-C56ABAVP.js +0 -2
  154. package/dist/slack-6P7ZJ32Y.js +0 -3
  155. package/dist/slack-6P7ZJ32Y.js.map +0 -1
  156. package/dist/store-BAPDZHXE.js +0 -2
  157. package/dist/task-store-RHISEU6V.js +0 -2
  158. package/dist/telegram-PIYDRM46.js +0 -3
  159. package/dist/telegram-PIYDRM46.js.map +0 -1
  160. package/dist/types-ZRLHHCGB.js +0 -2
  161. package/dist/web/workflow-editor.global.js +0 -1452
  162. package/dist/web/workflow-editor.global.js.map +0 -1
  163. package/dist/whatsapp-PFRDMUMT.js +0 -3
  164. package/dist/whatsapp-PFRDMUMT.js.map +0 -1
  165. package/dist/wiki-53W5NLZV.js +0 -2
  166. package/dist/workspace-setup-DQCZX2NE.js +0 -2
  167. package/dist/workspace-setup-DQCZX2NE.js.map +0 -1
  168. /package/dist/{agent-FSE4KRLX.js.map → agent-QGZIAZMY.js.map} +0 -0
  169. /package/dist/{builtin-TQKMR42Z.js.map → architect-SEPWMWS4.js.map} +0 -0
  170. /package/dist/{config-ADS54KLC.js.map → builtin-FRKAQMFS.js.map} +0 -0
  171. /package/dist/{chunk-IKBHHTIT.js.map → chunk-5U35WAZF.js.map} +0 -0
  172. /package/dist/{chunk-7P5QRX5W.js.map → chunk-BFXONUUN.js.map} +0 -0
  173. /package/dist/{chunk-DXTIG5YV.js.map → chunk-FDMWOPEL.js.map} +0 -0
  174. /package/dist/{chunk-NOF4HSGK.js.map → chunk-H5D7ATXS.js.map} +0 -0
  175. /package/dist/{chunk-4ITQP3XL.js.map → chunk-JP5IEXR7.js.map} +0 -0
  176. /package/dist/{config-mutator-WTRMBZTO.js.map → client-GGVVLOOF.js.map} +0 -0
  177. /package/dist/{compaction-BTUJJ3LJ.js.map → compaction-NWEMOSHA.js.map} +0 -0
  178. /package/dist/{doctor-T6RB4Q5X.js.map → config-7FSZFRTQ.js.map} +0 -0
  179. /package/dist/{heal-YEQYWX2L.js.map → config-mutator-POCOYYJ4.js.map} +0 -0
  180. /package/dist/{context-planner-I7ID7XF3.js.map → context-planner-45GEWKGL.js.map} +0 -0
  181. /package/dist/{inbox-BIN2S5XN.js.map → doctor-DHLBEKA4.js.map} +0 -0
  182. /package/dist/{memory-extract-KRCHD3OP.js.map → heal-HZ3EQD52.js.map} +0 -0
  183. /package/dist/{processes-RJ6RDP32.js.map → memory-extract-AENFVVO5.js.map} +0 -0
  184. /package/dist/{program-V7NGFB2R.js.map → processes-UIC4XXA6.js.map} +0 -0
  185. /package/dist/{providers-WVCMIW67.js.map → program-WVAVJFND.js.map} +0 -0
  186. /package/dist/{registry-42HEWCLA.js.map → providers-L27BJ5A6.js.map} +0 -0
  187. /package/dist/{scheduler-C56ABAVP.js.map → registry-T36ZIAWV.js.map} +0 -0
  188. /package/dist/{store-BAPDZHXE.js.map → scheduler-GH7TIKYS.js.map} +0 -0
  189. /package/dist/{task-store-RHISEU6V.js.map → surface-usage-PRL6Z4U3.js.map} +0 -0
  190. /package/dist/{types-ZRLHHCGB.js.map → wiki-M6YNRFC4.js.map} +0 -0
  191. /package/dist/{wiki-53W5NLZV.js.map → workspace-setup-TBQAHYHJ.js.map} +0 -0
@@ -0,0 +1,3 @@
1
+ import{readFileSync as a,existsSync as p}from"fs";import{resolve as d}from"path";function g(e){let n=[];for(let i of e.split(`
2
+ `)){let t=i.trim();if(!t||t.startsWith("#"))continue;let r=t.startsWith("export ")?t.slice(7).trim():t,o=r.indexOf("=");if(o===-1)continue;let c=r.slice(0,o).trim(),s=r.slice(o+1).trim();s.length>=2&&(s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))&&(s=s.slice(1,-1)),c&&n.push([c,s])}return n}function u(e){let n={};for(let i of[".env",".env.gitlab"]){let t=d(e,i);if(p(t))try{for(let[r,o]of g(a(t,"utf-8")))n[r]=o}catch{}}return n}var f=new Map;function l(e=process.cwd()){let n=f.get(e);if(n)return n;let i=d(e,".env"),t;if(!p(i))t=new Set;else try{t=new Set(g(a(i,"utf-8")).map(([r])=>r))}catch{t=new Set}return f.set(e,t),t}function m(e,n=process.env,i=process.cwd()){let t=u(e),r=l(i),o={...n};for(let c of r)c in t||delete o[c];for(let[c,s]of Object.entries(t))o[c]=s;return o}function E(e){return delete e.ANTHROPIC_API_KEY,delete e.ANTHROPIC_API_KEY_OLD,e}export{m as a,E as b};
3
+ //# sourceMappingURL=chunk-HR4JSRZS.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/utils/workspace-env.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// Loads per-agent secrets from the agent's workspace and tells the runtime\n// which keys the daemon's own .env injected, so we can strip those before\n// spawning the agent's claude process. Without this, every agent inherits\n// the daemon's GITLAB_TOKEN regardless of whether their workspace has its\n// own .env.gitlab — defeating per-agent identity boundaries (each agent\n// should commit/push as itself, not as the daemon's account).\n\n/**\n * Parse a dotenv-style file. Tolerates the `export KEY=VALUE` form used by\n * shell-sourced files like .env.gitlab on clawd-server.\n */\nfunction parseDotEnv(content: string): Array<[string, string]> {\n const out: Array<[string, string]> = []\n for (const raw of content.split(\"\\n\")) {\n const trimmed = raw.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const stripped = trimmed.startsWith(\"export \") ? trimmed.slice(7).trim() : trimmed\n const eq = stripped.indexOf(\"=\")\n if (eq === -1) continue\n const key = stripped.slice(0, eq).trim()\n let value = stripped.slice(eq + 1).trim()\n if (value.length >= 2 && ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\")))) {\n value = value.slice(1, -1)\n }\n if (key) out.push([key, value])\n }\n return out\n}\n\n/**\n * Read .env then .env.gitlab from a workspace and return their merged keys.\n * .env.gitlab wins on collision — it's the more specific per-channel file.\n */\nexport function loadWorkspaceEnv(workspace: string): Record<string, string> {\n const merged: Record<string, string> = {}\n for (const fname of [\".env\", \".env.gitlab\"]) {\n const path = resolve(workspace, fname)\n if (!existsSync(path)) continue\n try {\n for (const [k, v] of parseDotEnv(readFileSync(path, \"utf-8\"))) {\n merged[k] = v\n }\n } catch { /* best-effort */ }\n }\n return merged\n}\n\n/**\n * Keys defined in the daemon's own .env file, cached per daemonCwd. The\n * runtime uses this to strip those keys from the child env before adding\n * the workspace overrides — so a daemon-level GITLAB_TOKEN never leaks to\n * an agent that hasn't put one in its own workspace.\n */\nconst daemonEnvKeysCache = new Map<string, Set<string>>()\n\nexport function getDaemonEnvKeys(daemonCwd: string = process.cwd()): Set<string> {\n const cached = daemonEnvKeysCache.get(daemonCwd)\n if (cached) return cached\n const path = resolve(daemonCwd, \".env\")\n let keys: Set<string>\n if (!existsSync(path)) {\n keys = new Set()\n } else {\n try {\n keys = new Set(parseDotEnv(readFileSync(path, \"utf-8\")).map(([k]) => k))\n } catch {\n keys = new Set()\n }\n }\n daemonEnvKeysCache.set(daemonCwd, keys)\n return keys\n}\n\n/** Test-only: clear the cache so a different daemonCwd is re-read. */\nexport function _resetDaemonEnvKeysCache(): void {\n daemonEnvKeysCache.clear()\n}\n\n/**\n * Build the env object passed to a spawned agent process. Starts from\n * parentEnv (default process.env), removes any keys that came from the\n * daemon's own .env (so daemon-level secrets don't leak), then layers in\n * workspace .env / .env.gitlab.\n *\n * - System env (PATH, HOME, USER, …) survives untouched.\n * - Daemon-level secrets (e.g. GITLAB_TOKEN in /home/clawd/agentx/.env) are\n * stripped UNLESS the workspace provides its own value for that key.\n * - Workspace env (per-agent .env / .env.gitlab) wins on the final merge.\n */\nexport function buildAgentEnv(\n workspace: string,\n parentEnv: NodeJS.ProcessEnv = process.env,\n daemonCwd: string = process.cwd(),\n): NodeJS.ProcessEnv {\n const workspaceEnv = loadWorkspaceEnv(workspace)\n const daemonOwnedKeys = getDaemonEnvKeys(daemonCwd)\n const child: NodeJS.ProcessEnv = { ...parentEnv }\n for (const k of daemonOwnedKeys) {\n if (!(k in workspaceEnv)) delete child[k]\n }\n for (const [k, v] of Object.entries(workspaceEnv)) {\n child[k] = v\n }\n return child\n}\n"],"mappings":"AAAA,OAAS,gBAAAA,EAAc,cAAAC,MAAkB,KACzC,OAAS,WAAAC,MAAe,OAaxB,SAASC,EAAYC,EAA0C,CAC7D,IAAMC,EAA+B,CAAC,EACtC,QAAWC,KAAOF,EAAQ,MAAM;AAAA,CAAI,EAAG,CACrC,IAAMG,EAAUD,EAAI,KAAK,EACzB,GAAI,CAACC,GAAWA,EAAQ,WAAW,GAAG,EAAG,SACzC,IAAMC,EAAWD,EAAQ,WAAW,SAAS,EAAIA,EAAQ,MAAM,CAAC,EAAE,KAAK,EAAIA,EACrEE,EAAKD,EAAS,QAAQ,GAAG,EAC/B,GAAIC,IAAO,GAAI,SACf,IAAMC,EAAMF,EAAS,MAAM,EAAGC,CAAE,EAAE,KAAK,EACnCE,EAAQH,EAAS,MAAMC,EAAK,CAAC,EAAE,KAAK,EACpCE,EAAM,QAAU,IAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,GAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,KACtHA,EAAQA,EAAM,MAAM,EAAG,EAAE,GAEvBD,GAAKL,EAAI,KAAK,CAACK,EAAKC,CAAK,CAAC,EAEhC,OAAON,CACT,CAMO,SAASO,EAAiBC,EAA2C,CAC1E,IAAMC,EAAiC,CAAC,EACxC,QAAWC,IAAS,CAAC,OAAQ,aAAa,EAAG,CAC3C,IAAMC,EAAOd,EAAQW,EAAWE,CAAK,EACrC,GAAKd,EAAWe,CAAI,EACpB,GAAI,CACF,OAAW,CAACC,EAAGC,CAAC,IAAKf,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAC1DF,EAAOG,CAAC,EAAIC,CAEhB,MAAE,CAA0B,EAE9B,OAAOJ,CACT,CAQA,IAAMK,EAAqB,IAAI,IAExB,SAASC,EAAiBC,EAAoB,QAAQ,IAAI,EAAgB,CAC/E,IAAMC,EAASH,EAAmB,IAAIE,CAAS,EAC/C,GAAIC,EAAQ,OAAOA,EACnB,IAAMN,EAAOd,EAAQmB,EAAW,MAAM,EAClCE,EACJ,GAAI,CAACtB,EAAWe,CAAI,EAClBO,EAAO,IAAI,QAEX,IAAI,CACFA,EAAO,IAAI,IAAIpB,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAAE,IAAI,CAAC,CAACC,CAAC,IAAMA,CAAC,CAAC,CACzE,MAAE,CACAM,EAAO,IAAI,GACb,CAEF,OAAAJ,EAAmB,IAAIE,EAAWE,CAAI,EAC/BA,CACT,CAkBO,SAASC,EACdC,EACAC,EAA+B,QAAQ,IACvCC,EAAoB,QAAQ,IAAI,EACb,CACnB,IAAMC,EAAeC,EAAiBJ,CAAS,EACzCK,EAAkBC,EAAiBJ,CAAS,EAC5CK,EAA2B,CAAE,GAAGN,CAAU,EAChD,QAAWO,KAAKH,EACRG,KAAKL,GAAe,OAAOI,EAAMC,CAAC,EAE1C,OAAW,CAACA,EAAGC,CAAC,IAAK,OAAO,QAAQN,CAAY,EAC9CI,EAAMC,CAAC,EAAIC,EAEb,OAAOF,CACT","names":["readFileSync","existsSync","resolve","parseDotEnv","content","out","raw","trimmed","stripped","eq","key","value","loadWorkspaceEnv","workspace","merged","fname","path","k","v","daemonEnvKeysCache","getDaemonEnvKeys","daemonCwd","cached","keys","buildAgentEnv","workspace","parentEnv","daemonCwd","workspaceEnv","loadWorkspaceEnv","daemonOwnedKeys","getDaemonEnvKeys","child","k","v"]}
1
+ {"version":3,"sources":["../src/utils/workspace-env.ts"],"sourcesContent":["import { readFileSync, existsSync } from \"fs\"\nimport { resolve } from \"path\"\n\n// Loads per-agent secrets from the agent's workspace and tells the runtime\n// which keys the daemon's own .env injected, so we can strip those before\n// spawning the agent's claude process. Without this, every agent inherits\n// the daemon's GITLAB_TOKEN regardless of whether their workspace has its\n// own .env.gitlab — defeating per-agent identity boundaries (each agent\n// should commit/push as itself, not as the daemon's account).\n\n/**\n * Parse a dotenv-style file. Tolerates the `export KEY=VALUE` form used by\n * shell-sourced files like .env.gitlab on clawd-server.\n */\nfunction parseDotEnv(content: string): Array<[string, string]> {\n const out: Array<[string, string]> = []\n for (const raw of content.split(\"\\n\")) {\n const trimmed = raw.trim()\n if (!trimmed || trimmed.startsWith(\"#\")) continue\n const stripped = trimmed.startsWith(\"export \") ? trimmed.slice(7).trim() : trimmed\n const eq = stripped.indexOf(\"=\")\n if (eq === -1) continue\n const key = stripped.slice(0, eq).trim()\n let value = stripped.slice(eq + 1).trim()\n if (value.length >= 2 && ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\")))) {\n value = value.slice(1, -1)\n }\n if (key) out.push([key, value])\n }\n return out\n}\n\n/**\n * Read .env then .env.gitlab from a workspace and return their merged keys.\n * .env.gitlab wins on collision — it's the more specific per-channel file.\n */\nexport function loadWorkspaceEnv(workspace: string): Record<string, string> {\n const merged: Record<string, string> = {}\n for (const fname of [\".env\", \".env.gitlab\"]) {\n const path = resolve(workspace, fname)\n if (!existsSync(path)) continue\n try {\n for (const [k, v] of parseDotEnv(readFileSync(path, \"utf-8\"))) {\n merged[k] = v\n }\n } catch { /* best-effort */ }\n }\n return merged\n}\n\n/**\n * Keys defined in the daemon's own .env file, cached per daemonCwd. The\n * runtime uses this to strip those keys from the child env before adding\n * the workspace overrides — so a daemon-level GITLAB_TOKEN never leaks to\n * an agent that hasn't put one in its own workspace.\n */\nconst daemonEnvKeysCache = new Map<string, Set<string>>()\n\nexport function getDaemonEnvKeys(daemonCwd: string = process.cwd()): Set<string> {\n const cached = daemonEnvKeysCache.get(daemonCwd)\n if (cached) return cached\n const path = resolve(daemonCwd, \".env\")\n let keys: Set<string>\n if (!existsSync(path)) {\n keys = new Set()\n } else {\n try {\n keys = new Set(parseDotEnv(readFileSync(path, \"utf-8\")).map(([k]) => k))\n } catch {\n keys = new Set()\n }\n }\n daemonEnvKeysCache.set(daemonCwd, keys)\n return keys\n}\n\n/** Test-only: clear the cache so a different daemonCwd is re-read. */\nexport function _resetDaemonEnvKeysCache(): void {\n daemonEnvKeysCache.clear()\n}\n\n/**\n * Build the env object passed to a spawned agent process. Starts from\n * parentEnv (default process.env), removes any keys that came from the\n * daemon's own .env (so daemon-level secrets don't leak), then layers in\n * workspace .env / .env.gitlab.\n *\n * - System env (PATH, HOME, USER, …) survives untouched.\n * - Daemon-level secrets (e.g. GITLAB_TOKEN in /home/clawd/agentx/.env) are\n * stripped UNLESS the workspace provides its own value for that key.\n * - Workspace env (per-agent .env / .env.gitlab) wins on the final merge.\n */\nexport function buildAgentEnv(\n workspace: string,\n parentEnv: NodeJS.ProcessEnv = process.env,\n daemonCwd: string = process.cwd(),\n): NodeJS.ProcessEnv {\n const workspaceEnv = loadWorkspaceEnv(workspace)\n const daemonOwnedKeys = getDaemonEnvKeys(daemonCwd)\n const child: NodeJS.ProcessEnv = { ...parentEnv }\n for (const k of daemonOwnedKeys) {\n if (!(k in workspaceEnv)) delete child[k]\n }\n for (const [k, v] of Object.entries(workspaceEnv)) {\n child[k] = v\n }\n return child\n}\n\n// Force the `claude` CLI to use OAuth/subscription billing. When\n// ANTHROPIC_API_KEY is present (env, systemd EnvironmentFile, daemon .env not\n// stripped because the workspace also defines it), the CLI silently switches\n// to the API-key path and any usage caps on that key surface as\n// \"You have reached your specified API usage limits...\". This caused\n// claude-code-tier agents to bypass the user's subscription. The same\n// stripping pattern lives in src/agent/providers/claude-code.ts for the\n// in-process provider — keep them in sync.\nexport function stripAnthropicApiKey(env: NodeJS.ProcessEnv): NodeJS.ProcessEnv {\n delete env.ANTHROPIC_API_KEY\n delete env.ANTHROPIC_API_KEY_OLD\n return env\n}\n"],"mappings":"AAAA,OAAS,gBAAAA,EAAc,cAAAC,MAAkB,KACzC,OAAS,WAAAC,MAAe,OAaxB,SAASC,EAAYC,EAA0C,CAC7D,IAAMC,EAA+B,CAAC,EACtC,QAAWC,KAAOF,EAAQ,MAAM;AAAA,CAAI,EAAG,CACrC,IAAMG,EAAUD,EAAI,KAAK,EACzB,GAAI,CAACC,GAAWA,EAAQ,WAAW,GAAG,EAAG,SACzC,IAAMC,EAAWD,EAAQ,WAAW,SAAS,EAAIA,EAAQ,MAAM,CAAC,EAAE,KAAK,EAAIA,EACrEE,EAAKD,EAAS,QAAQ,GAAG,EAC/B,GAAIC,IAAO,GAAI,SACf,IAAMC,EAAMF,EAAS,MAAM,EAAGC,CAAE,EAAE,KAAK,EACnCE,EAAQH,EAAS,MAAMC,EAAK,CAAC,EAAE,KAAK,EACpCE,EAAM,QAAU,IAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,GAAOA,EAAM,WAAW,GAAG,GAAKA,EAAM,SAAS,GAAG,KACtHA,EAAQA,EAAM,MAAM,EAAG,EAAE,GAEvBD,GAAKL,EAAI,KAAK,CAACK,EAAKC,CAAK,CAAC,EAEhC,OAAON,CACT,CAMO,SAASO,EAAiBC,EAA2C,CAC1E,IAAMC,EAAiC,CAAC,EACxC,QAAWC,IAAS,CAAC,OAAQ,aAAa,EAAG,CAC3C,IAAMC,EAAOd,EAAQW,EAAWE,CAAK,EACrC,GAAKd,EAAWe,CAAI,EACpB,GAAI,CACF,OAAW,CAACC,EAAGC,CAAC,IAAKf,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAC1DF,EAAOG,CAAC,EAAIC,CAEhB,MAAE,CAA0B,EAE9B,OAAOJ,CACT,CAQA,IAAMK,EAAqB,IAAI,IAExB,SAASC,EAAiBC,EAAoB,QAAQ,IAAI,EAAgB,CAC/E,IAAMC,EAASH,EAAmB,IAAIE,CAAS,EAC/C,GAAIC,EAAQ,OAAOA,EACnB,IAAMN,EAAOd,EAAQmB,EAAW,MAAM,EAClCE,EACJ,GAAI,CAACtB,EAAWe,CAAI,EAClBO,EAAO,IAAI,QAEX,IAAI,CACFA,EAAO,IAAI,IAAIpB,EAAYH,EAAagB,EAAM,OAAO,CAAC,EAAE,IAAI,CAAC,CAACC,CAAC,IAAMA,CAAC,CAAC,CACzE,MAAE,CACAM,EAAO,IAAI,GACb,CAEF,OAAAJ,EAAmB,IAAIE,EAAWE,CAAI,EAC/BA,CACT,CAkBO,SAASC,EACdC,EACAC,EAA+B,QAAQ,IACvCC,EAAoB,QAAQ,IAAI,EACb,CACnB,IAAMC,EAAeC,EAAiBJ,CAAS,EACzCK,EAAkBC,EAAiBJ,CAAS,EAC5CK,EAA2B,CAAE,GAAGN,CAAU,EAChD,QAAWO,KAAKH,EACRG,KAAKL,GAAe,OAAOI,EAAMC,CAAC,EAE1C,OAAW,CAACA,EAAGC,CAAC,IAAK,OAAO,QAAQN,CAAY,EAC9CI,EAAMC,CAAC,EAAIC,EAEb,OAAOF,CACT,CAUO,SAASG,EAAqBC,EAA2C,CAC9E,cAAOA,EAAI,kBACX,OAAOA,EAAI,sBACJA,CACT","names":["readFileSync","existsSync","resolve","parseDotEnv","content","out","raw","trimmed","stripped","eq","key","value","loadWorkspaceEnv","workspace","merged","fname","path","k","v","daemonEnvKeysCache","getDaemonEnvKeys","daemonCwd","cached","keys","buildAgentEnv","workspace","parentEnv","daemonCwd","workspaceEnv","loadWorkspaceEnv","daemonOwnedKeys","getDaemonEnvKeys","child","k","v","stripAnthropicApiKey","env"]}
@@ -0,0 +1,44 @@
1
+ import{a as A,b as v,g as E}from"./chunk-67Q4DCPS.js";import{existsSync as l,mkdirSync as S,writeFileSync as h,readFileSync as k}from"fs";import{resolve as p}from"path";import{createHash as $}from"crypto";var f="<!-- agentx-managed: hash=",y=" -->";function R(t){return $("sha256").update(t.systemPrompt??"").update("\0").update(t.codegraph?"cg:1":"cg:0").digest("hex").slice(0,16)}function C(t){return`${f}${R(t)}${y}`}function j(t){let s=t.split(`
2
+ `,1)[0]??"";return!s.startsWith(f)||!s.endsWith(y)?null:s.slice(f.length,s.length-y.length)}function D(t,s){let o=`${t} ${s.systemPrompt||""} ${s.name}`.toLowerCase();return/cod(e|ing|er)|develop|engineer|program/.test(o)?"coding":/pm|project.?manag|product|scrum/.test(o)?"pm":/devops|deploy|infra|ops|sre|ci.?cd/.test(o)?"devops":/qa|test|quality|forensic/.test(o)?"qa":"general"}function N(t,s,o){let n=D(t,s),e=[];switch(e.push(C(s)),e.push(`# ${s.name}`),e.push(""),e.push("@AGENTS.md"),e.push(""),s.systemPrompt&&s.systemPrompt.trim().length>0&&(e.push("## Role"),e.push(""),e.push(s.systemPrompt.trimEnd()),e.push("")),e.push("## Commands"),e.push(""),l(p(s.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("```")):l(p(s.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(""),n){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:${o}/send \\`),e.push(' -H "Content-Type: application/json" \\'),e.push(` -d '{"channel":"telegram","chatId":"<id>","text":"<message>","agentId":"${t}"}'`),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":"${t}","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(""),s.codegraph&&e.push(v()),e.join(`
3
+ `)}var g=["Bash(npx prisma migrate reset*)","Bash(prisma migrate reset*)","Bash(npx prisma db push*)","Bash(prisma db push*)","Bash(dropdb*)"];function M(t,s){return`curl -s --max-time 3 -H 'Content-Type: application/json' --data-binary @- '${`http://127.0.0.1:${s}/guard/check?agent=${encodeURIComponent(t)}`}' 2>/dev/null || true`}function P(t,s){let o=M(t,s);return[{matcher:"Bash",hooks:[{type:"command",command:o,timeout:10}]},{matcher:"Write|Edit",hooks:[{type:"command",command:o,timeout:10}]}]}function B(t){return Array.isArray(t?.hooks)&&t.hooks.some(s=>typeof s?.command=="string"&&(s.command.includes("/guard/check")||s.command.includes("guard check")))}function O(t,s,o="19900"){let n={autoMemoryEnabled:!0,env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}};if(s.permissionMode==="bypassPermissions"?n.permissions={deny:[...g]}:s.permissionMode==="plan"?n.permissions={deny:[...g,"Bash(rm -rf *)","Bash(drop *)","Bash(DELETE *)"]}:n.permissions={deny:[...g,"Bash(rm -rf /)","Bash(> /dev/sda*)","Bash(mkfs*)"]},s.codegraph){let r=n.permissions||{},a=Array.isArray(r.allow)?r.allow:[];r.allow=[...new Set([...a,...A])],n.permissions=r}let e={};return e.PreToolUse=P(t,o),e.Notification=[{matcher:"",hooks:[{type:"command",command:`echo "[${t}] notification: $(jq -r '.type // "unknown"')" >> /tmp/agentx-${t}.log`}]}],e.PostToolUse=[{matcher:"Bash",hooks:[{type:"command",command:`jq -r '.tool_input.command // empty' >> /tmp/agentx-${t}-commands.log`}]}],e.SessionStart=[{matcher:"compact",hooks:[{type:"command",command:`echo "Reminder: You are ${s.name} (${t}). Follow the conventions in CLAUDE.md."`}]}],n.hooks=e,n}function U(t,s){let o=D(t,s),n=[];switch(o){case"coding":n.push({name:"testing.md",content:`---
4
+ paths:
5
+ - "**/*.test.{ts,tsx,js,jsx}"
6
+ - "**/*.spec.{ts,tsx,js,jsx}"
7
+ - "test/**/*"
8
+ ---
9
+
10
+ # Testing Rules
11
+
12
+ - Every new function should have a corresponding test
13
+ - Test both happy path and error cases
14
+ - Use descriptive test names that explain what's being tested
15
+ - Mock external dependencies, not internal modules
16
+ `}),n.push({name:"code-quality.md",content:`# Code Quality
17
+
18
+ - No console.log in production code (use a logger)
19
+ - Handle errors explicitly \u2014 no empty catch blocks
20
+ - Keep files under 300 lines
21
+ - Extract repeated logic into shared utilities
22
+ `});break;case"devops":n.push({name:"deployment-safety.md",content:`# Deployment Safety
23
+
24
+ - Always verify staging before production
25
+ - Check disk space and memory before deploying
26
+ - Never expose secrets in logs or commits
27
+ - Use rollback-safe deployment strategies
28
+ `});break;case"pm":n.push({name:"communication.md",content:`# Communication Rules
29
+
30
+ - Keep GitLab comments under 5 lines for the main message
31
+ - Use <details> for verbose output (logs, commands, steps)
32
+ - Reference issues with #IID, merge requests with !IID
33
+ - Never mention Telegram handles on GitLab
34
+ `});break;case"qa":n.push({name:"bug-reports.md",content:`# Bug Report Format
35
+
36
+ Always include:
37
+ 1. Steps to reproduce
38
+ 2. Expected behavior
39
+ 3. Actual behavior
40
+ 4. Environment (browser, OS, version)
41
+ 5. Screenshots if applicable
42
+ `});break}return n}function _(t,s,o="19900",n=console.error){let e=[],r=[],a=s.workspace;if(!l(a))return n(`Workspace not found: ${a}`),{created:e,skipped:r};if(s.tier!=="claude-code"&&s.tier!=="codex-cli")return{created:e,skipped:r};let c=(i,m)=>{l(i)?r.push(i):(S(p(i,".."),{recursive:!0}),h(i,m),e.push(i))},u=(i,m,T)=>{if(!l(i)){S(p(i,".."),{recursive:!0}),h(i,m),e.push(i);return}let w;try{w=k(i,"utf8")}catch{r.push(i);return}let x=j(w);if(x===null){r.push(i);return}if(x===T){r.push(i);return}h(i,m),e.push(`${i} (refreshed)`)};if(c(p(a,"AGENTS.md"),E({name:s.name,id:t,role:s.systemPrompt?.split(`
43
+ `)[0],workspace:a,tier:s.tier})),s.tier==="codex-cli")return e.length>0&&n(`[${t}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:r};u(p(a,"CLAUDE.md"),N(t,s,o),R(s));let b=p(a,".claude/settings.json");c(b,JSON.stringify(O(t,s,o),null,2));let d=U(t,s);for(let i of d)c(p(a,".claude/rules",i.name),i.content);return e.length>0&&n(`[${t}] workspace setup: created ${e.length} file(s)`),{created:e,skipped:r}}function L(t,s){let o=p(t,".claude/settings.json");if(!l(o))return!1;try{let n=JSON.parse(k(o,"utf-8")),e=!1;for(let[r,a]of Object.entries(s))if(typeof a=="object"&&a!==null&&!Array.isArray(a)){n[r]||(n[r]={});for(let[c,u]of Object.entries(a))n[r][c]===void 0&&(n[r][c]=u,e=!0)}else n[r]===void 0&&(n[r]=a,e=!0);return e&&h(o,JSON.stringify(n,null,2)),e}catch{return!1}}function q(t,s,o="19900"){let n=p(t,".claude/settings.json");if(!l(n))return!1;try{let e=JSON.parse(k(n,"utf-8")),r=!1,a=e.permissions??={},c=Array.isArray(a.deny)?a.deny:a.deny=[];for(let i of g)c.includes(i)||(c.push(i),r=!0);let u=e.hooks??={},d=[...Array.isArray(u.PreToolUse)?u.PreToolUse.filter(i=>!B(i)):[],...P(s,o)];return JSON.stringify(u.PreToolUse??null)!==JSON.stringify(d)&&(u.PreToolUse=d,r=!0),r&&h(n,JSON.stringify(e,null,2)),r}catch{return!1}}function F(t,s="19900",o=console.error){let n=0,e=0,r=0;for(let[a,c]of Object.entries(t)){if(c.tier!=="claude-code"&&c.tier!=="codex-cli")continue;let u=_(a,c,s,o);n+=u.created.length,c.tier==="claude-code"&&L(c.workspace,{env:{CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS:"1"}})&&e++,c.tier==="claude-code"&&q(c.workspace,a,s)&&r++}n>0&&o(`Workspace setup: ${n} file(s) created across agent workspaces`),e>0&&o(`Workspace patch: ${e} workspace(s) updated with agent teams support`),r>0&&o(`Guardrails: ${r} workspace(s) backfilled with deny list + PreToolUse guard hook`)}export{j as a,N as b,_ as c,q as d,F as e};
44
+ //# sourceMappingURL=chunk-I3SIXTJO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/agents/workspace-setup.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\"\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/** Generate .claude/rules/ files based on agent role */\nfunction generateRules(agentId: string, def: AgentDef): Array<{ name: string; content: string }> {\n const role = detectRole(agentId, def)\n const rules: Array<{ name: string; content: string }> = []\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- Keep files under 300 lines\n- Extract repeated logic into shared utilities\n`,\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 writeIfMissing(resolve(workspace, \".claude/rules\", rule.name), rule.content)\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/**\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): void {\n let totalCreated = 0\n let totalPatched = 0\n let totalGuarded = 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 (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}\n"],"mappings":"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","names":["existsSync","mkdirSync","writeFileSync","readFileSync","resolve","createHash","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","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","setupAllWorkspaces","agents","totalCreated","totalPatched","totalGuarded","id","result"]}
@@ -0,0 +1,118 @@
1
+ import{z as e}from"zod";var P=e.enum(["trigger.channel","trigger.manual","trigger.cron","trigger.hook","trigger.form","agent","transform","branch","gateway.parallel","rule","action.send","action.createIssue","action.setLabel","action.readLabel","action.react","action.editMessage","action.logTime","action.callHTTP","action.run","action.builtin","subProcess","signal.emit","signal.wait","timer.boundary","checkpoint","end"]),R=e.enum(["equals","contains","matches","exists"]),F=e.object({kind:R,params:e.record(e.unknown()).default({})}),_=e.object({maxAttempts:e.number().int().min(1).max(10).default(1),backoffMs:e.number().int().min(0).max(6e4).default(1e3)}).default({maxAttempts:1,backoffMs:1e3}),$=e.object({id:e.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/,"node id must be identifier-safe"),type:P,config:e.record(e.unknown()).default({}),retry:_,position:e.object({x:e.number(),y:e.number()}).optional()}),N=e.object({from:e.string(),fromPort:e.string().optional(),to:e.string(),toPort:e.string().optional(),label:e.string().optional()}),A=e.union([e.string(),e.date().transform(t=>t.toISOString())]),v=e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"workflow id must be lower-kebab"),version:e.literal(2).default(2),title:e.string().min(1),description:e.string().optional(),status:e.enum(["draft","review","active","deprecated"]).default("active"),tags:e.array(e.string()).default([]),project:e.string().optional(),entity:e.string().optional(),intentPath:e.array(e.string()).default([]),generatedFrom:e.string().optional(),sourceTaskIds:e.array(e.string()).default([]),confidence:e.number().min(0).max(1).optional(),workflowVersion:e.string().optional(),ownerAgent:e.string().optional(),lastMatchedAt:e.string().optional(),matchCount:e.number().int().min(0).default(0),state:e.enum(["active","disabled","quarantined"]).default("active"),priority:e.number().int().default(0),fanOut:e.boolean().default(!1),nodes:e.array($).refine(t=>t.length>0,{message:"workflow must have at least one node"}),edges:e.array(N).default([]),envAllow:e.array(e.string()).default([]),retention:e.object({maxRuns:e.number().int().positive().default(500),maxDays:e.number().int().positive().default(90)}).default({maxRuns:500,maxDays:90}),maxChildDepth:e.number().int().positive().default(5),mesh:e.object({allowRemote:e.boolean().default(!1),peers:e.array(e.string()).optional()}).default({allowRemote:!1}),created:A.optional(),updated:A.optional()});function k(t){let r=[],u=new Map(t.nodes.map(n=>[n.id,n])),l=t.nodes.filter(n=>n.type.startsWith("trigger."));l.length===0&&r.push("workflow must have exactly one trigger.* node (found 0)"),l.length>1&&r.push(`workflow has ${l.length} trigger.* nodes; v1 supports exactly one`);for(let n of t.edges)u.has(n.from)||r.push(`edge references missing node: from="${n.from}"`),u.has(n.to)||r.push(`edge references missing node: to="${n.to}"`);let a=l[0];if(a){let n=new Map;for(let s of t.edges)n.has(s.from)||n.set(s.from,[]),n.get(s.from).push(s.to);let o=new Set,c=[a.id];for(;c.length;){let s=c.pop();if(!o.has(s)){o.add(s);for(let g of n.get(s)??[])c.push(g)}}for(let s of t.nodes)o.has(s.id)||r.push(`node "${s.id}" is unreachable from trigger "${a.id}"`);t.nodes.some(s=>o.has(s.id)&&O(s.type))||r.push("no reachable `end`, `checkpoint`, `subProcess`, `signal.wait`, or `timer.boundary` node \u2014 the run cannot terminate or pause")}for(let n of t.nodes){if(n.type!=="branch"&&n.type!=="rule")continue;let o=n.type==="branch"?n.config.cases??[]:(n.config.rules??[]).filter(d=>typeof d.to=="string"),c=n.type==="branch"?n.config.default:n.config.default?.to,s=new Set(o.map(d=>d.to));c&&s.add(c);let g=t.edges.filter(d=>d.from===n.id);for(let d of g){let f=d.fromPort??"";s.has(f)||r.push(`${n.type} "${n.id}" has outgoing edge with port "${f}" not declared in cases or default`)}}let i=C(t);for(let n of i)r.push(`cycle without agent/checkpoint: ${n.join(" -> ")}`);return r}function O(t){return t==="end"||t==="checkpoint"||t==="subProcess"||t==="signal.wait"||t==="timer.boundary"}function M(t){return t==="agent"||t==="checkpoint"||t==="subProcess"||t==="signal.wait"||t==="timer.boundary"}function C(t){let r=new Map;for(let o of t.edges)r.has(o.from)||r.set(o.from,[]),r.get(o.from).push(o.to);let u=new Map(t.nodes.map(o=>[o.id,o])),l=[],a=new Map,i=[],n=o=>{if(a.get(o)===1){let c=i.indexOf(o);if(c>=0){let s=i.slice(c).concat(o);s.some(d=>{let f=u.get(d)?.type;return f?M(f):!1})||l.push(s)}return}if(a.get(o)!==2){a.set(o,1),i.push(o);for(let c of r.get(o)??[])n(c);i.pop(),a.set(o,2)}};for(let o of t.nodes)a.get(o.id)!==2&&n(o.id);return l}var D=e.enum(["running","paused","completed","failed","canceled"]),W=e.enum(["ok","failed","timeout","skipped","paused","resumed"]),z=e.object({at:e.string(),nodeId:e.string(),inputKeys:e.array(e.string()).default([]),status:W,output:e.record(e.unknown()).optional(),idempotencyKey:e.string(),note:e.string().optional()}),L=e.object({backend:e.string(),id:e.string()}),U=e.discriminatedUnion("kind",[e.object({kind:e.literal("checkpoint"),nodeId:e.string(),checkpointName:e.string(),resumeMatch:e.record(e.unknown()).default({})}),e.object({kind:e.literal("subProcess"),nodeId:e.string(),childRunId:e.string(),childWorkflowId:e.string()}),e.object({kind:e.literal("signalWait"),nodeId:e.string(),signalName:e.string(),match:e.record(e.unknown()).default({}),scope:e.enum(["workflow","global"]).default("workflow")}),e.object({kind:e.literal("timerWait"),nodeId:e.string(),fireAt:e.string()})]),V=e.object({id:e.string(),workflowId:e.string(),workflowVersion:e.literal(2),homeNode:e.string(),status:D,pausedAt:U.optional(),context:e.record(e.record(e.unknown())).default({}),pending:e.array(e.string()).default([]),joinCounters:e.record(e.array(e.string())).default({}),entityRef:L,history:e.array(z).default([]),parentRunId:e.string().nullable().default(null),parentNodeId:e.string().nullable().default(null),rootRunId:e.string().nullable().default(null),depth:e.number().int().min(0).default(0),createdAt:e.string(),updatedAt:e.string()});var q="https://api.anthropic.com/v1/messages",H="2023-06-01",Y="claude-sonnet-4-6",T=`You are a workflow architect for AgentX. Given a successful task trace, decompose it into a REUSABLE workflow DAG. The same workflow must apply across project, platform, and environment variations \u2014 extract anything that varies between runs into the trigger's inputSchema and reference it symbolically.
2
+
3
+ PARAMETERIZATION (this is the most important rule):
4
+ The procedure should be one workflow, not one workflow per project / branch / environment. When you see specific values in the trace, ask "would this be different on a future run of the same procedure?" \u2014 if yes, extract them as inputs.
5
+
6
+ Always extract into trigger.config.inputSchema.properties when the trace shows them:
7
+ - platform \u2014 "github" | "gitlab" (only when both are plausible for this work)
8
+ - project \u2014 full repo path or group/repo identifier ("mtgl/mtgl_system", "anis-marrouchi/agentx")
9
+ - environment \u2014 "staging" | "production" | "dev" (infer from hostnames, paths, message text)
10
+ - ref \u2014 branch / tag / commit ("master", "main", "release/2.4")
11
+ - id \u2014 issue / MR / PR number
12
+ - host \u2014 SSH target or service hostname
13
+ - agentId \u2014 only when the work could legitimately be assigned to different agents
14
+
15
+ Each input must be a typed JSON-Schema property under trigger.config.inputSchema.properties with type, description, and (where useful) enum, default, or example. List the actual required ones in trigger.config.inputSchema.required.
16
+
17
+ Reference inputs symbolically EVERYWHERE:
18
+ - BAD: "ssh root@staging.mtgl.com.sa 'cd /var/www/test_mtglbackend && git pull origin master'"
19
+ - GOOD: "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'"
20
+
21
+ Cross-reference between nodes the same way: {{previousNodeId.outputField}} for chained steps.
22
+
23
+ OUTPUT CONTRACT:
24
+ The end node must declare what the workflow produces \u2014 operators reading the workflow card need to see what comes out. Use end.config.output as a structured object literal of the form { status: "...", "<key>": "{{node.field}}" }, not a free-text string. Pull values from the nodes that produced them.
25
+
26
+ Decomposition rules:
27
+ - Group related tool calls into one semantic node. Five Bash calls that pull a repo, install deps, run migrations, restart services, and verify status are ONE deploy node, not five.
28
+ - Prefer LINEAR workflows. Use branch ONLY when the procedure had a clear conditional fork that future runs will also need (e.g. "if migration \u2192 backup db first; else skip"). Don't invent branches the trace didn't actually exercise.
29
+ - Use deterministic node types (action.run, action.builtin, transform) when the work is mechanical.
30
+ - Use an "agent" node only when the step genuinely requires LLM reasoning (drafting prose, classifying intent, synthesizing output).
31
+
32
+ Available node types and their configs:
33
+ - trigger.manual config: { inputSchema: { type:"object", properties: {...}, required: [...] } }
34
+ - agent config: { agentId: string, prompt: string }
35
+ - transform config: { expression: string } (jq-style data shaping)
36
+ - action.run config: { command: string } (shell, supports {{templates}})
37
+ - action.send config: { channel: string, chatId: string, text: string }
38
+ - action.builtin config: { name: string, input: object } (e.g. name: "http.fetch")
39
+ - extract.structured config: { prompt: string, schema: object }
40
+ - branch config: { cases: [{ when: <Condition>, to: <port-name> }, ...], default?: <port-name> }
41
+ - rule config: { rows: [...] } (DMN decision table)
42
+ - checkpoint config: { label: string }
43
+ - end config: { status: "completed"|"failed", output: object }
44
+
45
+ Branch/rule mechanics \u2014 read carefully:
46
+ - The "to" field in cases is a PORT NAME (a string label), NOT a node id.
47
+ - Each outgoing edge from a branch must have fromPort equal to one of the port names declared in cases or in default.
48
+ - The edge.to field is the destination node id.
49
+
50
+ WORKED EXAMPLE \u2014 a deploy workflow that handles ANY project/env/branch via inputs:
51
+ {
52
+ "title": "Deploy MR to environment",
53
+ "description": "Pull a branch onto a host, run migrations if present, rebuild Laravel caches. Same procedure for any project/env/branch \u2014 pass them as inputs.",
54
+ "nodes": [
55
+ {
56
+ "id": "trigger",
57
+ "type": "trigger.manual",
58
+ "config": {
59
+ "inputSchema": {
60
+ "type": "object",
61
+ "properties": {
62
+ "project": { "type": "string", "description": "group/repo (e.g. 'mtgl/mtgl_system')" },
63
+ "environment": { "type": "string", "enum": ["staging", "production", "dev"], "default": "staging" },
64
+ "host": { "type": "string", "description": "SSH target hostname (e.g. 'staging.mtgl.com.sa')" },
65
+ "path": { "type": "string", "description": "Absolute path on host where the project is deployed" },
66
+ "ref": { "type": "string", "description": "Branch/tag/commit to deploy", "default": "master" }
67
+ },
68
+ "required": ["project", "environment", "host", "path", "ref"]
69
+ }
70
+ }
71
+ },
72
+ { "id": "pull", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'" } },
73
+ { "id": "needs_backup", "type": "branch", "config": { "cases": [{ "when": { "kind": "matches", "params": { "path": "pull.stdout", "pattern": "migrate" } }, "to": "yes" }], "default": "no" } },
74
+ { "id": "backup_db", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'pg_dump app > /backups/{{trigger.input.environment}}-$(date +%Y%m%d-%H%M%S).sql'" } },
75
+ { "id": "migrate", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan migrate --force'" } },
76
+ { "id": "rebuild_cache", "type": "action.run", "config": { "command": "ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan config:cache && php artisan route:cache'" } },
77
+ {
78
+ "id": "done",
79
+ "type": "end",
80
+ "config": {
81
+ "status": "completed",
82
+ "output": {
83
+ "project": "{{trigger.input.project}}",
84
+ "environment": "{{trigger.input.environment}}",
85
+ "ref": "{{trigger.input.ref}}",
86
+ "deployedAt": "{{rebuild_cache.completedAt}}",
87
+ "migrated": "{{needs_backup.matchedPort}}"
88
+ }
89
+ }
90
+ }
91
+ ],
92
+ "edges": [
93
+ { "from": "trigger", "to": "pull" },
94
+ { "from": "pull", "to": "needs_backup" },
95
+ { "from": "needs_backup", "to": "backup_db", "fromPort": "yes" },
96
+ { "from": "needs_backup", "to": "migrate", "fromPort": "no" },
97
+ { "from": "backup_db", "to": "migrate" },
98
+ { "from": "migrate", "to": "rebuild_cache" },
99
+ { "from": "rebuild_cache", "to": "done" }
100
+ ]
101
+ }
102
+
103
+ Notice three things:
104
+ 1. The trigger declares a typed inputSchema. An operator running this workflow knows EXACTLY what to pass in.
105
+ 2. Every command references {{trigger.input.X}}, never literal hostnames or paths.
106
+ 3. The end node's output is a structured object so callers know what they get back.
107
+
108
+ Constraints:
109
+ - Exactly one trigger.* node and at least one "end" node.
110
+ - DAG only (no cycles).
111
+ - Every edge's from/to must reference an existing node id.
112
+ - Node ids match /^[a-z0-9][a-z0-9_]*$/.
113
+ - Don't invent fields not in the spec above. Don't add config keys you didn't see in this spec.
114
+ - The trigger MUST declare inputSchema unless the procedure genuinely takes no parameters.`,B={type:"object",required:["title","description","nodes","edges"],properties:{title:{type:"string",maxLength:120},description:{type:"string",maxLength:600},nodes:{type:"array",minItems:2,maxItems:30,items:{type:"object",required:["id","type","config"],properties:{id:{type:"string",pattern:"^[a-z0-9][a-z0-9_]*$",maxLength:48},type:{type:"string",enum:["trigger.manual","agent","transform","branch","rule","action.run","action.send","action.builtin","extract.structured","checkpoint","end"]},config:{type:"object"}}}},edges:{type:"array",maxItems:60,items:{type:"object",required:["from","to"],properties:{from:{type:"string"},to:{type:"string"},fromPort:{type:"string"},label:{type:"string"}}}}}};function b(t,r){let u=String(t??"").trim();return u.length>r?`${u.slice(0,r)}\u2026`:u}function E(t,r){let l=r.filter(a=>a.status!=="error"&&a.name!=="system").slice(0,40).map((a,i)=>{let n=[a.name,a.action].filter(Boolean).join(":"),o=b(a.inputSummary||"",280),c=b(a.outputSummary||"",280),s=[o&&`in=${o}`,c&&`out=${c}`].filter(Boolean).join(" | ");return`${i+1}. ${n}${s?` ${s}`:""}`});return["Original task message:",b(t.messagePreview||"",600),"",`Agent: ${t.agentId} (channel=${t.channel||"unknown"}, chatId=${t.chatId||"\u2014"})`,`Outcome: ${t.status}, steps=${r.length}`,"","Procedure evidence (filtered, in order):",...l].join(`
115
+ `)}async function I(t,r,u,l,a,i){let n=[{role:"user",content:l}];a&&(n.push({role:"assistant",content:"(previous attempt; will retry with corrections)"}),n.push({role:"user",content:a}));let o={model:r,max_tokens:i.maxTokens,system:u,messages:n,tools:[{name:"emit_workflow",description:"Return the workflow DAG. Must be valid against the schema.",input_schema:B}],tool_choice:{type:"tool",name:"emit_workflow"}},c=new AbortController,s=setTimeout(()=>c.abort(),i.timeoutMs),g;try{g=await i.fetchImpl(q,{method:"POST",headers:{"Content-Type":"application/json","x-api-key":t,"anthropic-version":H},body:JSON.stringify(o),signal:c.signal})}finally{clearTimeout(s)}if(!g.ok){let m=await g.text().catch(()=>"");throw new Error(`anthropic API ${g.status}: ${m.slice(0,500)}`)}let d=await g.json(),h=(Array.isArray(d.content)?d.content:[]).find(m=>m?.type==="tool_use"&&m?.name==="emit_workflow");if(!h||!h.input)throw new Error("anthropic API returned no emit_workflow tool_use");let p=d.usage??{};return{data:h.input,usage:{inputTokens:typeof p.input_tokens=="number"?p.input_tokens:0,outputTokens:typeof p.output_tokens=="number"?p.output_tokens:0}}}function S(t,r,u,l,a,i){let n=new Set;r.channel&&n.add(r.channel);for(let o of u)o.action&&n.add(o.action.toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-|-$/g,""));return v.parse({id:l,version:2,title:b(t.title,120)||`Draft: ${b(r.messagePreview,72)||r.taskId.slice(0,8)}`,description:b(t.description,600)||`LLM-architected from task trace ${r.taskId}.`,status:"draft",state:"disabled",tags:Array.from(n).slice(0,12),entity:r.chatId||void 0,generatedFrom:"llm-architect",sourceTaskIds:a,confidence:i,workflowVersion:"draft-1",ownerAgent:r.agentId,nodes:t.nodes,edges:t.edges,envAllow:[],retention:{maxRuns:500,maxDays:90},maxChildDepth:5})}function J(t){let r=t.match(/```(?:json)?\s*([\s\S]*?)```/i),u=r?r[1]:t,l=u.indexOf("{");if(l<0)return null;let a=0,i=!1,n=!1;for(let o=l;o<u.length;o++){let c=u[o];if(n){n=!1;continue}if(c==="\\"&&i){n=!0;continue}if(c==='"'){i=!i;continue}if(!i){if(c==="{")a++;else if(c==="}"&&(a--,a===0))return u.slice(l,o+1)}}return null}async function j(t,r,u,l,a,i){let n=l?["Your previous response failed workflow-schema validation with these issues:",l,"","Re-emit the corrected workflow as a single JSON object \u2014 same shape as before, but fix the listed issues. No prose, no code fences."].join(`
116
+ `):[r,"","---","","Task trace to architect:","",u,"","---","","Reply with ONLY a JSON object matching this shape (no prose, no fenced code block, no commentary):","{",' "title": "<short title>",',' "description": "<short description>",',' "nodes": [{ "id": "...", "type": "...", "config": {} }, ...],',' "edges": [{ "from": "...", "to": "...", "fromPort": "..." (optional), "label": "..." (optional) }, ...]',"}"].join(`
117
+ `),o=new AbortController,c=setTimeout(()=>o.abort(),i.timeoutMs),s;try{s=await i.fetchImpl(`${i.daemonUrl.replace(/\/+$/,"")}/task`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({agent:t,message:n,freshSession:l===null,context:{channel:"api",chatId:a}}),signal:o.signal})}finally{clearTimeout(c)}if(!s.ok){let y=await s.text().catch(()=>"");throw new Error(`agent /task ${s.status}: ${y.slice(0,500)}`)}let g=await s.json(),d=String(g.content??"").trim();if(g.error)throw new Error(`agent reported error: ${g.error}`);if(!d)throw new Error("agent returned empty content");let f=J(d);if(!f)throw new Error(`agent reply has no JSON object (first 200 chars: ${d.slice(0,200)})`);let h;try{h=JSON.parse(f)}catch(y){throw new Error(`agent reply JSON is unparseable: ${y.message}`)}if(!h||typeof h!="object")throw new Error("agent reply is not a JSON object");let p=h;if(!Array.isArray(p.nodes)||!Array.isArray(p.edges))throw new Error("agent reply missing nodes/edges arrays");let m=g.usage??{};return{data:p,usage:{inputTokens:typeof m.inputTokens=="number"?m.inputTokens:0,outputTokens:typeof m.outputTokens=="number"?m.outputTokens:0}}}async function Z(t,r,u,l,a,i={agentId:""}){if(!i.agentId)throw new Error("architectWorkflowViaAgent: agentId is required");let n=i.daemonUrl||"http://127.0.0.1:18800",o=i.fetchImpl||fetch,c={daemonUrl:n,timeoutMs:i.timeoutMs??18e4,fetchImpl:o},s=E(t,r),g=`architect-${t.taskId.toLowerCase()}`,d=await j(i.agentId,T,s,null,g,c),f=d.usage,h=d.data,p;try{p=S(h,t,r,u,l,a);let m=k(p);if(m.length)throw new Error(`lint: ${m.join("; ")}`);return{workflow:p,usage:f,via:`agent:${i.agentId}`}}catch(m){let y=await j(i.agentId,T,s,m.message,g,c);h=y.data,f={inputTokens:f.inputTokens+y.usage.inputTokens,outputTokens:f.outputTokens+y.usage.outputTokens},p=S(h,t,r,u,l,a);let w=k(p);if(w.length)throw new Error(`agent architect retry still invalid: ${w.join("; ")}`);return{workflow:p,usage:f,via:`agent:${i.agentId}`}}}async function Q(t,r,u,l,a,i={}){let n=process.env.ANTHROPIC_API_KEY;if(!n)throw new Error("ANTHROPIC_API_KEY not set; LLM architect unavailable");let o=i.model||Y,c=i.fetchImpl||fetch,s={maxTokens:i.maxTokens??4e3,timeoutMs:i.timeoutMs??9e4,fetchImpl:c},g=E(t,r),d=await I(n,o,T,g,null,s),f=d.data,h=d.usage,p;try{p=S(f,t,r,u,l,a);let m=k(p);if(m.length)throw new Error(`lint: ${m.join("; ")}`);return{workflow:p,usage:h,model:o}}catch(m){let y=`Your previous response failed validation: ${m.message}. Re-emit emit_workflow with the corrections. Keep the same overall structure but fix the listed issues.`,w=await I(n,o,T,g,y,s);f=w.data,h={inputTokens:h.inputTokens+w.usage.inputTokens,outputTokens:h.outputTokens+w.usage.outputTokens},p=S(f,t,r,u,l,a);let x=k(p);if(x.length)throw new Error(`LLM architect retry still invalid: ${x.join("; ")}`);return{workflow:p,usage:h,model:o}}}export{v as a,k as b,Z as c,Q as d};
118
+ //# sourceMappingURL=chunk-ISEBLHWX.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/workflows/types.ts","../src/workflows/architect.ts"],"sourcesContent":["import { z } from \"zod\"\n\n// --- Workflows (V2 — dataflow DAG) ---\n//\n// A workflow is a directed acyclic graph of nodes connected by edges.\n// A run executes the graph by walking from the trigger, accumulating each\n// node's output into a shared context. Prompts and action params template\n// against the context via `{{<nodeId>.<path>}}`.\n//\n// This replaces the previous state-machine model (states + transitions +\n// external label backend). The label-as-state pattern is re-expressible\n// via a `checkpoint` node + `action.setLabel` + `action.readLabel`.\n//\n// Shape on disk (.agentx/workflows/):\n// <id>.json — one workflow definition\n// _runs/<run-id>.jsonl — append-only run events (home-node only)\n// _index/<backend>__<entity>.json — entity -> active runId lookup\n//\n// JSON-on-disk: no YAML dep.\n\n// --------------------------- Node types -----------------------------------\n\nexport const nodeTypeSchema = z.enum([\n // Entry points\n \"trigger.channel\",\n \"trigger.manual\",\n \"trigger.cron\",\n \"trigger.hook\",\n \"trigger.form\",\n // Compute\n \"agent\",\n \"transform\",\n // Control flow\n \"branch\",\n \"gateway.parallel\",\n \"rule\",\n // Side-effect sinks (per channel verb). Each action.* maps 1:1 to an\n // existing channel-adapter method. New verbs arrive as new entries.\n \"action.send\",\n \"action.createIssue\",\n \"action.setLabel\",\n \"action.readLabel\",\n \"action.react\",\n \"action.editMessage\",\n \"action.logTime\",\n \"action.callHTTP\",\n // Registered action invocation — references an entry from the action\n // registry (.agentx/actions/<id>.json) by id, with templated inputs.\n // Lets operators define a reusable shell/http action once and call it\n // from many workflows without duplicating the command/url/headers.\n \"action.run\",\n // Built-in typed action invocation (improvement plan #6 + #9). Calls\n // a daemon-shipped action (http.fetch, mesh.delegate, extract.structured,\n // etc.) by name. Input is templated against the run context the same\n // way action.run is. Output is the validated builtin response so\n // downstream nodes can pipe typed data forward without reparsing.\n \"action.builtin\",\n // BPM: human tasks + composition + signals + intermediate timer\n \"subProcess\",\n \"signal.emit\",\n \"signal.wait\",\n \"timer.boundary\",\n // Persistence / pause\n \"checkpoint\",\n // Terminal\n \"end\",\n])\nexport type NodeType = z.infer<typeof nodeTypeSchema>\n\n// --------------------------- Condition (branch evaluator) -----------------\n\nexport const conditionKindSchema = z.enum([\n \"equals\", // params: { path, value }\n \"contains\", // params: { path, value }\n \"matches\", // params: { path, regex }\n \"exists\", // params: { path }\n])\nexport type ConditionKind = z.infer<typeof conditionKindSchema>\n\nexport const conditionSchema = z.object({\n kind: conditionKindSchema,\n params: z.record(z.unknown()).default({}),\n})\nexport type Condition = z.infer<typeof conditionSchema>\n\n// --------------------------- Nodes + edges --------------------------------\n\n/** Improvement plan #9b — per-node retry policy. When a node's\n * handler returns `{ error }` (or throws), the dispatcher re-runs it\n * up to `maxAttempts - 1` more times with exponential backoff\n * (`backoffMs * 2^(attempt-1)`). Once all attempts are exhausted,\n * the LAST error is what gets recorded as the node's failure.\n * Defaults: maxAttempts=1 (no retry), backoffMs=1000.\n *\n * Pause results (signalWait, timerWait) are NEVER retried —\n * pausing is a normal lifecycle transition, not a failure. Only\n * hard errors (`{error}` or thrown exceptions) trigger the retry. */\nexport const retryPolicySchema = z.object({\n maxAttempts: z.number().int().min(1).max(10).default(1),\n backoffMs: z.number().int().min(0).max(60_000).default(1000),\n}).default({ maxAttempts: 1, backoffMs: 1000 })\nexport type RetryPolicy = z.infer<typeof retryPolicySchema>\n\nexport const workflowNodeSchema = z.object({\n id: z.string().min(1).regex(/^[a-zA-Z_][a-zA-Z0-9_-]*$/, \"node id must be identifier-safe\"),\n type: nodeTypeSchema,\n /** Free-form per-type config. Each node handler validates its own shape\n * at execution time via a local Zod schema; keeping the top-level schema\n * permissive lets new node types ship without a round-trip through here. */\n config: z.record(z.unknown()).default({}),\n /** Per-node retry on hard errors. See RetryPolicy. */\n retry: retryPolicySchema,\n /** Optional UI position. Ignored by the engine; persisted to keep the\n * editor layout alongside the workflow (no separate _layouts file). */\n position: z.object({ x: z.number(), y: z.number() }).optional(),\n})\nexport type WorkflowNode = z.infer<typeof workflowNodeSchema>\n\nexport const workflowEdgeSchema = z.object({\n /** Source node id. */\n from: z.string(),\n /** Optional source port. Used by `branch` nodes to select an outgoing\n * case; default `\"out\"` for single-port nodes. */\n fromPort: z.string().optional(),\n /** Target node id. */\n to: z.string(),\n /** Reserved — `\"in\"` in v1. Kept for future multi-input nodes. */\n toPort: z.string().optional(),\n /** Optional label shown on the edge in the editor. */\n label: z.string().optional(),\n})\nexport type WorkflowEdge = z.infer<typeof workflowEdgeSchema>\n\n// --------------------------- Workflow -------------------------------------\n\n/** A date that may arrive as a string OR as a JS Date. YAML parsers coerce\n * an unquoted `2026-05-10` into a Date; JSON never does. Normalises to an\n * ISO string so downstream consumers see one type. */\nconst isoDateString = z.union([\n z.string(),\n z.date().transform((d) => d.toISOString()),\n])\n\nexport const workflowSchema = z.object({\n id: z.string().regex(/^[a-z0-9][a-z0-9_-]*$/, \"workflow id must be lower-kebab\"),\n /** Schema version. V2 = dataflow DAG. V1 workflows are not loadable. */\n version: z.literal(2).default(2),\n title: z.string().min(1),\n description: z.string().optional(),\n /** REVIEW METADATA ONLY — `status` never gates dispatch. The one and only\n * dispatch gate is `state` below: a workflow with status=draft but\n * state=active WILL fire; status=active with state=disabled will NOT.\n * (The matcher in matcher.ts is the single exception: it requires both\n * to be active before suggesting.) Generated workflows are saved as\n * status=draft + state=disabled so they validate but never fire until\n * an operator promotes them. If you're deciding \"will this run?\", read\n * `state`; if you're deciding \"has a human reviewed this?\", read\n * `status`. */\n status: z.enum([\"draft\", \"review\", \"active\", \"deprecated\"]).default(\"active\"),\n tags: z.array(z.string()).default([]),\n /** Project this workflow is scoped to, in `<org>/<repo>` form for\n * GitLab/GitHub or any operator-defined slug for non-VCS workflows.\n * Optional — workflows without a project field are global and match\n * any project's events. When set, the field is BOTH metadata (UI\n * grouping, project badges) AND a hard scope: the dispatcher and\n * the per-workflow hook handler in `triggers.ts` drop events whose\n * `ctx.project` differs from this field. Without that gate, two\n * workflows tagged for different projects but subscribed to the\n * same hook (e.g. on:gitlab-issue) both fire on every event — see\n * the cross-tenant leak that prompted this commit. Project rules\n * in `.agentx/projects/` still pre-filter at the webhook layer; the\n * workflow-level scope is a defence in depth. */\n project: z.string().optional(),\n entity: z.string().optional(),\n intentPath: z.array(z.string()).default([]),\n generatedFrom: z.string().optional(),\n sourceTaskIds: z.array(z.string()).default([]),\n confidence: z.number().min(0).max(1).optional(),\n workflowVersion: z.string().optional(),\n ownerAgent: z.string().optional(),\n lastMatchedAt: z.string().optional(),\n matchCount: z.number().int().min(0).default(0),\n /** THE dispatch gate (see `status` above for the review-metadata axis).\n * The dispatcher and trigger registrar honor this:\n * - active: normal — triggers register, runs create + advance\n * - disabled: operator kill switch (config or admin action). No new\n * triggers, no new runs. In-flight runs continue.\n * - quarantined: system set by the conflict detector when this workflow\n * would race with another dispatch path (e.g. a gitlab\n * agentMapping handles the same project the workflow\n * triggers on). Treated like `disabled` at runtime;\n * resolved by either fixing the conflict or operator\n * explicitly flipping the state back to `active`. */\n state: z.enum([\"active\", \"disabled\", \"quarantined\"]).default(\"active\"),\n /** Higher priority wins when multiple workflows match the same event.\n * Default 0. Ties broken by id. */\n priority: z.number().int().default(0),\n /** If true, every matching workflow runs in parallel (each with its own\n * run). Default: only the highest-priority match runs. */\n fanOut: z.boolean().default(false),\n nodes: z.array(workflowNodeSchema).refine(\n (ns) => ns.length > 0, { message: \"workflow must have at least one node\" },\n ),\n edges: z.array(workflowEdgeSchema).default([]),\n /** Env vars that templates + node configs may read via {{env.*}}. */\n envAllow: z.array(z.string()).default([]),\n retention: z.object({\n maxRuns: z.number().int().positive().default(500),\n maxDays: z.number().int().positive().default(90),\n }).default({ maxRuns: 500, maxDays: 90 }),\n /** Safety rail on sub-process nesting depth. A parent at depth N may\n * spawn children up to depth maxChildDepth - 1. Default 5. Override per\n * workflow if a deeper composition is genuinely needed. */\n maxChildDepth: z.number().int().positive().default(5),\n /** Mesh integration. By default workflows are local — only triggers\n * observed on the same node can fire them. Set `mesh.allowRemote: true`\n * to opt the workflow into cross-mesh trigger fan-out: when a peer\n * receives a channel event, it broadcasts it to mesh peers and any\n * matching workflow with allowRemote runs on the peer that owns its\n * definition. The opt-in is required because remote events carry\n * reduced trust (sender provenance is mesh-token only) and can\n * surprise authors who expected isolation. */\n mesh: z.object({\n allowRemote: z.boolean().default(false),\n /** Optional peer-name allowlist. When present, only triggers\n * broadcast from these named peers are honored. Empty/unset = any\n * authenticated peer. Useful for production workflows that should\n * only accept events from a specific upstream node. */\n peers: z.array(z.string()).optional(),\n }).default({ allowRemote: false }),\n // YAML turns an unquoted `2026-05-10` into a JS Date, so a hand-authored\n // workflow that omits the quotes was silently rejected by the schema and\n // dropped by WorkflowStore.list() — which swallows parse failures. Accept\n // both shapes and normalise to an ISO string.\n created: isoDateString.optional(),\n updated: isoDateString.optional(),\n})\nexport type Workflow = z.infer<typeof workflowSchema>\n\n// --------------------------- DAG linter -----------------------------------\n//\n// Replaces the FSM-era `lintWorkflow` from V1. Checks:\n// - exactly one trigger.* node (v1 rule; multi-trigger out of scope)\n// - every edge's from/to references an existing node\n// - no unreachable nodes (except the trigger) — DFS from trigger\n// - no cycles unless every cycle has an agent or checkpoint on it (those\n// are the nodes that can pause or consume external events; cycles\n// without either would infinite-loop the walker)\n// - every branch node's edge.fromPort matches a case in its config\n// - at least one `end` or `checkpoint` is reachable (runs must terminate)\n\nexport function lintWorkflow(wf: Workflow): string[] {\n const issues: string[] = []\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n\n // 1. exactly one trigger\n const triggers = wf.nodes.filter((n) => n.type.startsWith(\"trigger.\"))\n if (triggers.length === 0) issues.push(\"workflow must have exactly one trigger.* node (found 0)\")\n if (triggers.length > 1) issues.push(`workflow has ${triggers.length} trigger.* nodes; v1 supports exactly one`)\n\n // 2. edge endpoints exist\n for (const e of wf.edges) {\n if (!byId.has(e.from)) issues.push(`edge references missing node: from=\"${e.from}\"`)\n if (!byId.has(e.to)) issues.push(`edge references missing node: to=\"${e.to}\"`)\n }\n\n // 3. reachability from trigger\n const trigger = triggers[0]\n if (trigger) {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const seen = new Set<string>()\n const stack = [trigger.id]\n while (stack.length) {\n const id = stack.pop()!\n if (seen.has(id)) continue\n seen.add(id)\n for (const next of adj.get(id) ?? []) stack.push(next)\n }\n for (const n of wf.nodes) {\n if (!seen.has(n.id)) issues.push(`node \"${n.id}\" is unreachable from trigger \"${trigger.id}\"`)\n }\n if (!wf.nodes.some((n) => seen.has(n.id) && isTerminalOrPauseNode(n.type))) {\n issues.push(\"no reachable `end`, `checkpoint`, `subProcess`, `signal.wait`, or `timer.boundary` node — the run cannot terminate or pause\")\n }\n }\n\n // 4. branch ports (also applies to DMN-style `rule` nodes whose rows\n // carry the same `{ to }` shape and a shared `default`).\n for (const n of wf.nodes) {\n if (n.type !== \"branch\" && n.type !== \"rule\") continue\n const cases = n.type === \"branch\"\n ? ((n.config.cases as Array<{ to: string }> | undefined) ?? [])\n : ((n.config.rules as Array<{ to?: string }> | undefined) ?? []).filter((r): r is { to: string } => typeof r.to === \"string\")\n const defaultPort = n.type === \"branch\"\n ? (n.config.default as string | undefined)\n : ((n.config.default as { to?: string } | undefined)?.to)\n const declared = new Set<string>(cases.map((c) => c.to))\n if (defaultPort) declared.add(defaultPort)\n const outgoing = wf.edges.filter((e) => e.from === n.id)\n for (const edge of outgoing) {\n const port = edge.fromPort ?? \"\"\n if (!declared.has(port)) {\n issues.push(`${n.type} \"${n.id}\" has outgoing edge with port \"${port}\" not declared in cases or default`)\n }\n }\n }\n\n // 5. cycle detection — only flag cycles that would infinite-loop (no agent\n // or checkpoint on the cycle).\n const onCycle = detectCyclesWithoutPauseNodes(wf)\n for (const nodeIds of onCycle) {\n issues.push(`cycle without agent/checkpoint: ${nodeIds.join(\" -> \")}`)\n }\n\n return issues\n}\n\nfunction isTerminalOrPauseNode(type: NodeType): boolean {\n return type === \"end\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction isPauseCapableNode(type: NodeType): boolean {\n // Nodes that either pause the run (checkpoint, subProcess,\n // signal.wait, timer.boundary) or consume an external event (agent).\n // A cycle is safe if it crosses at least one such node — otherwise the\n // walker would spin forever.\n return type === \"agent\"\n || type === \"checkpoint\"\n || type === \"subProcess\"\n || type === \"signal.wait\"\n || type === \"timer.boundary\"\n}\n\nfunction detectCyclesWithoutPauseNodes(wf: Workflow): string[][] {\n const adj = new Map<string, string[]>()\n for (const e of wf.edges) {\n if (!adj.has(e.from)) adj.set(e.from, [])\n adj.get(e.from)!.push(e.to)\n }\n const byId = new Map(wf.nodes.map((n) => [n.id, n] as const))\n const results: string[][] = []\n const color = new Map<string, 0 | 1 | 2>() // 0=unvisited, 1=in-progress, 2=done\n const stack: string[] = []\n const dfs = (id: string) => {\n if (color.get(id) === 1) {\n // cycle: slice from first occurrence\n const i = stack.indexOf(id)\n if (i >= 0) {\n const cycle = stack.slice(i).concat(id)\n const hasPause = cycle.some((nid) => {\n const t = byId.get(nid)?.type\n return t ? isPauseCapableNode(t) : false\n })\n if (!hasPause) results.push(cycle)\n }\n return\n }\n if (color.get(id) === 2) return\n color.set(id, 1)\n stack.push(id)\n for (const next of adj.get(id) ?? []) dfs(next)\n stack.pop()\n color.set(id, 2)\n }\n for (const n of wf.nodes) if (color.get(n.id) !== 2) dfs(n.id)\n return results\n}\n\n// --------------------------- Run model ------------------------------------\n\nexport const runStatusSchema = z.enum([\n \"running\",\n \"paused\",\n \"completed\",\n \"failed\",\n \"canceled\",\n])\nexport type RunStatus = z.infer<typeof runStatusSchema>\n\nexport const nodeExecutionStatusSchema = z.enum([\n \"ok\",\n \"failed\",\n \"timeout\",\n \"skipped\",\n \"paused\",\n \"resumed\",\n])\nexport type NodeExecutionStatus = z.infer<typeof nodeExecutionStatusSchema>\n\nexport const nodeExecutionEntrySchema = z.object({\n at: z.string(), // ISO-8601\n nodeId: z.string(),\n /** Which upstream node outputs fed this execution. Usually one id, or\n * empty for the trigger. */\n inputKeys: z.array(z.string()).default([]),\n status: nodeExecutionStatusSchema,\n /** The node's output bundle, if successful. Stored here so the full run\n * history can be replayed without the live context. */\n output: z.record(z.unknown()).optional(),\n /** Per-(runId, nodeId, eventId) key used to drop duplicate webhook\n * deliveries when a run is resumed from a checkpoint. */\n idempotencyKey: z.string(),\n note: z.string().optional(),\n})\nexport type NodeExecutionEntry = z.infer<typeof nodeExecutionEntrySchema>\n\nexport const entityRefSchema = z.object({\n backend: z.string(),\n /** Stable identifier for the entity this run is scoped to. Used to key\n * the entity index so webhook re-entry can find the active run. */\n id: z.string(),\n})\nexport type EntityRef = z.infer<typeof entityRefSchema>\n\n// Discriminated `pausedAt` — each kind has its own resume path in the\n// dispatcher. The original checkpoint shape is the `checkpoint` variant;\n// new BPM nodes add subProcess / signalWait / timerWait.\nexport const pausedAtSchema = z.discriminatedUnion(\"kind\", [\n z.object({\n kind: z.literal(\"checkpoint\"),\n nodeId: z.string(),\n checkpointName: z.string(),\n /** Filter applied to incoming events for resume. Same shape as the\n * matching trigger.channel filter. */\n resumeMatch: z.record(z.unknown()).default({}),\n }),\n z.object({\n kind: z.literal(\"subProcess\"),\n nodeId: z.string(),\n childRunId: z.string(),\n childWorkflowId: z.string(),\n }),\n z.object({\n kind: z.literal(\"signalWait\"),\n nodeId: z.string(),\n signalName: z.string(),\n /** Optional field-match filter against the emitted signal payload. */\n match: z.record(z.unknown()).default({}),\n scope: z.enum([\"workflow\", \"global\"]).default(\"workflow\"),\n }),\n z.object({\n kind: z.literal(\"timerWait\"),\n nodeId: z.string(),\n /** ISO-8601 instant when the timer should fire. */\n fireAt: z.string(),\n }),\n])\nexport type PausedAt = z.infer<typeof pausedAtSchema>\n\nexport const workflowRunSchema = z.object({\n id: z.string(),\n workflowId: z.string(),\n workflowVersion: z.literal(2),\n homeNode: z.string(),\n status: runStatusSchema,\n pausedAt: pausedAtSchema.optional(),\n /** Output bundles keyed by producing node id. Downstream nodes read via\n * `{{<nodeId>.<path>}}` in templates. */\n context: z.record(z.record(z.unknown())).default({}),\n /** Node ids queued for execution next. The walk driver pops from this\n * until it's empty or a checkpoint pauses the run. */\n pending: z.array(z.string()).default([]),\n /** Per-`gateway.parallel(mode=join)` arrival tracker. Keys are join node\n * ids; values are the upstream node ids that have already delivered.\n * The join fires once when arrived.length === #incoming edges. Entries\n * are deleted from the map on fire so repeated passes (rare, but\n * possible via crash + retry) stay idempotent. */\n joinCounters: z.record(z.array(z.string())).default({}),\n entityRef: entityRefSchema,\n history: z.array(nodeExecutionEntrySchema).default([]),\n /** Parent run id if this run is a sub-process child. Root runs have no\n * parent. */\n parentRunId: z.string().nullable().default(null),\n /** The parent node in the parent run that spawned this child. Null on\n * root runs. */\n parentNodeId: z.string().nullable().default(null),\n /** Root ancestor. Self-referential on root runs. Lets queries fetch the\n * entire composition tree in one shot. */\n rootRunId: z.string().nullable().default(null),\n /** Depth from root. 0 on root runs, N on Nth-level descendants. */\n depth: z.number().int().min(0).default(0),\n createdAt: z.string(),\n updatedAt: z.string(),\n})\nexport type WorkflowRun = z.infer<typeof workflowRunSchema>\n","import type { TraceRecord, TraceStepRecord } from \"@/storage/traces\"\nimport { lintWorkflow, workflowSchema, type Workflow } from \"./types\"\n\n// --- LLM workflow architect ---\n//\n// The deterministic draft path produces a 3-node workflow: trigger → agent\n// → end, with the entire procedure crammed into the agent's prompt. That's\n// useful as a sanity-check skeleton but un-edittable in any meaningful\n// way — there's no DAG structure for an operator to refine.\n//\n// This module asks an LLM to do the structural work: read the trace + step\n// list, group related tool calls into semantic nodes, identify branching\n// points, and emit a proper workflow DAG. We force tool_use so the response\n// is structured JSON, validate against `workflowSchema`, and retry once\n// with the validation errors as feedback if the first attempt failed.\n//\n// Failure modes:\n// - ANTHROPIC_API_KEY missing → throw (caller falls back to deterministic)\n// - API error / timeout → throw (caller falls back to deterministic)\n// - Both attempts produce schema-invalid workflows → throw (caller falls back)\n//\n// Why direct API instead of the agent registry: same reasoning as\n// extract.structured — registry routing keeps a turn slot busy for >2s on\n// warm cache, the SDK adds 10MB+ of dependencies, and the auth path\n// (ANTHROPIC_API_KEY) is identical.\n\nexport interface ArchitectOptions {\n /** Anthropic model id. Defaults to claude-sonnet-4-6 — sonnet handles\n * multi-step decomposition well; haiku is cheaper but tends to flatten\n * branches. */\n model?: string\n /** Per-call timeout for each Anthropic round-trip. Default 90s. */\n timeoutMs?: number\n /** Cap output tokens. Default 4000 — enough for ~30-node workflows. */\n maxTokens?: number\n /** Hook used by tests to inject a fake fetch. Production uses global fetch. */\n fetchImpl?: typeof fetch\n}\n\nconst ANTHROPIC_API = \"https://api.anthropic.com/v1/messages\"\nconst ANTHROPIC_VERSION = \"2023-06-01\"\nconst DEFAULT_MODEL = \"claude-sonnet-4-6\"\n\nconst ARCHITECT_SYSTEM = `You are a workflow architect for AgentX. Given a successful task trace, decompose it into a REUSABLE workflow DAG. The same workflow must apply across project, platform, and environment variations — extract anything that varies between runs into the trigger's inputSchema and reference it symbolically.\n\nPARAMETERIZATION (this is the most important rule):\nThe procedure should be one workflow, not one workflow per project / branch / environment. When you see specific values in the trace, ask \"would this be different on a future run of the same procedure?\" — if yes, extract them as inputs.\n\nAlways extract into trigger.config.inputSchema.properties when the trace shows them:\n- platform — \"github\" | \"gitlab\" (only when both are plausible for this work)\n- project — full repo path or group/repo identifier (\"mtgl/mtgl_system\", \"anis-marrouchi/agentx\")\n- environment — \"staging\" | \"production\" | \"dev\" (infer from hostnames, paths, message text)\n- ref — branch / tag / commit (\"master\", \"main\", \"release/2.4\")\n- id — issue / MR / PR number\n- host — SSH target or service hostname\n- agentId — only when the work could legitimately be assigned to different agents\n\nEach input must be a typed JSON-Schema property under trigger.config.inputSchema.properties with type, description, and (where useful) enum, default, or example. List the actual required ones in trigger.config.inputSchema.required.\n\nReference inputs symbolically EVERYWHERE:\n- BAD: \"ssh root@staging.mtgl.com.sa 'cd /var/www/test_mtglbackend && git pull origin master'\"\n- GOOD: \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'\"\n\nCross-reference between nodes the same way: {{previousNodeId.outputField}} for chained steps.\n\nOUTPUT CONTRACT:\nThe end node must declare what the workflow produces — operators reading the workflow card need to see what comes out. Use end.config.output as a structured object literal of the form { status: \"...\", \"<key>\": \"{{node.field}}\" }, not a free-text string. Pull values from the nodes that produced them.\n\nDecomposition rules:\n- Group related tool calls into one semantic node. Five Bash calls that pull a repo, install deps, run migrations, restart services, and verify status are ONE deploy node, not five.\n- Prefer LINEAR workflows. Use branch ONLY when the procedure had a clear conditional fork that future runs will also need (e.g. \"if migration → backup db first; else skip\"). Don't invent branches the trace didn't actually exercise.\n- Use deterministic node types (action.run, action.builtin, transform) when the work is mechanical.\n- Use an \"agent\" node only when the step genuinely requires LLM reasoning (drafting prose, classifying intent, synthesizing output).\n\nAvailable node types and their configs:\n- trigger.manual config: { inputSchema: { type:\"object\", properties: {...}, required: [...] } }\n- agent config: { agentId: string, prompt: string }\n- transform config: { expression: string } (jq-style data shaping)\n- action.run config: { command: string } (shell, supports {{templates}})\n- action.send config: { channel: string, chatId: string, text: string }\n- action.builtin config: { name: string, input: object } (e.g. name: \"http.fetch\")\n- extract.structured config: { prompt: string, schema: object }\n- branch config: { cases: [{ when: <Condition>, to: <port-name> }, ...], default?: <port-name> }\n- rule config: { rows: [...] } (DMN decision table)\n- checkpoint config: { label: string }\n- end config: { status: \"completed\"|\"failed\", output: object }\n\nBranch/rule mechanics — read carefully:\n- The \"to\" field in cases is a PORT NAME (a string label), NOT a node id.\n- Each outgoing edge from a branch must have fromPort equal to one of the port names declared in cases or in default.\n- The edge.to field is the destination node id.\n\nWORKED EXAMPLE — a deploy workflow that handles ANY project/env/branch via inputs:\n{\n \"title\": \"Deploy MR to environment\",\n \"description\": \"Pull a branch onto a host, run migrations if present, rebuild Laravel caches. Same procedure for any project/env/branch — pass them as inputs.\",\n \"nodes\": [\n {\n \"id\": \"trigger\",\n \"type\": \"trigger.manual\",\n \"config\": {\n \"inputSchema\": {\n \"type\": \"object\",\n \"properties\": {\n \"project\": { \"type\": \"string\", \"description\": \"group/repo (e.g. 'mtgl/mtgl_system')\" },\n \"environment\": { \"type\": \"string\", \"enum\": [\"staging\", \"production\", \"dev\"], \"default\": \"staging\" },\n \"host\": { \"type\": \"string\", \"description\": \"SSH target hostname (e.g. 'staging.mtgl.com.sa')\" },\n \"path\": { \"type\": \"string\", \"description\": \"Absolute path on host where the project is deployed\" },\n \"ref\": { \"type\": \"string\", \"description\": \"Branch/tag/commit to deploy\", \"default\": \"master\" }\n },\n \"required\": [\"project\", \"environment\", \"host\", \"path\", \"ref\"]\n }\n }\n },\n { \"id\": \"pull\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && git pull origin {{trigger.input.ref}}'\" } },\n { \"id\": \"needs_backup\", \"type\": \"branch\", \"config\": { \"cases\": [{ \"when\": { \"kind\": \"matches\", \"params\": { \"path\": \"pull.stdout\", \"pattern\": \"migrate\" } }, \"to\": \"yes\" }], \"default\": \"no\" } },\n { \"id\": \"backup_db\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'pg_dump app > /backups/{{trigger.input.environment}}-$(date +%Y%m%d-%H%M%S).sql'\" } },\n { \"id\": \"migrate\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan migrate --force'\" } },\n { \"id\": \"rebuild_cache\", \"type\": \"action.run\", \"config\": { \"command\": \"ssh {{trigger.input.host}} 'cd {{trigger.input.path}} && php artisan config:cache && php artisan route:cache'\" } },\n {\n \"id\": \"done\",\n \"type\": \"end\",\n \"config\": {\n \"status\": \"completed\",\n \"output\": {\n \"project\": \"{{trigger.input.project}}\",\n \"environment\": \"{{trigger.input.environment}}\",\n \"ref\": \"{{trigger.input.ref}}\",\n \"deployedAt\": \"{{rebuild_cache.completedAt}}\",\n \"migrated\": \"{{needs_backup.matchedPort}}\"\n }\n }\n }\n ],\n \"edges\": [\n { \"from\": \"trigger\", \"to\": \"pull\" },\n { \"from\": \"pull\", \"to\": \"needs_backup\" },\n { \"from\": \"needs_backup\", \"to\": \"backup_db\", \"fromPort\": \"yes\" },\n { \"from\": \"needs_backup\", \"to\": \"migrate\", \"fromPort\": \"no\" },\n { \"from\": \"backup_db\", \"to\": \"migrate\" },\n { \"from\": \"migrate\", \"to\": \"rebuild_cache\" },\n { \"from\": \"rebuild_cache\", \"to\": \"done\" }\n ]\n}\n\nNotice three things:\n1. The trigger declares a typed inputSchema. An operator running this workflow knows EXACTLY what to pass in.\n2. Every command references {{trigger.input.X}}, never literal hostnames or paths.\n3. The end node's output is a structured object so callers know what they get back.\n\nConstraints:\n- Exactly one trigger.* node and at least one \"end\" node.\n- DAG only (no cycles).\n- Every edge's from/to must reference an existing node id.\n- Node ids match /^[a-z0-9][a-z0-9_]*$/.\n- Don't invent fields not in the spec above. Don't add config keys you didn't see in this spec.\n- The trigger MUST declare inputSchema unless the procedure genuinely takes no parameters.`\n\n/** JSON-Schema fed to the Anthropic forced tool_use. Shape mirrors the\n * subset of workflowSchema that the LLM should produce — the rest of the\n * workflow (status, sourceTaskIds, etc.) is filled in by the caller. */\nconst ARCHITECT_OUTPUT_SCHEMA: Record<string, unknown> = {\n type: \"object\",\n required: [\"title\", \"description\", \"nodes\", \"edges\"],\n properties: {\n title: { type: \"string\", maxLength: 120 },\n description: { type: \"string\", maxLength: 600 },\n nodes: {\n type: \"array\",\n minItems: 2,\n maxItems: 30,\n items: {\n type: \"object\",\n required: [\"id\", \"type\", \"config\"],\n properties: {\n id: { type: \"string\", pattern: \"^[a-z0-9][a-z0-9_]*$\", maxLength: 48 },\n type: {\n type: \"string\",\n enum: [\n \"trigger.manual\",\n \"agent\",\n \"transform\",\n \"branch\",\n \"rule\",\n \"action.run\",\n \"action.send\",\n \"action.builtin\",\n \"extract.structured\",\n \"checkpoint\",\n \"end\",\n ],\n },\n config: { type: \"object\" },\n },\n },\n },\n edges: {\n type: \"array\",\n maxItems: 60,\n items: {\n type: \"object\",\n required: [\"from\", \"to\"],\n properties: {\n from: { type: \"string\" },\n to: { type: \"string\" },\n fromPort: { type: \"string\" },\n label: { type: \"string\" },\n },\n },\n },\n },\n}\n\nfunction cap(s: string | null | undefined, n: number): string {\n const t = String(s ?? \"\").trim()\n return t.length > n ? `${t.slice(0, n)}…` : t\n}\n\nfunction summarizeTraceForPrompt(trace: TraceRecord, steps: TraceStepRecord[]): string {\n const useful = steps.filter((s) => s.status !== \"error\" && s.name !== \"system\").slice(0, 40)\n const lines = useful.map((s, i) => {\n const label = [s.name, s.action].filter(Boolean).join(\":\")\n const inSummary = cap(s.inputSummary || \"\", 280)\n const outSummary = cap(s.outputSummary || \"\", 280)\n const parts = [inSummary && `in=${inSummary}`, outSummary && `out=${outSummary}`].filter(Boolean).join(\" | \")\n return `${i + 1}. ${label}${parts ? ` ${parts}` : \"\"}`\n })\n return [\n `Original task message:`,\n cap(trace.messagePreview || \"\", 600),\n ``,\n `Agent: ${trace.agentId} (channel=${trace.channel || \"unknown\"}, chatId=${trace.chatId || \"—\"})`,\n `Outcome: ${trace.status}, steps=${steps.length}`,\n ``,\n `Procedure evidence (filtered, in order):`,\n ...lines,\n ].join(\"\\n\")\n}\n\ninterface ArchitectDraftShape {\n title: string\n description: string\n nodes: Array<{ id: string; type: string; config: Record<string, unknown> }>\n edges: Array<{ from: string; to: string; fromPort?: string; label?: string }>\n}\n\nasync function callAnthropic(\n apiKey: string,\n model: string,\n systemPrompt: string,\n userMessage: string,\n feedback: string | null,\n opts: { maxTokens: number; timeoutMs: number; fetchImpl: typeof fetch },\n): Promise<{ data: ArchitectDraftShape; usage: { inputTokens: number; outputTokens: number } }> {\n const messages: Array<{ role: string; content: string }> = [\n { role: \"user\", content: userMessage },\n ]\n if (feedback) {\n messages.push({\n role: \"assistant\",\n content: \"(previous attempt; will retry with corrections)\",\n })\n messages.push({ role: \"user\", content: feedback })\n }\n\n const requestBody = {\n model,\n max_tokens: opts.maxTokens,\n system: systemPrompt,\n messages,\n tools: [{\n name: \"emit_workflow\",\n description: \"Return the workflow DAG. Must be valid against the schema.\",\n input_schema: ARCHITECT_OUTPUT_SCHEMA,\n }],\n tool_choice: { type: \"tool\", name: \"emit_workflow\" },\n }\n\n const ctrl = new AbortController()\n const t = setTimeout(() => ctrl.abort(), opts.timeoutMs)\n let res: Response\n try {\n res = await opts.fetchImpl(ANTHROPIC_API, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"x-api-key\": apiKey,\n \"anthropic-version\": ANTHROPIC_VERSION,\n },\n body: JSON.stringify(requestBody),\n signal: ctrl.signal,\n })\n } finally {\n clearTimeout(t)\n }\n\n if (!res.ok) {\n const errBody = await res.text().catch(() => \"\")\n throw new Error(`anthropic API ${res.status}: ${errBody.slice(0, 500)}`)\n }\n const body: any = await res.json()\n const blocks = Array.isArray(body.content) ? body.content : []\n const toolUse = blocks.find((b: any) => b?.type === \"tool_use\" && b?.name === \"emit_workflow\")\n if (!toolUse || !toolUse.input) {\n throw new Error(\"anthropic API returned no emit_workflow tool_use\")\n }\n const usage = body.usage ?? {}\n return {\n data: toolUse.input as ArchitectDraftShape,\n usage: {\n inputTokens: typeof usage.input_tokens === \"number\" ? usage.input_tokens : 0,\n outputTokens: typeof usage.output_tokens === \"number\" ? usage.output_tokens : 0,\n },\n }\n}\n\nfunction assembleWorkflow(\n raw: ArchitectDraftShape,\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n): Workflow {\n const channelTags = new Set<string>()\n if (trace.channel) channelTags.add(trace.channel)\n for (const s of steps) {\n if (s.action) channelTags.add(s.action.toLowerCase().replace(/[^a-z0-9]+/g, \"-\").replace(/^-|-$/g, \"\"))\n }\n return workflowSchema.parse({\n id: inferredId,\n version: 2,\n title: cap(raw.title, 120) || `Draft: ${cap(trace.messagePreview, 72) || trace.taskId.slice(0, 8)}`,\n description: cap(raw.description, 600) || `LLM-architected from task trace ${trace.taskId}.`,\n status: \"draft\",\n state: \"disabled\",\n tags: Array.from(channelTags).slice(0, 12),\n entity: trace.chatId || undefined,\n generatedFrom: \"llm-architect\",\n sourceTaskIds,\n confidence,\n workflowVersion: \"draft-1\",\n ownerAgent: trace.agentId,\n nodes: raw.nodes,\n edges: raw.edges,\n envAllow: [],\n retention: { maxRuns: 500, maxDays: 90 },\n maxChildDepth: 5,\n })\n}\n\n/** Pull the first balanced JSON object out of free-form agent reply text.\n * Agents tend to wrap JSON in fenced code blocks, prose preambles, or both.\n * We scan for `{` and walk forward counting braces, ignoring braces inside\n * string literals. Returns null if no balanced object is found. */\nfunction extractFirstJsonObject(text: string): string | null {\n // Strip code fences first — they're the common case and the brace walker\n // doesn't need to handle them, but they're free to remove.\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)```/i)\n const haystack = fenced ? fenced[1] : text\n const start = haystack.indexOf(\"{\")\n if (start < 0) return null\n let depth = 0\n let inString = false\n let escape = false\n for (let i = start; i < haystack.length; i++) {\n const ch = haystack[i]\n if (escape) { escape = false; continue }\n if (ch === \"\\\\\" && inString) { escape = true; continue }\n if (ch === '\"') { inString = !inString; continue }\n if (inString) continue\n if (ch === \"{\") depth++\n else if (ch === \"}\") {\n depth--\n if (depth === 0) return haystack.slice(start, i + 1)\n }\n }\n return null\n}\n\n/** Dispatch a task to the agent registry over HTTP, ask the agent to emit\n * workflow JSON, parse + validate. Same fallback semantics as the direct\n * API path: throw on any failure so the caller can fall back to the\n * deterministic single-node draft. Uses the daemon's POST /task endpoint\n * rather than registry.execute() directly because the architect runs from\n * the CLI process which doesn't have the registry in scope. */\nasync function callAgent(\n agentId: string,\n systemPrompt: string,\n userMessage: string,\n feedback: string | null,\n chatId: string,\n opts: { daemonUrl: string; timeoutMs: number; fetchImpl: typeof fetch },\n): Promise<{ data: ArchitectDraftShape; usage: { inputTokens: number; outputTokens: number } }> {\n // First-attempt: full system + spec + trace. Retry: short corrective\n // message — the agent already has its own prior reply in context via\n // claude --resume, so we don't need to repeat the spec. Stable chatId\n // (passed in from the caller) keeps both calls in the same session.\n const fullMessage = feedback\n ? [\n `Your previous response failed workflow-schema validation with these issues:`,\n feedback,\n ``,\n `Re-emit the corrected workflow as a single JSON object — same shape as before, but fix the listed issues. No prose, no code fences.`,\n ].join(\"\\n\")\n : [\n systemPrompt,\n \"\",\n \"---\",\n \"\",\n \"Task trace to architect:\",\n \"\",\n userMessage,\n \"\",\n \"---\",\n \"\",\n `Reply with ONLY a JSON object matching this shape (no prose, no fenced code block, no commentary):`,\n `{`,\n ` \"title\": \"<short title>\",`,\n ` \"description\": \"<short description>\",`,\n ` \"nodes\": [{ \"id\": \"...\", \"type\": \"...\", \"config\": {} }, ...],`,\n ` \"edges\": [{ \"from\": \"...\", \"to\": \"...\", \"fromPort\": \"...\" (optional), \"label\": \"...\" (optional) }, ...]`,\n `}`,\n ].join(\"\\n\")\n\n const ctrl = new AbortController()\n const timer = setTimeout(() => ctrl.abort(), opts.timeoutMs)\n let res: Response\n try {\n res = await opts.fetchImpl(`${opts.daemonUrl.replace(/\\/+$/, \"\")}/task`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n agent: agentId,\n message: fullMessage,\n // Only the first call uses freshSession — the retry needs to see\n // the agent's prior reply (via --resume replay) so it can fix\n // its own output rather than start over from scratch.\n freshSession: feedback === null,\n context: { channel: \"api\", chatId },\n }),\n signal: ctrl.signal,\n })\n } finally {\n clearTimeout(timer)\n }\n if (!res.ok) {\n const errBody = await res.text().catch(() => \"\")\n throw new Error(`agent /task ${res.status}: ${errBody.slice(0, 500)}`)\n }\n const body: any = await res.json()\n const content = String(body.content ?? \"\").trim()\n if (body.error) throw new Error(`agent reported error: ${body.error}`)\n if (!content) throw new Error(\"agent returned empty content\")\n\n const jsonStr = extractFirstJsonObject(content)\n if (!jsonStr) throw new Error(`agent reply has no JSON object (first 200 chars: ${content.slice(0, 200)})`)\n let parsed: unknown\n try { parsed = JSON.parse(jsonStr) } catch (e: any) {\n throw new Error(`agent reply JSON is unparseable: ${e.message}`)\n }\n if (!parsed || typeof parsed !== \"object\") throw new Error(\"agent reply is not a JSON object\")\n const data = parsed as ArchitectDraftShape\n if (!Array.isArray(data.nodes) || !Array.isArray(data.edges)) {\n throw new Error(\"agent reply missing nodes/edges arrays\")\n }\n\n const usage = body.usage ?? {}\n return {\n data,\n usage: {\n inputTokens: typeof usage.inputTokens === \"number\" ? usage.inputTokens : 0,\n outputTokens: typeof usage.outputTokens === \"number\" ? usage.outputTokens : 0,\n },\n }\n}\n\n/** Architect a workflow via the agent registry instead of direct Anthropic\n * API. Mirrors `architectWorkflowFromTrace` but routes the LLM call through\n * POST /task → registry.execute → claude-code subprocess (or codex-cli).\n * Operators don't need a raw ANTHROPIC_API_KEY — the agent's own session\n * token handles auth. Trade-off: ~5-10s per call vs ~2s for direct API,\n * fine for cron use. */\nexport async function architectWorkflowViaAgent(\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n opts: { agentId: string; daemonUrl?: string; timeoutMs?: number; fetchImpl?: typeof fetch } = { agentId: \"\" },\n): Promise<{ workflow: Workflow; usage: { inputTokens: number; outputTokens: number }; via: string }> {\n if (!opts.agentId) throw new Error(\"architectWorkflowViaAgent: agentId is required\")\n const daemonUrl = opts.daemonUrl || \"http://127.0.0.1:18800\"\n const fetchImpl = opts.fetchImpl || fetch\n const callOpts = {\n daemonUrl,\n timeoutMs: opts.timeoutMs ?? 180_000, // 3 min — agent dispatch is slower than direct API\n fetchImpl,\n }\n const userMessage = summarizeTraceForPrompt(trace, steps)\n // Stable per-trace chatId so the retry's --resume replay surfaces the\n // agent's first reply. SessionStore keys on (agentId, channel, chatId);\n // sessions persist past the call but the unique-per-trace shape means\n // unrelated absorbs don't share context.\n const chatId = `architect-${trace.taskId.toLowerCase()}`\n\n const first = await callAgent(opts.agentId, ARCHITECT_SYSTEM, userMessage, null, chatId, callOpts)\n let usage = first.usage\n let raw = first.data\n\n let workflow: Workflow\n try {\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`lint: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, via: `agent:${opts.agentId}` }\n } catch (err: any) {\n const retry = await callAgent(opts.agentId, ARCHITECT_SYSTEM, userMessage, err.message, chatId, callOpts)\n raw = retry.data\n usage = {\n inputTokens: usage.inputTokens + retry.usage.inputTokens,\n outputTokens: usage.outputTokens + retry.usage.outputTokens,\n }\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`agent architect retry still invalid: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, via: `agent:${opts.agentId}` }\n }\n}\n\n/** Architect a workflow from a task trace using an LLM. Throws on any\n * failure (caller falls back to deterministic single-node draft). */\nexport async function architectWorkflowFromTrace(\n trace: TraceRecord,\n steps: TraceStepRecord[],\n inferredId: string,\n sourceTaskIds: string[],\n confidence: number,\n opts: ArchitectOptions = {},\n): Promise<{ workflow: Workflow; usage: { inputTokens: number; outputTokens: number }; model: string }> {\n const apiKey = process.env.ANTHROPIC_API_KEY\n if (!apiKey) throw new Error(\"ANTHROPIC_API_KEY not set; LLM architect unavailable\")\n\n const model = opts.model || DEFAULT_MODEL\n const fetchImpl = opts.fetchImpl || fetch\n const callOpts = {\n maxTokens: opts.maxTokens ?? 4000,\n timeoutMs: opts.timeoutMs ?? 90_000,\n fetchImpl,\n }\n\n const userMessage = summarizeTraceForPrompt(trace, steps)\n\n // First attempt.\n const firstAttempt = await callAnthropic(apiKey, model, ARCHITECT_SYSTEM, userMessage, null, callOpts)\n let raw = firstAttempt.data\n let usage = firstAttempt.usage\n\n let workflow: Workflow\n try {\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`lint: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, model }\n } catch (err: any) {\n // Retry once with structured feedback so the model can correct.\n const feedback = `Your previous response failed validation: ${err.message}. Re-emit emit_workflow with the corrections. Keep the same overall structure but fix the listed issues.`\n const retry = await callAnthropic(apiKey, model, ARCHITECT_SYSTEM, userMessage, feedback, callOpts)\n raw = retry.data\n usage = {\n inputTokens: usage.inputTokens + retry.usage.inputTokens,\n outputTokens: usage.outputTokens + retry.usage.outputTokens,\n }\n workflow = assembleWorkflow(raw, trace, steps, inferredId, sourceTaskIds, confidence)\n const lintIssues = lintWorkflow(workflow)\n if (lintIssues.length) throw new Error(`LLM architect retry still invalid: ${lintIssues.join(\"; \")}`)\n return { workflow, usage, model }\n }\n}\n"],"mappings":"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","names":["z","nodeTypeSchema","conditionKindSchema","conditionSchema","retryPolicySchema","workflowNodeSchema","workflowEdgeSchema","isoDateString","d","workflowSchema","ns","lintWorkflow","wf","issues","byId","triggers","e","trigger","adj","seen","stack","id","next","n","isTerminalOrPauseNode","cases","r","defaultPort","declared","c","outgoing","edge","port","onCycle","detectCyclesWithoutPauseNodes","nodeIds","type","isPauseCapableNode","results","color","dfs","i","cycle","nid","t","runStatusSchema","nodeExecutionStatusSchema","nodeExecutionEntrySchema","entityRefSchema","pausedAtSchema","workflowRunSchema","ANTHROPIC_API","ANTHROPIC_VERSION","DEFAULT_MODEL","ARCHITECT_SYSTEM","ARCHITECT_OUTPUT_SCHEMA","cap","s","n","t","summarizeTraceForPrompt","trace","steps","lines","label","inSummary","outSummary","parts","callAnthropic","apiKey","model","systemPrompt","userMessage","feedback","opts","messages","requestBody","ctrl","res","errBody","body","toolUse","b","usage","assembleWorkflow","raw","inferredId","sourceTaskIds","confidence","channelTags","workflowSchema","extractFirstJsonObject","text","fenced","haystack","start","depth","inString","escape","i","ch","callAgent","agentId","chatId","fullMessage","timer","content","jsonStr","parsed","e","data","architectWorkflowViaAgent","daemonUrl","fetchImpl","callOpts","first","workflow","lintIssues","lintWorkflow","err","retry","architectWorkflowFromTrace","firstAttempt"]}
@@ -1,4 +1,4 @@
1
- import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{l as g}from"./chunk-JGXE2YNV.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
1
+ import{a as w,b as y,c as x}from"./chunk-SFQUP3BP.js";import{m as g}from"./chunk-FDWOECJY.js";import{existsSync as d,promises as h}from"fs";import c from"path";import{execa as C}from"execa";var b={enabled:!0,maxAttempts:3,provider:"claude-code"},v=class{constructor(e,r,i){this.cwd=e;this.config={...b,...r},this.memory=i||new x(e)}config;memory;async detectAndHeal(e,r,i){if(!this.config.enabled)return{healed:!1,attempts:0,error:"Heal disabled",filesChanged:[]};let t=await this.runVerification();if(!t)return{healed:!0,attempts:0,error:"",filesChanged:[]};let n=0,s=t,a=[];for(;n<this.config.maxAttempts;){n++;let l=await this.generateFix(s,e,r,n);if(!l.files.length)break;for(let o of l.files){let m=c.isAbsolute(o.path)?o.path:c.resolve(this.cwd,o.path),p=c.dirname(m);await h.mkdir(p,{recursive:!0}),await h.writeFile(m,o.content,"utf8"),a.push(o.path)}let f=await this.runVerification();if(!f)return i&&await this.memory.recordHeal(i,t,!0,a),{healed:!0,attempts:n,error:t,fix:l.explanation,filesChanged:a};s=f}return i&&await this.memory.recordHeal(i,t,!1,a),{healed:!1,attempts:n,error:s,filesChanged:a}}async runVerification(){let e=[this.config.buildCommand,this.config.lintCommand,this.config.testCommand].filter(Boolean);if(!e.length){let r=await this.detectCommands();e.push(...r)}for(let r of e)try{let[i,...t]=r.split(" ");await C(i,t,{cwd:this.cwd,timeout:6e4,reject:!0})}catch(i){let t=i.stderr||"",n=i.stdout||"",s=i.message||"";return k(`Command failed: ${r}
2
2
  ${t}
3
3
  ${n}
4
4
  ${s}`)}return null}async detectCommands(){let e=[],r=c.resolve(this.cwd,"package.json");if(d(r))try{let t=JSON.parse(await h.readFile(r,"utf8")).scripts||{};t.build&&e.push("npm run build"),t.typecheck&&e.push("npm run typecheck"),t.lint&&e.push("npm run lint"),t.test&&e.push("npm run test")}catch{}return d(c.resolve(this.cwd,"pyproject.toml"))&&d(c.resolve(this.cwd,"conftest.py"))&&e.push("python -m pytest --tb=short -q"),e}async generateFix(e,r,i,t){let n=g(this.config.provider,this.config.apiKey),s=[];for(let o of r.slice(0,5)){let m=c.resolve(this.cwd,o);if(d(m)){let p=await h.readFile(m,"utf8");s.push(`## ${o}
@@ -29,4 +29,4 @@ Fix the error. Use create_files with the corrected file content.`}],f=await n.ge
29
29
  `)+`
30
30
  ... (truncated)`:e.join(`
31
31
  `)}export{v as a};
32
- //# sourceMappingURL=chunk-4ITQP3XL.js.map
32
+ //# sourceMappingURL=chunk-JP5IEXR7.js.map