@askexenow/exe-os 0.9.271 → 0.9.272

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 (305) hide show
  1. package/dist/active-agent-BDYXURXQ.js +26 -0
  2. package/dist/active-agent-YWBGAKGU.js +25 -0
  3. package/dist/agentic-ontology-56VHSVS3.js +25 -0
  4. package/dist/backfill-metadata-G46ABBVR.js +597 -0
  5. package/dist/backfill-metadata-VAV27KJK.js +597 -0
  6. package/dist/behaviors-USUTDXVA.js +25 -0
  7. package/dist/bin/agentic-ontology-backfill.js +5 -5
  8. package/dist/bin/agentic-reflection-backfill.js +6 -6
  9. package/dist/bin/agentic-semantic-label.js +5 -5
  10. package/dist/bin/backfill-conversations.js +4 -4
  11. package/dist/bin/backfill-responses.js +4 -4
  12. package/dist/bin/backfill-vectors.js +5 -5
  13. package/dist/bin/bulk-sync-postgres.js +6 -6
  14. package/dist/bin/cc-doctor.js +4 -4
  15. package/dist/bin/cleanup-stale-review-tasks.js +10 -10
  16. package/dist/bin/cli.js +16 -16
  17. package/dist/bin/exe-agent-config.js +3 -3
  18. package/dist/bin/exe-agent.js +4 -4
  19. package/dist/bin/exe-assign.js +5 -5
  20. package/dist/bin/exe-boot.js +17 -17
  21. package/dist/bin/exe-call.js +4 -4
  22. package/dist/bin/exe-cloud.js +4 -4
  23. package/dist/bin/exe-dispatch.js +10 -10
  24. package/dist/bin/exe-doctor.js +1 -1
  25. package/dist/bin/exe-export-behaviors.js +7 -7
  26. package/dist/bin/exe-forget.js +6 -6
  27. package/dist/bin/exe-gateway.js +7 -7
  28. package/dist/bin/exe-healthcheck.js +4 -4
  29. package/dist/bin/exe-heartbeat.js +10 -10
  30. package/dist/bin/exe-kill.js +13 -13
  31. package/dist/bin/exe-launch-agent.js +37 -19
  32. package/dist/bin/exe-new-employee.js +6 -6
  33. package/dist/bin/exe-pending-messages.js +11 -11
  34. package/dist/bin/exe-pending-notifications.js +10 -10
  35. package/dist/bin/exe-pending-reviews.js +10 -10
  36. package/dist/bin/exe-rename.js +4 -4
  37. package/dist/bin/exe-review.js +12 -12
  38. package/dist/bin/exe-search.js +5 -5
  39. package/dist/bin/exe-session-cleanup.js +15 -15
  40. package/dist/bin/exe-settings.js +5 -5
  41. package/dist/bin/exe-start-codex.js +11 -11
  42. package/dist/bin/exe-start-opencode.js +8 -8
  43. package/dist/bin/exe-status.js +11 -11
  44. package/dist/bin/exe-team.js +3 -3
  45. package/dist/bin/git-sweep.js +11 -11
  46. package/dist/bin/graph-backfill.js +4 -4
  47. package/dist/bin/graph-export.js +5 -5
  48. package/dist/bin/import-history.js +7 -7
  49. package/dist/bin/install.js +6 -6
  50. package/dist/bin/intercom-check.js +4 -4
  51. package/dist/bin/mcp-sessions.js +2 -2
  52. package/dist/bin/orchestration-metrics.js +4 -4
  53. package/dist/bin/postgres-agentic-reflection-backfill.js +2 -2
  54. package/dist/bin/postgres-agentic-semantic-backfill.js +1 -1
  55. package/dist/bin/scan-tasks.js +10 -10
  56. package/dist/bin/setup.js +1 -1
  57. package/dist/bin/shard-migrate.js +4 -4
  58. package/dist/capacity-monitor-IFVRCIM7.js +49 -0
  59. package/dist/capacity-monitor-Q47GBDSY.js +49 -0
  60. package/dist/catchup-brief-RP4QHXNT.js +151 -0
  61. package/dist/catchup-brief-TKA6TEK4.js +151 -0
  62. package/dist/chunk-23KJ2LXY.js +58 -0
  63. package/dist/chunk-2NQQP3FF.js +630 -0
  64. package/dist/chunk-3FU5I3KV.js +526 -0
  65. package/dist/chunk-46IEEKPU.js +13696 -0
  66. package/dist/chunk-57UAFTO2.js +3958 -0
  67. package/dist/chunk-5AS622MM.js +3958 -0
  68. package/dist/chunk-62DEE65H.js +371 -0
  69. package/dist/chunk-64T6DFSS.js +447 -0
  70. package/dist/chunk-6BWDP63Z.js +197 -0
  71. package/dist/chunk-6GPYL7TX.js +214 -0
  72. package/dist/chunk-6KWLUVFL.js +54 -0
  73. package/dist/chunk-6N5ISWBF.js +1148 -0
  74. package/dist/chunk-6OD7PVMC.js +333 -0
  75. package/dist/chunk-7ET5CYTD.js +382 -0
  76. package/dist/chunk-7IWLKR6N.js +76 -0
  77. package/dist/chunk-7OEUOJL5.js +1021 -0
  78. package/dist/chunk-AIRJTKDK.js +204 -0
  79. package/dist/chunk-ATJ3NXDP.js +244 -0
  80. package/dist/chunk-B5IS7LE4.js +128 -0
  81. package/dist/chunk-BKINEQVI.js +244 -0
  82. package/dist/chunk-BOJV6NI3.js +128 -0
  83. package/dist/chunk-BPHWI6N2.js +284 -0
  84. package/dist/chunk-BXCQWWJP.js +185 -0
  85. package/dist/chunk-BZ6K7AY3.js +50 -0
  86. package/dist/chunk-C54KIFLS.js +214 -0
  87. package/dist/chunk-C6OYEJJI.js +260 -0
  88. package/dist/chunk-CHBHR5W6.js +3556 -0
  89. package/dist/chunk-D2T3272U.js +171 -0
  90. package/dist/chunk-DCHEIVGT.js +221 -0
  91. package/dist/chunk-DHIBLMSP.js +30 -0
  92. package/dist/chunk-E2AF2WYY.js +346 -0
  93. package/dist/chunk-E2KZEZZW.js +1090 -0
  94. package/dist/chunk-E4KWB4WM.js +348 -0
  95. package/dist/chunk-ENU7URWK.js +1073 -0
  96. package/dist/chunk-EZ7KAZMC.js +132 -0
  97. package/dist/chunk-F4FSSHR4.js +1073 -0
  98. package/dist/chunk-FPXU56FG.js +346 -0
  99. package/dist/chunk-FY7HHR5I.js +128 -0
  100. package/dist/chunk-G2S2UMU4.js +159 -0
  101. package/dist/chunk-G33BHQCO.js +70 -0
  102. package/dist/chunk-GJQTL7RX.js +127 -0
  103. package/dist/chunk-GLCKDEM2.js +97 -0
  104. package/dist/chunk-GMA34SXV.js +240 -0
  105. package/dist/chunk-GVAVEBYR.js +2091 -0
  106. package/dist/chunk-HOSJTLBQ.js +513 -0
  107. package/dist/chunk-IC6HVAS3.js +56 -0
  108. package/dist/chunk-IDFJNO44.js +1051 -0
  109. package/dist/chunk-ILFJMEY5.js +97 -0
  110. package/dist/chunk-ISQAOSL3.js +1921 -0
  111. package/dist/chunk-J6V2DCZK.js +382 -0
  112. package/dist/chunk-JP4CLFLR.js +1148 -0
  113. package/dist/chunk-JTIOZHWG.js +58 -0
  114. package/dist/chunk-KDICWAYV.js +1345 -0
  115. package/dist/chunk-KOBIB6WG.js +159 -0
  116. package/dist/chunk-KQFDDQB6.js +13696 -0
  117. package/dist/chunk-KZNSOHCB.js +280 -0
  118. package/dist/chunk-LVMBYP3C.js +171 -0
  119. package/dist/chunk-M2WQW5NC.js +227 -0
  120. package/dist/chunk-MY6SP5NZ.js +551 -0
  121. package/dist/chunk-N2ACW2ZG.js +363 -0
  122. package/dist/chunk-NSMJDATI.js +495 -0
  123. package/dist/chunk-NSQ5JE23.js +1090 -0
  124. package/dist/chunk-NZL567WG.js +81 -0
  125. package/dist/chunk-O5OMH6LI.js +244 -0
  126. package/dist/chunk-OBUV3W7L.js +163 -0
  127. package/dist/chunk-OLDS7LJN.js +495 -0
  128. package/dist/chunk-OO2I22RX.js +38 -0
  129. package/dist/chunk-OPUUT33V.js +447 -0
  130. package/dist/chunk-OT3VMTKB.js +50 -0
  131. package/dist/chunk-P6RVIOVA.js +157 -0
  132. package/dist/chunk-PUA5564C.js +210 -0
  133. package/dist/chunk-PUQLKLQX.js +731 -0
  134. package/dist/chunk-QROKS65G.js +76 -0
  135. package/dist/chunk-R54I2N2T.js +818 -0
  136. package/dist/chunk-RCFYQHUP.js +818 -0
  137. package/dist/chunk-RJTND4YS.js +284 -0
  138. package/dist/chunk-SBLHQMMZ.js +81 -0
  139. package/dist/chunk-SG2ANG5C.js +123 -0
  140. package/dist/chunk-SVFNKSZV.js +333 -0
  141. package/dist/chunk-TAQT2DC7.js +330 -0
  142. package/dist/chunk-TB7HFW7M.js +127 -0
  143. package/dist/chunk-UUKDAIH2.js +731 -0
  144. package/dist/chunk-V6VEFEEH.js +1345 -0
  145. package/dist/chunk-VIO2ALGH.js +290 -0
  146. package/dist/chunk-VKCUSNJW.js +377 -0
  147. package/dist/chunk-VRPPJFIQ.js +1921 -0
  148. package/dist/chunk-WP3PVBBP.js +204 -0
  149. package/dist/chunk-WQEUY7DC.js +129 -0
  150. package/dist/chunk-WXMXUKCA.js +262 -0
  151. package/dist/chunk-X2WBH2IO.js +297 -0
  152. package/dist/chunk-X33TSJNO.js +394 -0
  153. package/dist/chunk-X7MMI2UI.js +89 -0
  154. package/dist/chunk-XG3BQZIK.js +85 -0
  155. package/dist/chunk-XIKBIAOS.js +75 -0
  156. package/dist/chunk-XPEB545Q.js +54 -0
  157. package/dist/chunk-XWH2MLWS.js +330 -0
  158. package/dist/chunk-YH7V73XW.js +89 -0
  159. package/dist/chunk-YMLM5D65.js +135 -0
  160. package/dist/chunk-YNJPRQ6J.js +377 -0
  161. package/dist/chunk-YSNEHBI6.js +551 -0
  162. package/dist/chunk-ZD6BMW2K.js +33 -0
  163. package/dist/chunk-ZKG5IYCG.js +668 -0
  164. package/dist/chunk-ZU4K7ZNX.js +197 -0
  165. package/dist/co-activation-HZMJC34P.js +72 -0
  166. package/dist/co-occurrence-AVYXRV4L.js +74 -0
  167. package/dist/core-memory-NPJCVUMF.js +110 -0
  168. package/dist/core-memory-OKGXL33Z.js +110 -0
  169. package/dist/crdt-sync-ZCH55JNR.js +33 -0
  170. package/dist/crm-webhook-6OMVUUGR.js +10 -0
  171. package/dist/crm-webhook-UCWF3XDB.js +10 -0
  172. package/dist/cto-delegation-gate-JFZFZGC2.js +206 -0
  173. package/dist/cto-delegation-gate-K32M4GVM.js +206 -0
  174. package/dist/daemon-orchestration-2Q7BYOHC.js +135 -0
  175. package/dist/daemon-orchestration-4RJ2CZJL.js +135 -0
  176. package/dist/db-backup-5GA2YFDX.js +33 -0
  177. package/dist/dreaming-I6KXO6E2.js +32 -0
  178. package/dist/dreaming-NJBK5ILR.js +32 -0
  179. package/dist/exe-drift-VSMIMHL4.js +68 -0
  180. package/dist/exe-export-DVHHIA6Y.js +73 -0
  181. package/dist/exe-export-GIVQDENS.js +73 -0
  182. package/dist/exe-import-7N46LSMQ.js +76 -0
  183. package/dist/exe-import-FINYUV5T.js +76 -0
  184. package/dist/exe-key-H45JY44F.js +579 -0
  185. package/dist/exe-key-MAEQGTB7.js +579 -0
  186. package/dist/exe-snapshot-3TEM3BFD.js +164 -0
  187. package/dist/exe-snapshot-HECGUHL3.js +164 -0
  188. package/dist/fast-db-init-3CNTADVO.js +7 -0
  189. package/dist/fast-db-init-HXCS2AP5.js +7 -0
  190. package/dist/gateway/index.js +8 -8
  191. package/dist/git-staleness-YCEBBIVK.js +110 -0
  192. package/dist/git-task-sweep-H34STRNT.js +40 -0
  193. package/dist/git-task-sweep-YL7NLDCK.js +40 -0
  194. package/dist/global-procedures-IHZM6C2K.js +20 -0
  195. package/dist/graph-auto-extract-RZQ3MHP2.js +162 -0
  196. package/dist/hooks/bug-report-worker.js +12 -12
  197. package/dist/hooks/codex-stop-task-finalizer.js +12 -12
  198. package/dist/hooks/commit-complete.js +12 -12
  199. package/dist/hooks/error-recall.js +6 -6
  200. package/dist/hooks/exe-heartbeat-hook.js +3 -3
  201. package/dist/hooks/ingest.js +6 -6
  202. package/dist/hooks/instructions-loaded.js +4 -4
  203. package/dist/hooks/manifest.json +19 -19
  204. package/dist/hooks/notification.js +4 -4
  205. package/dist/hooks/post-compact.js +11 -11
  206. package/dist/hooks/post-tool-combined.js +5 -5
  207. package/dist/hooks/pre-compact.js +12 -12
  208. package/dist/hooks/pre-tool-use.js +15 -15
  209. package/dist/hooks/prompt-submit.js +21 -21
  210. package/dist/hooks/session-end.js +16 -16
  211. package/dist/hooks/session-start.js +10 -10
  212. package/dist/hooks/stop.js +15 -15
  213. package/dist/hooks/subagent-stop.js +11 -11
  214. package/dist/hooks/summary-worker.js +15 -15
  215. package/dist/index.js +18 -18
  216. package/dist/installer-4EW5ZDGD.js +296 -0
  217. package/dist/installer-B2JTQO55.js +38 -0
  218. package/dist/installer-MIL352T7.js +342 -0
  219. package/dist/lib/agent-config.js +9 -3
  220. package/dist/lib/cloud-sync.js +4 -4
  221. package/dist/lib/consolidation.js +5 -5
  222. package/dist/lib/database.js +2 -2
  223. package/dist/lib/db.js +2 -2
  224. package/dist/lib/employee-templates.js +4 -4
  225. package/dist/lib/employees.js +2 -2
  226. package/dist/lib/exe-daemon.js +34 -34
  227. package/dist/lib/hybrid-search.js +5 -5
  228. package/dist/lib/identity.js +2 -2
  229. package/dist/lib/messaging.js +10 -10
  230. package/dist/lib/reminders.js +3 -3
  231. package/dist/lib/schedules.js +5 -5
  232. package/dist/lib/session-registry.js +4 -4
  233. package/dist/lib/skill-learning.js +4 -4
  234. package/dist/lib/store.js +4 -4
  235. package/dist/lib/task-router.js +3 -3
  236. package/dist/lib/tasks.js +11 -11
  237. package/dist/lib/tmux-routing.js +9 -9
  238. package/dist/lib/token-spend.js +3 -3
  239. package/dist/mcp/register-tools.js +54 -54
  240. package/dist/mcp/server.js +55 -55
  241. package/dist/mcp/tools/complete-reminder.js +4 -4
  242. package/dist/mcp/tools/create-reminder.js +4 -4
  243. package/dist/mcp/tools/create-task.js +13 -13
  244. package/dist/mcp/tools/deactivate-behavior.js +5 -5
  245. package/dist/mcp/tools/list-reminders.js +4 -4
  246. package/dist/mcp/tools/list-tasks.js +13 -13
  247. package/dist/mcp/tools/send-message.js +12 -12
  248. package/dist/mcp/tools/update-task.js +12 -12
  249. package/dist/mcp-http-config-OJQR246S.js +27 -0
  250. package/dist/memory-cards-IPULSQFA.js +174 -0
  251. package/dist/memory-graph-extractor-3TZZOKHY.js +17 -0
  252. package/dist/memory-poisoning-defense-SGUGR5YJ.js +225 -0
  253. package/dist/memory-reflection-H3WGCEM6.js +238 -0
  254. package/dist/notifications-VWPO6NJF.js +45 -0
  255. package/dist/notifications-WCSRQN2V.js +45 -0
  256. package/dist/orchestration-events-O5PSDEIO.js +25 -0
  257. package/dist/orchestrator-RAPEJUOI.js +33 -0
  258. package/dist/orchestrator-XPG6LJAI.js +33 -0
  259. package/dist/pipeline-router-5NT6FUC3.js +13 -0
  260. package/dist/pipeline-router-KSUXONDT.js +13 -0
  261. package/dist/plan-limits-53NXLNDQ.js +26 -0
  262. package/dist/project-boot-ITN3FZMM.js +299 -0
  263. package/dist/projection-worker-27XX5M2W.js +964 -0
  264. package/dist/reranker-GU7L2PJX.js +19 -0
  265. package/dist/reranker-TZEXIJAN.js +19 -0
  266. package/dist/review-polling-FA2J2Q5O.js +124 -0
  267. package/dist/review-polling-MLS4BQ3N.js +124 -0
  268. package/dist/runtime/index.js +12 -12
  269. package/dist/session-events-WWGF3B2N.js +36 -0
  270. package/dist/session-events-ZHXXAH6B.js +36 -0
  271. package/dist/session-kill-telemetry-O4TJHHOZ.js +29 -0
  272. package/dist/session-scope-CQXB7VMH.js +86 -0
  273. package/dist/session-scope-HHUMJYF6.js +86 -0
  274. package/dist/setup-wizard-UM2RHSBJ.js +12 -0
  275. package/dist/skill-refinement-MJPOHYD5.js +157 -0
  276. package/dist/skill-refinement-NVUBRK22.js +157 -0
  277. package/dist/stack-release-BAPCXMXW.js +713 -0
  278. package/dist/stack-release-W4TWTEZP.js +731 -0
  279. package/dist/steward-gate-VLE7OCKO.js +13 -0
  280. package/dist/task-enforcement-FUHDL6UR.js +391 -0
  281. package/dist/task-enforcement-QL3K4N3F.js +391 -0
  282. package/dist/task-scope-TZYMB634.js +35 -0
  283. package/dist/task-scope-ZVLUBS4C.js +35 -0
  284. package/dist/tasks-crud-4MSLJWXE.js +77 -0
  285. package/dist/tasks-crud-HIPXKRKX.js +77 -0
  286. package/dist/tasks-notify-7JBUNE7R.js +38 -0
  287. package/dist/tasks-notify-UPIJ3L4O.js +38 -0
  288. package/dist/tasks-review-5SJSFTUB.js +47 -0
  289. package/dist/tasks-review-JHSYBR5I.js +47 -0
  290. package/dist/telemetry-upload-BSGOXGUP.js +739 -0
  291. package/dist/telemetry-upload-LTX3C5HZ.js +739 -0
  292. package/dist/token-budget-2CDWQU3Q.js +84 -0
  293. package/dist/tool-telemetry-7YS7EN7B.js +17 -0
  294. package/dist/tui/App.js +17 -17
  295. package/dist/tui-data-GDGBOS6G.js +258 -0
  296. package/dist/tui-data-VXF2RBVM.js +258 -0
  297. package/dist/wiki-acl-MJIMXRQV.js +111 -0
  298. package/dist/worker-gate-WQGTZOSM.js +21 -0
  299. package/dist/worker-gate-X2YDTKTL.js +21 -0
  300. package/dist/workflow-engine-CYXRZXBM.js +28 -0
  301. package/dist/workflow-engine-KMLAXVA4.js +28 -0
  302. package/dist/worktree-NLSKVRNC.js +26 -0
  303. package/dist/worktree-sweep-44TMEPLE.js +19 -0
  304. package/package.json +1 -1
  305. package/release-notes.json +24 -19
@@ -0,0 +1,346 @@
1
+ import {
2
+ recordSessionKill
3
+ } from "./chunk-SG2ANG5C.js";
4
+ import {
5
+ sessionScopeFilter,
6
+ verifyPaneAtCapacity,
7
+ writeNotification
8
+ } from "./chunk-5AS622MM.js";
9
+ import {
10
+ listSessions
11
+ } from "./chunk-OBUV3W7L.js";
12
+ import {
13
+ getTransport
14
+ } from "./chunk-MVW62NIZ.js";
15
+ import {
16
+ recordOrchestrationEventBestEffort
17
+ } from "./chunk-2NQQP3FF.js";
18
+ import {
19
+ getClient,
20
+ isCoordinatorName
21
+ } from "./chunk-CHBHR5W6.js";
22
+
23
+ // src/lib/capacity-monitor.ts
24
+ var CAPACITY_PATTERNS = [
25
+ /conversation is too long/i,
26
+ /maximum context length/i,
27
+ /context window.*(?:limit|exceed|full)/i,
28
+ /reached.*(?:token|context).*limit/i
29
+ ];
30
+ var CONTENT_LINE_PREFIX = /^[\s>#\-*[]/;
31
+ var CONTENT_LINE_MARKERS = [
32
+ "RESUME:",
33
+ "intercom",
34
+ "capacity-monitor",
35
+ "CAPACITY_PATTERNS",
36
+ "isAtCapacity",
37
+ "CONTENT_LINE_MARKERS",
38
+ "pollCapacityDead",
39
+ "confirmCapacityKill",
40
+ "session_kills",
41
+ "capacity-monitor.test"
42
+ ];
43
+ var SOURCE_CODE_MARKERS = [
44
+ /["'`/].*(?:maximum context length|conversation is too long)/i,
45
+ /(?:maximum context length|conversation is too long).*["'`/]/i
46
+ ];
47
+ var RELAUNCH_COOLDOWN_MS = 5 * 60 * 1e3;
48
+ var _lastRelaunch = /* @__PURE__ */ new Map();
49
+ var RESUME_TITLE_PREFIX = "RESUME:";
50
+ var RESUME_TITLE_LIKE_PATTERN = `${RESUME_TITLE_PREFIX} % hit context capacity%`;
51
+ var RESUME_ACTIVE_STATUSES = ["open", "in_progress"];
52
+ function resumeTaskTitle(agentId) {
53
+ return `${RESUME_TITLE_PREFIX} ${agentId} hit context capacity \u2014 continue open tasks`;
54
+ }
55
+ function buildResumeContext(agentId, openTasks) {
56
+ const taskList = openTasks.map(
57
+ (r, i) => `${i + 1}. [${String(r.priority).toUpperCase()}] ${String(r.title)} (${String(r.task_file)})`
58
+ ).join("\n");
59
+ return [
60
+ "## Context",
61
+ "",
62
+ `${agentId} hit context capacity and was auto-relaunched by the capacity monitor.`,
63
+ "Call recall_my_memory first \u2014 search for 'CONTEXT CHECKPOINT'. Pick up where the previous session stopped.",
64
+ "",
65
+ `You have ${openTasks.length} open task(s). Work through them in priority order:`,
66
+ "",
67
+ taskList,
68
+ "",
69
+ "Read each task file and chain through them. Build and commit after each one."
70
+ ].join("\n");
71
+ }
72
+ var CONFIRMATION_WINDOW_MS = 3 * 60 * 1e3;
73
+ var _pendingCapacityKill = /* @__PURE__ */ new Map();
74
+ function filterPaneContent(paneOutput) {
75
+ return paneOutput.split("\n").filter((line) => {
76
+ if (CONTENT_LINE_PREFIX.test(line)) return false;
77
+ for (const marker of CONTENT_LINE_MARKERS) {
78
+ if (line.includes(marker)) return false;
79
+ }
80
+ for (const re of SOURCE_CODE_MARKERS) {
81
+ if (re.test(line)) return false;
82
+ }
83
+ return true;
84
+ }).join("\n");
85
+ }
86
+ var CC_CONTEXT_BAR_RE = /([\u2588\u2591\u2592\u2593]{10})\s+(\d+)%/;
87
+ var CTX_FLOOR_PERCENT = 50;
88
+ function extractContextPercent(paneOutput) {
89
+ const match = paneOutput.match(CC_CONTEXT_BAR_RE);
90
+ if (!match) return null;
91
+ const parsed = Number.parseInt(match[2], 10);
92
+ return Number.isFinite(parsed) ? parsed : null;
93
+ }
94
+ function isAtCapacity(paneOutput) {
95
+ const filtered = filterPaneContent(paneOutput);
96
+ return CAPACITY_PATTERNS.some((p) => p.test(filtered));
97
+ }
98
+ function confirmCapacityKill(agentId, now = Date.now()) {
99
+ const pendingSince = _pendingCapacityKill.get(agentId);
100
+ if (pendingSince === void 0) {
101
+ _pendingCapacityKill.set(agentId, now);
102
+ return false;
103
+ }
104
+ if (now - pendingSince > CONFIRMATION_WINDOW_MS) {
105
+ _pendingCapacityKill.set(agentId, now);
106
+ return false;
107
+ }
108
+ _pendingCapacityKill.delete(agentId);
109
+ return true;
110
+ }
111
+ function _resetPendingCapacityKills() {
112
+ _pendingCapacityKill.clear();
113
+ }
114
+ function _resetLastRelaunchCache() {
115
+ _lastRelaunch.clear();
116
+ }
117
+ async function lastResumeCreatedAtMs(agentId, sessionScope) {
118
+ const client = getClient();
119
+ const cmScope = sessionScopeFilter(sessionScope ?? null);
120
+ const result = await client.execute({
121
+ sql: `SELECT MAX(created_at) AS last_created_at
122
+ FROM tasks
123
+ WHERE assigned_to = ? AND title LIKE ?${cmScope.sql}`,
124
+ args: [agentId, `${RESUME_TITLE_PREFIX} %`, ...cmScope.args]
125
+ });
126
+ const raw = result.rows[0]?.last_created_at;
127
+ if (raw === null || raw === void 0) return null;
128
+ const parsed = Date.parse(String(raw));
129
+ return Number.isNaN(parsed) ? null : parsed;
130
+ }
131
+ async function isWithinRelaunchCooldown(agentId, now = Date.now(), sessionScope) {
132
+ const cacheKey = sessionScope ? `${agentId}::${sessionScope}` : agentId;
133
+ const cached = _lastRelaunch.get(cacheKey);
134
+ if (cached !== void 0) return now - cached < RELAUNCH_COOLDOWN_MS;
135
+ const persisted = await lastResumeCreatedAtMs(agentId, sessionScope);
136
+ if (persisted === null) return false;
137
+ if (now - persisted >= RELAUNCH_COOLDOWN_MS) return false;
138
+ _lastRelaunch.set(cacheKey, persisted);
139
+ return true;
140
+ }
141
+ async function createOrRefreshResumeTask(agentId, projectDir, openTasks, sessionScope) {
142
+ const client = getClient();
143
+ const now = (/* @__PURE__ */ new Date()).toISOString();
144
+ const context = buildResumeContext(agentId, openTasks);
145
+ const rdScope = sessionScopeFilter(sessionScope ?? null);
146
+ const existing = await client.execute({
147
+ sql: `SELECT id FROM tasks
148
+ WHERE assigned_to = ?
149
+ AND title LIKE ?
150
+ AND status IN (${RESUME_ACTIVE_STATUSES.map(() => "?").join(", ")})${rdScope.sql}
151
+ ORDER BY created_at DESC
152
+ LIMIT 1`,
153
+ args: [agentId, RESUME_TITLE_LIKE_PATTERN, ...RESUME_ACTIVE_STATUSES, ...rdScope.args]
154
+ });
155
+ if (existing.rows.length > 0) {
156
+ const taskId = String(existing.rows[0].id);
157
+ await client.execute({
158
+ sql: `UPDATE tasks SET context = ?, updated_at = ? WHERE id = ?`,
159
+ args: [context, now, taskId]
160
+ });
161
+ recordOrchestrationEventBestEffort({
162
+ eventType: "session.resumed",
163
+ source: "capacity-monitor.createOrRefreshResumeTask",
164
+ agentId,
165
+ taskId,
166
+ payload: { created: false, action: "refreshed" }
167
+ });
168
+ return { created: false, taskId };
169
+ }
170
+ const { createTask } = await import("./lib/tasks.js");
171
+ const task = await createTask({
172
+ title: resumeTaskTitle(agentId),
173
+ assignedTo: agentId,
174
+ assignedBy: "system",
175
+ projectName: projectDir.split("/").pop() ?? "unknown",
176
+ priority: "p0",
177
+ context,
178
+ baseDir: projectDir
179
+ });
180
+ recordOrchestrationEventBestEffort({
181
+ eventType: "session.resumed",
182
+ source: "capacity-monitor.createOrRefreshResumeTask",
183
+ agentId,
184
+ taskId: task.id,
185
+ payload: { created: true, action: "created", openTaskCount: openTasks.length }
186
+ });
187
+ return { created: true, taskId: task.id };
188
+ }
189
+ async function pollCapacityDead() {
190
+ const transport = getTransport();
191
+ const relaunched = [];
192
+ const registered = listSessions();
193
+ if (registered.length === 0) return [];
194
+ let liveSessions;
195
+ try {
196
+ liveSessions = transport.listSessions();
197
+ } catch {
198
+ return [];
199
+ }
200
+ for (const entry of registered) {
201
+ const { windowName, agentId, projectDir } = entry;
202
+ if (!liveSessions.includes(windowName)) continue;
203
+ const cmDashIdx = windowName.indexOf("-");
204
+ const cmDerivedScope = cmDashIdx >= 0 ? windowName.slice(cmDashIdx + 1) : null;
205
+ if (await isWithinRelaunchCooldown(agentId, Date.now(), cmDerivedScope)) continue;
206
+ let pane;
207
+ try {
208
+ pane = transport.capturePane(windowName, 15);
209
+ } catch {
210
+ continue;
211
+ }
212
+ if (!isAtCapacity(pane)) continue;
213
+ const ctxPct = extractContextPercent(pane);
214
+ if (ctxPct !== null && ctxPct < CTX_FLOOR_PERCENT) {
215
+ process.stderr.write(
216
+ `[capacity-monitor] ctx-floor: ${agentId} at ${ctxPct}% in ${windowName} \u2014 below ${CTX_FLOOR_PERCENT}%. Skipping capacity kill (likely self-referential content or false positive).
217
+ `
218
+ );
219
+ continue;
220
+ }
221
+ if (!confirmCapacityKill(agentId)) {
222
+ process.stderr.write(
223
+ `[capacity-monitor] ${agentId} matched capacity pattern once in ${windowName}. Awaiting confirmation on next tick.
224
+ `
225
+ );
226
+ continue;
227
+ }
228
+ const verify = await verifyPaneAtCapacity(windowName);
229
+ if (!verify.atCapacity) {
230
+ process.stderr.write(
231
+ `[capacity-monitor] verifyPaneAtCapacity rejected kill for ${agentId} in ${windowName} (reason: ${verify.reason}). Skipping.
232
+ `
233
+ );
234
+ void recordSessionKill({
235
+ sessionName: windowName,
236
+ agentId,
237
+ reason: "capacity_false_positive_blocked"
238
+ });
239
+ continue;
240
+ }
241
+ process.stderr.write(
242
+ `[capacity-monitor] Detected ${agentId} at capacity in session ${windowName} (confirmed). Auto-relaunching.
243
+ `
244
+ );
245
+ if (isCoordinatorName(agentId)) {
246
+ try {
247
+ const { loadConfigSync } = await import("./lib/config.js");
248
+ const cfg = loadConfigSync();
249
+ if (cfg.sessionLifecycle.autoRelaunchCoordinator !== false) {
250
+ const fs = await import("fs");
251
+ const path = await import("path");
252
+ const os = await import("os");
253
+ const markerDir = path.join(os.homedir(), ".exe-os", "session-cache");
254
+ fs.mkdirSync(markerDir, { recursive: true });
255
+ const markerPath = path.join(markerDir, `relaunch-${windowName}.json`);
256
+ fs.writeFileSync(markerPath, JSON.stringify({
257
+ reason: "context_full",
258
+ agentId,
259
+ projectDir,
260
+ timestamp: (/* @__PURE__ */ new Date()).toISOString()
261
+ }));
262
+ process.stderr.write(
263
+ `[capacity-monitor] Coordinator relaunch marker written: ${markerPath}
264
+ `
265
+ );
266
+ }
267
+ transport.kill(windowName);
268
+ void recordSessionKill({ sessionName: windowName, agentId, reason: "capacity" });
269
+ const coordCacheKey = cmDerivedScope ? `${agentId}::${cmDerivedScope}` : agentId;
270
+ _lastRelaunch.set(coordCacheKey, Date.now());
271
+ relaunched.push(agentId);
272
+ } catch (err) {
273
+ process.stderr.write(
274
+ `[capacity-monitor] Coordinator relaunch marker write failed: ${err instanceof Error ? err.message : String(err)}
275
+ `
276
+ );
277
+ }
278
+ continue;
279
+ }
280
+ try {
281
+ transport.kill(windowName);
282
+ void recordSessionKill({
283
+ sessionName: windowName,
284
+ agentId,
285
+ reason: "capacity"
286
+ });
287
+ const client = getClient();
288
+ const dashIdx = windowName.indexOf("-");
289
+ const derivedScope = dashIdx >= 0 ? windowName.slice(dashIdx + 1) : null;
290
+ const rlScope = sessionScopeFilter(derivedScope);
291
+ const openTasks = await client.execute({
292
+ sql: `SELECT id, title, priority, task_file, status
293
+ FROM tasks
294
+ WHERE assigned_to = ? AND status IN ('open', 'in_progress')${rlScope.sql}
295
+ ORDER BY
296
+ CASE priority WHEN 'p0' THEN 0 WHEN 'p1' THEN 1 WHEN 'p2' THEN 2 ELSE 3 END,
297
+ created_at ASC
298
+ LIMIT 10`,
299
+ args: [agentId, ...rlScope.args]
300
+ });
301
+ if (openTasks.rows.length === 0) {
302
+ process.stderr.write(
303
+ `[capacity-monitor] ${agentId} has no open tasks \u2014 skipping relaunch.
304
+ `
305
+ );
306
+ continue;
307
+ }
308
+ const { created } = await createOrRefreshResumeTask(
309
+ agentId,
310
+ projectDir,
311
+ openTasks.rows,
312
+ derivedScope
313
+ );
314
+ if (created) {
315
+ await writeNotification({
316
+ agentId: "system",
317
+ agentRole: "daemon",
318
+ event: "capacity_relaunch",
319
+ project: projectDir.split("/").pop() ?? "unknown",
320
+ summary: `${agentId} hit context capacity. Auto-relaunched with ${openTasks.rows.length} open task(s).`
321
+ });
322
+ }
323
+ const cacheKey = derivedScope ? `${agentId}::${derivedScope}` : agentId;
324
+ _lastRelaunch.set(cacheKey, Date.now());
325
+ if (created) relaunched.push(agentId);
326
+ } catch (err) {
327
+ process.stderr.write(
328
+ `[capacity-monitor] Failed to relaunch ${agentId}: ${err instanceof Error ? err.message : String(err)}
329
+ `
330
+ );
331
+ }
332
+ }
333
+ return relaunched;
334
+ }
335
+
336
+ export {
337
+ CTX_FLOOR_PERCENT,
338
+ extractContextPercent,
339
+ isAtCapacity,
340
+ confirmCapacityKill,
341
+ _resetPendingCapacityKills,
342
+ _resetLastRelaunchCache,
343
+ isWithinRelaunchCooldown,
344
+ createOrRefreshResumeTask,
345
+ pollCapacityDead
346
+ };
@@ -0,0 +1,128 @@
1
+ import {
2
+ getCurrentSessionScope,
3
+ strictSessionScopeFilter
4
+ } from "./chunk-5AS622MM.js";
5
+ import {
6
+ getProjectName
7
+ } from "./chunk-OPU3NYOO.js";
8
+
9
+ // src/lib/session-events.ts
10
+ import { randomUUID } from "crypto";
11
+ async function ensureSessionEventsTable(client) {
12
+ await client.execute(`
13
+ CREATE TABLE IF NOT EXISTS session_events (
14
+ id TEXT PRIMARY KEY,
15
+ agent_id TEXT NOT NULL,
16
+ agent_role TEXT NOT NULL,
17
+ session_id TEXT NOT NULL,
18
+ session_scope TEXT,
19
+ project_name TEXT NOT NULL,
20
+ event_index INTEGER NOT NULL,
21
+ event_type TEXT NOT NULL,
22
+ tool_name TEXT,
23
+ tool_use_id TEXT,
24
+ content TEXT NOT NULL,
25
+ payload_json TEXT,
26
+ has_error INTEGER NOT NULL DEFAULT 0,
27
+ created_at TEXT NOT NULL
28
+ )
29
+ `);
30
+ await client.execute(`
31
+ CREATE INDEX IF NOT EXISTS idx_session_events_agent_time
32
+ ON session_events(agent_id, created_at DESC)
33
+ `);
34
+ await client.execute(`
35
+ CREATE INDEX IF NOT EXISTS idx_session_events_session_index
36
+ ON session_events(session_id, event_index)
37
+ `);
38
+ await client.execute(`
39
+ CREATE INDEX IF NOT EXISTS idx_session_events_scope_agent_time
40
+ ON session_events(session_scope, agent_id, created_at DESC)
41
+ `);
42
+ }
43
+ async function recordSessionEvent(client, input) {
44
+ if (!input.content || input.content.trim().length === 0) return;
45
+ await ensureSessionEventsTable(client);
46
+ const maxResult = await client.execute({
47
+ sql: "SELECT COALESCE(MAX(event_index), 0) AS max_index FROM session_events WHERE session_id = ?",
48
+ args: [input.sessionId]
49
+ });
50
+ const currentMax = Number(maxResult.rows[0]?.max_index ?? 0);
51
+ const eventIndex = Number.isFinite(currentMax) ? currentMax + 1 : 1;
52
+ await client.execute({
53
+ sql: `INSERT INTO session_events (
54
+ id, agent_id, agent_role, session_id, session_scope, project_name,
55
+ event_index, event_type, tool_name, tool_use_id, content,
56
+ payload_json, has_error, created_at
57
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
58
+ args: [
59
+ randomUUID(),
60
+ input.agentId,
61
+ input.agentRole,
62
+ input.sessionId,
63
+ input.sessionScope ?? getCurrentSessionScope(),
64
+ input.projectName ?? getProjectName(input.cwd),
65
+ eventIndex,
66
+ input.eventType,
67
+ input.toolName ?? null,
68
+ input.toolUseId ?? null,
69
+ input.content,
70
+ input.payloadJson ?? null,
71
+ input.hasError ? 1 : 0,
72
+ input.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
73
+ ]
74
+ });
75
+ }
76
+ async function listRecentSessionEvents(client, options) {
77
+ await ensureSessionEventsTable(client);
78
+ const conditions = ["agent_id = ?"];
79
+ const args = [options.agentId];
80
+ if (options.sessionId) {
81
+ conditions.push("session_id = ?");
82
+ args.push(options.sessionId);
83
+ }
84
+ if (options.eventType) {
85
+ conditions.push("event_type = ?");
86
+ args.push(options.eventType);
87
+ }
88
+ if (options.projectName && options.projectName !== "all") {
89
+ conditions.push("project_name = ?");
90
+ args.push(options.projectName);
91
+ }
92
+ const scope = strictSessionScopeFilter(options.sessionScope);
93
+ const where = `WHERE ${conditions.join(" AND ")}${scope.sql}`;
94
+ args.push(...scope.args);
95
+ args.push(Math.min(Math.max(options.limit ?? 20, 1), 100));
96
+ const result = await client.execute({
97
+ sql: `SELECT id, agent_id, agent_role, session_id, session_scope,
98
+ project_name, event_index, event_type, tool_name, tool_use_id,
99
+ content, payload_json, has_error, created_at
100
+ FROM session_events
101
+ ${where}
102
+ ORDER BY created_at DESC, event_index DESC
103
+ LIMIT ?`,
104
+ args
105
+ });
106
+ return result.rows.map((row) => ({
107
+ id: String(row.id),
108
+ agentId: String(row.agent_id),
109
+ agentRole: String(row.agent_role),
110
+ sessionId: String(row.session_id),
111
+ sessionScope: row.session_scope == null ? null : String(row.session_scope),
112
+ projectName: String(row.project_name),
113
+ eventIndex: Number(row.event_index),
114
+ eventType: String(row.event_type),
115
+ toolName: row.tool_name == null ? null : String(row.tool_name),
116
+ toolUseId: row.tool_use_id == null ? null : String(row.tool_use_id),
117
+ content: String(row.content),
118
+ payloadJson: row.payload_json == null ? null : String(row.payload_json),
119
+ hasError: Number(row.has_error) === 1,
120
+ createdAt: String(row.created_at)
121
+ }));
122
+ }
123
+
124
+ export {
125
+ ensureSessionEventsTable,
126
+ recordSessionEvent,
127
+ listRecentSessionEvents
128
+ };
@@ -0,0 +1,159 @@
1
+ import {
2
+ EXE_AI_DIR
3
+ } from "./chunk-VXIMSRTO.js";
4
+
5
+ // src/lib/worker-gate.ts
6
+ import { readdirSync, writeFileSync, unlinkSync, mkdirSync, existsSync, readFileSync } from "fs";
7
+ import path from "path";
8
+ var WORKER_PID_DIR = path.join(EXE_AI_DIR, "worker-pids");
9
+ var MAX_CONCURRENT_WORKERS = 3;
10
+ function tryAcquireWorkerSlot() {
11
+ try {
12
+ mkdirSync(WORKER_PID_DIR, { recursive: true });
13
+ const reservationId = `res-${process.pid}-${Date.now()}`;
14
+ const reservationPath = path.join(WORKER_PID_DIR, `${reservationId}.pid`);
15
+ writeFileSync(reservationPath, String(process.pid));
16
+ const files = readdirSync(WORKER_PID_DIR);
17
+ let alive = 0;
18
+ for (const f of files) {
19
+ if (!f.endsWith(".pid")) continue;
20
+ if (f.startsWith("res-")) {
21
+ const resParts = f.replace(".pid", "").split("-");
22
+ const resPid = parseInt(resParts[1] ?? "", 10);
23
+ if (!isNaN(resPid) && resPid > 0) {
24
+ try {
25
+ process.kill(resPid, 0);
26
+ alive++;
27
+ } catch {
28
+ try {
29
+ unlinkSync(path.join(WORKER_PID_DIR, f));
30
+ } catch {
31
+ }
32
+ }
33
+ } else {
34
+ alive++;
35
+ }
36
+ continue;
37
+ }
38
+ const dashIdx = f.lastIndexOf("-");
39
+ const pid = parseInt(f.slice(dashIdx + 1).replace(".pid", ""), 10);
40
+ if (isNaN(pid)) continue;
41
+ try {
42
+ process.kill(pid, 0);
43
+ alive++;
44
+ } catch {
45
+ try {
46
+ unlinkSync(path.join(WORKER_PID_DIR, f));
47
+ } catch {
48
+ }
49
+ }
50
+ }
51
+ if (alive >= MAX_CONCURRENT_WORKERS) {
52
+ try {
53
+ unlinkSync(reservationPath);
54
+ } catch {
55
+ }
56
+ return false;
57
+ }
58
+ try {
59
+ unlinkSync(reservationPath);
60
+ } catch {
61
+ }
62
+ return true;
63
+ } catch {
64
+ return true;
65
+ }
66
+ }
67
+ function registerWorkerPid(pid) {
68
+ try {
69
+ mkdirSync(WORKER_PID_DIR, { recursive: true });
70
+ writeFileSync(path.join(WORKER_PID_DIR, `worker-${pid}.pid`), String(pid));
71
+ } catch {
72
+ }
73
+ }
74
+ function cleanupWorkerPid() {
75
+ try {
76
+ unlinkSync(path.join(WORKER_PID_DIR, `worker-${process.pid}.pid`));
77
+ } catch {
78
+ }
79
+ }
80
+ var BACKFILL_LOCK = path.join(WORKER_PID_DIR, "backfill.lock");
81
+ function tryAcquireBackfillLock() {
82
+ try {
83
+ mkdirSync(WORKER_PID_DIR, { recursive: true });
84
+ if (existsSync(BACKFILL_LOCK)) {
85
+ try {
86
+ const pid = parseInt(
87
+ readFileSync(BACKFILL_LOCK, "utf8").trim(),
88
+ 10
89
+ );
90
+ if (!isNaN(pid) && pid > 0) {
91
+ try {
92
+ process.kill(pid, 0);
93
+ return false;
94
+ } catch {
95
+ }
96
+ }
97
+ } catch {
98
+ }
99
+ }
100
+ writeFileSync(BACKFILL_LOCK, String(process.pid));
101
+ return true;
102
+ } catch {
103
+ return true;
104
+ }
105
+ }
106
+ function releaseBackfillLock() {
107
+ try {
108
+ unlinkSync(BACKFILL_LOCK);
109
+ } catch {
110
+ }
111
+ }
112
+ async function getTaskAwareCapacity() {
113
+ const { getClient } = await import("./lib/database.js");
114
+ const { sessionScopeFilter } = await import("./task-scope-TZYMB634.js");
115
+ const client = getClient();
116
+ const scope = sessionScopeFilter();
117
+ const result = await client.execute({
118
+ sql: `SELECT assigned_to, COUNT(*) as cnt
119
+ FROM tasks
120
+ WHERE status IN ('open', 'in_progress')${scope.sql}
121
+ GROUP BY assigned_to`,
122
+ args: [...scope.args]
123
+ });
124
+ const busyAgents = [];
125
+ let totalInProgress = 0;
126
+ for (const row of result.rows) {
127
+ const agent = String(row.assigned_to ?? "");
128
+ const count = Number(row.cnt ?? 0);
129
+ if (agent && count > 0) {
130
+ busyAgents.push(agent);
131
+ totalInProgress += count;
132
+ }
133
+ }
134
+ const idleAgents = [];
135
+ try {
136
+ const { listTmuxSessions } = await import("./lib/tmux-status.js");
137
+ const { isExeSession } = await import("./lib/tmux-routing.js");
138
+ const { baseAgentName } = await import("./lib/employees.js");
139
+ const liveSessions = listTmuxSessions().filter((s) => !isExeSession(s) && s.includes("-"));
140
+ const liveAgentNames = new Set(liveSessions.map((s) => baseAgentName(s.split("-")[0] ?? "")));
141
+ for (const name of liveAgentNames) {
142
+ if (name && !busyAgents.includes(name)) {
143
+ idleAgents.push(name);
144
+ }
145
+ }
146
+ } catch {
147
+ }
148
+ return { busyAgents, idleAgents, totalInProgress };
149
+ }
150
+
151
+ export {
152
+ MAX_CONCURRENT_WORKERS,
153
+ tryAcquireWorkerSlot,
154
+ registerWorkerPid,
155
+ cleanupWorkerPid,
156
+ tryAcquireBackfillLock,
157
+ releaseBackfillLock,
158
+ getTaskAwareCapacity
159
+ };
@@ -0,0 +1,70 @@
1
+ import {
2
+ loadEmployees
3
+ } from "./chunk-CHBHR5W6.js";
4
+
5
+ // src/lib/agent-symlinks.ts
6
+ import os from "os";
7
+ import path from "path";
8
+ import {
9
+ existsSync,
10
+ lstatSync,
11
+ mkdirSync,
12
+ readlinkSync,
13
+ symlinkSync
14
+ } from "fs";
15
+ function claudeAgentsDir(homeDir) {
16
+ return path.join(homeDir, ".claude", "agents");
17
+ }
18
+ function identitySourcePath(homeDir, agentId) {
19
+ return path.join(homeDir, ".exe-os", "identity", `${agentId}.md`);
20
+ }
21
+ function claudeAgentLinkPath(homeDir, agentId) {
22
+ return path.join(claudeAgentsDir(homeDir), `${agentId}.md`);
23
+ }
24
+ function ensureAgentSymlink(agentId, homeDir = os.homedir()) {
25
+ const target = identitySourcePath(homeDir, agentId);
26
+ const link = claudeAgentLinkPath(homeDir, agentId);
27
+ mkdirSync(claudeAgentsDir(homeDir), { recursive: true });
28
+ if (existsSync(link)) {
29
+ let stat;
30
+ try {
31
+ stat = lstatSync(link);
32
+ } catch {
33
+ return { agentId, action: "conflict", target, link, conflict: "stat_failed" };
34
+ }
35
+ if (!stat.isSymbolicLink()) {
36
+ return { agentId, action: "conflict", target, link, conflict: "regular_file" };
37
+ }
38
+ let currentTarget;
39
+ try {
40
+ currentTarget = readlinkSync(link);
41
+ } catch {
42
+ return { agentId, action: "conflict", target, link, conflict: "readlink_failed" };
43
+ }
44
+ if (currentTarget === target) {
45
+ return { agentId, action: "already_correct", target, link };
46
+ }
47
+ return { agentId, action: "conflict", target, link, conflict: `points_to:${currentTarget}` };
48
+ }
49
+ try {
50
+ symlinkSync(target, link);
51
+ } catch (err) {
52
+ return {
53
+ agentId,
54
+ action: "conflict",
55
+ target,
56
+ link,
57
+ conflict: err instanceof Error ? err.message : String(err)
58
+ };
59
+ }
60
+ return { agentId, action: "created", target, link };
61
+ }
62
+ async function ensureAllAgentSymlinks(homeDir = os.homedir()) {
63
+ const employees = await loadEmployees();
64
+ return employees.map((emp) => ensureAgentSymlink(emp.name, homeDir));
65
+ }
66
+
67
+ export {
68
+ ensureAgentSymlink,
69
+ ensureAllAgentSymlinks
70
+ };