@ai-devkit/agent-manager 0.32.0 → 0.33.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (572) hide show
  1. package/dist/AgentManager.d.ts +13 -4
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +116 -84
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/AgentManager.test.js +623 -391
  6. package/dist/__tests__/AgentManager.test.js.map +1 -1
  7. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js +642 -642
  8. package/dist/__tests__/adapters/ClaudeCodeAdapter.test.js.map +1 -1
  9. package/dist/__tests__/adapters/CodexAdapter.test.js +322 -2321
  10. package/dist/__tests__/adapters/CodexAdapter.test.js.map +1 -1
  11. package/dist/__tests__/adapters/GrokCliAdapter.test.js +114 -114
  12. package/dist/__tests__/adapters/GrokCliAdapter.test.js.map +1 -1
  13. package/dist/__tests__/adapters/OpenCodeAdapter.test.js +170 -169
  14. package/dist/__tests__/adapters/OpenCodeAdapter.test.js.map +1 -1
  15. package/dist/__tests__/capacity/codex.test.js +93 -93
  16. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  17. package/dist/__tests__/capacity/index.test.js +17 -17
  18. package/dist/__tests__/capacity/index.test.js.map +1 -1
  19. package/dist/__tests__/durable/CodexCliProbe.test.js +60 -60
  20. package/dist/__tests__/durable/CodexCliProbe.test.js.map +1 -1
  21. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js +28 -28
  22. package/dist/__tests__/durable/CodexDurableAgentRepository.test.js.map +1 -1
  23. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js +30 -30
  24. package/dist/__tests__/durable/CodexPrintAgent.integration.test.js.map +1 -1
  25. package/dist/__tests__/durable/CodexPrintAgentService.test.js +71 -71
  26. package/dist/__tests__/durable/CodexPrintAgentService.test.js.map +1 -1
  27. package/dist/__tests__/durable/CodexPrintRunner.test.js +125 -125
  28. package/dist/__tests__/durable/CodexPrintRunner.test.js.map +1 -1
  29. package/dist/__tests__/print/ClaudeCliProbe.test.js +20 -20
  30. package/dist/__tests__/print/ClaudeCliProbe.test.js.map +1 -1
  31. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js +20 -20
  32. package/dist/__tests__/print/ClaudePrintAgent.integration.test.js.map +1 -1
  33. package/dist/__tests__/print/ClaudePrintAgentService.test.js +58 -58
  34. package/dist/__tests__/print/ClaudePrintAgentService.test.js.map +1 -1
  35. package/dist/__tests__/print/ClaudePrintRunner.test.js +55 -55
  36. package/dist/__tests__/print/ClaudePrintRunner.test.js.map +1 -1
  37. package/dist/__tests__/print/DurableAgent.test.js +9 -9
  38. package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
  39. package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js +40 -40
  40. package/dist/__tests__/print/DurableAgentRepository.sqlite.test.js.map +1 -1
  41. package/dist/__tests__/print/DurableAgentRepository.test.js +66 -66
  42. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
  43. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js +34 -34
  44. package/dist/__tests__/providers/claude/ClaudeAgentMapper.test.js.map +1 -1
  45. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js +132 -132
  46. package/dist/__tests__/providers/claude/ClaudeSessionLocator.test.js.map +1 -1
  47. package/dist/__tests__/providers/codex/CodexSessionLocator.test.js +194 -0
  48. package/dist/__tests__/providers/codex/CodexSessionLocator.test.js.map +1 -0
  49. package/dist/__tests__/providers/codex/CodexSessionMapping.test.js +86 -0
  50. package/dist/__tests__/providers/codex/CodexSessionMapping.test.js.map +1 -0
  51. package/dist/__tests__/providers/codex/CodexSessionParser.test.js +254 -0
  52. package/dist/__tests__/providers/codex/CodexSessionParser.test.js.map +1 -0
  53. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js +641 -0
  54. package/dist/__tests__/providers/copilot/CopilotAdapter.test.js.map +1 -0
  55. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js +1398 -0
  56. package/dist/__tests__/providers/gemini/GeminiCliAdapter.test.js.map +1 -0
  57. package/dist/__tests__/providers/pi/PiAdapter.test.js +337 -0
  58. package/dist/__tests__/providers/pi/PiAdapter.test.js.map +1 -0
  59. package/dist/__tests__/providers/pi/PiSessionParser.test.js +246 -0
  60. package/dist/__tests__/providers/pi/PiSessionParser.test.js.map +1 -0
  61. package/dist/__tests__/providers/pi/PiSessionTracker.test.js +108 -0
  62. package/dist/__tests__/providers/pi/PiSessionTracker.test.js.map +1 -0
  63. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js +33 -33
  64. package/dist/__tests__/providers/pi/durable/PiCliProbe.test.js.map +1 -1
  65. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js +16 -16
  66. package/dist/__tests__/providers/pi/durable/PiDurableAgentRepository.test.js.map +1 -1
  67. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js +52 -52
  68. package/dist/__tests__/providers/pi/durable/PiPrintAgentService.test.js.map +1 -1
  69. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js +44 -44
  70. package/dist/__tests__/providers/pi/durable/PiPrintProtocol.test.js.map +1 -1
  71. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js +126 -126
  72. package/dist/__tests__/providers/pi/durable/PiPrintRunner.test.js.map +1 -1
  73. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js +14 -14
  74. package/dist/__tests__/providers/pi/durable/PiStreamParser.test.js.map +1 -1
  75. package/dist/__tests__/readiness/AgentReadiness.test.js +134 -134
  76. package/dist/__tests__/readiness/AgentReadiness.test.js.map +1 -1
  77. package/dist/__tests__/runtime/AgentRuntime.test.js +13 -0
  78. package/dist/__tests__/runtime/AgentRuntime.test.js.map +1 -0
  79. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js +164 -0
  80. package/dist/__tests__/runtime/HerdrAgentDiscovery.test.js.map +1 -0
  81. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js +488 -0
  82. package/dist/__tests__/runtime/HerdrAgentRuntime.test.js.map +1 -0
  83. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js +588 -0
  84. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -0
  85. package/dist/__tests__/terminal/TerminalFocusManager.test.js +100 -100
  86. package/dist/__tests__/terminal/TerminalFocusManager.test.js.map +1 -1
  87. package/dist/__tests__/terminal/TmuxManager.test.js +101 -101
  88. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
  89. package/dist/__tests__/terminal/TtyWriter.test.js +185 -185
  90. package/dist/__tests__/terminal/TtyWriter.test.js.map +1 -1
  91. package/dist/__tests__/utils/AgentRegistry.test.js +343 -218
  92. package/dist/__tests__/utils/AgentRegistry.test.js.map +1 -1
  93. package/dist/__tests__/utils/ClaudeSessionParser.test.js +103 -101
  94. package/dist/__tests__/utils/ClaudeSessionParser.test.js.map +1 -1
  95. package/dist/__tests__/utils/agent-requests.test.js +43 -43
  96. package/dist/__tests__/utils/agent-requests.test.js.map +1 -1
  97. package/dist/__tests__/utils/agents.test.js +17 -17
  98. package/dist/__tests__/utils/agents.test.js.map +1 -1
  99. package/dist/__tests__/utils/matching.test.js +92 -92
  100. package/dist/__tests__/utils/matching.test.js.map +1 -1
  101. package/dist/__tests__/utils/process.test.js +108 -108
  102. package/dist/__tests__/utils/process.test.js.map +1 -1
  103. package/dist/__tests__/utils/session.test.js +53 -53
  104. package/dist/__tests__/utils/session.test.js.map +1 -1
  105. package/dist/adapters/AgentAdapter.d.ts +2 -2
  106. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  107. package/dist/adapters/AgentAdapter.js +1 -1
  108. package/dist/adapters/AgentAdapter.js.map +1 -1
  109. package/dist/adapters/GrokCliAdapter.d.ts +1 -1
  110. package/dist/adapters/GrokCliAdapter.d.ts.map +1 -1
  111. package/dist/adapters/GrokCliAdapter.js +80 -80
  112. package/dist/adapters/GrokCliAdapter.js.map +1 -1
  113. package/dist/adapters/OpenCodeAdapter.d.ts +2 -1
  114. package/dist/adapters/OpenCodeAdapter.d.ts.map +1 -1
  115. package/dist/adapters/OpenCodeAdapter.js +41 -37
  116. package/dist/adapters/OpenCodeAdapter.js.map +1 -1
  117. package/dist/adapters/index.d.ts +9 -9
  118. package/dist/adapters/index.d.ts.map +1 -1
  119. package/dist/adapters/index.js +8 -8
  120. package/dist/adapters/index.js.map +1 -1
  121. package/dist/capacity/codex.d.ts +3 -3
  122. package/dist/capacity/codex.d.ts.map +1 -1
  123. package/dist/capacity/codex.js +57 -57
  124. package/dist/capacity/codex.js.map +1 -1
  125. package/dist/capacity/index.d.ts +3 -3
  126. package/dist/capacity/index.d.ts.map +1 -1
  127. package/dist/capacity/index.js +8 -8
  128. package/dist/capacity/index.js.map +1 -1
  129. package/dist/capacity/types.d.ts +1 -1
  130. package/dist/capacity/types.js.map +1 -1
  131. package/dist/database/connection.d.ts +1 -1
  132. package/dist/database/connection.d.ts.map +1 -1
  133. package/dist/database/connection.js +15 -15
  134. package/dist/database/connection.js.map +1 -1
  135. package/dist/database/index.d.ts +3 -3
  136. package/dist/database/index.d.ts.map +1 -1
  137. package/dist/database/index.js +2 -2
  138. package/dist/database/index.js.map +1 -1
  139. package/dist/database/migrations/005_agent_runtime.sql +2 -0
  140. package/dist/database/schema.d.ts +1 -1
  141. package/dist/database/schema.d.ts.map +1 -1
  142. package/dist/database/schema.js +7 -7
  143. package/dist/database/schema.js.map +1 -1
  144. package/dist/durable/DurableAgent.d.ts +9 -9
  145. package/dist/durable/DurableAgent.d.ts.map +1 -1
  146. package/dist/durable/DurableAgent.js +16 -16
  147. package/dist/durable/DurableAgent.js.map +1 -1
  148. package/dist/durable/DurableAgentRepository.d.ts +4 -4
  149. package/dist/durable/DurableAgentRepository.d.ts.map +1 -1
  150. package/dist/durable/DurableAgentRepository.js +49 -48
  151. package/dist/durable/DurableAgentRepository.js.map +1 -1
  152. package/dist/durable/process.d.ts +1 -1
  153. package/dist/durable/process.d.ts.map +1 -1
  154. package/dist/durable/process.js +14 -14
  155. package/dist/durable/process.js.map +1 -1
  156. package/dist/durable/run.d.ts +2 -2
  157. package/dist/durable/run.d.ts.map +1 -1
  158. package/dist/durable/run.js +6 -6
  159. package/dist/durable/run.js.map +1 -1
  160. package/dist/durable/utils.d.ts +1 -1
  161. package/dist/durable/utils.d.ts.map +1 -1
  162. package/dist/durable/utils.js +4 -4
  163. package/dist/durable/utils.js.map +1 -1
  164. package/dist/index.d.ts +48 -43
  165. package/dist/index.d.ts.map +1 -1
  166. package/dist/index.js +28 -26
  167. package/dist/index.js.map +1 -1
  168. package/dist/providers/claude/ClaudeAgentMapper.d.ts +4 -4
  169. package/dist/providers/claude/ClaudeAgentMapper.d.ts.map +1 -1
  170. package/dist/providers/claude/ClaudeAgentMapper.js +10 -10
  171. package/dist/providers/claude/ClaudeAgentMapper.js.map +1 -1
  172. package/dist/providers/claude/ClaudeCodeAdapter.d.ts +1 -1
  173. package/dist/providers/claude/ClaudeCodeAdapter.d.ts.map +1 -1
  174. package/dist/providers/claude/ClaudeCodeAdapter.js +14 -14
  175. package/dist/providers/claude/ClaudeCodeAdapter.js.map +1 -1
  176. package/dist/providers/claude/ClaudeSessionLocator.d.ts +4 -4
  177. package/dist/providers/claude/ClaudeSessionLocator.d.ts.map +1 -1
  178. package/dist/providers/claude/ClaudeSessionLocator.js +79 -79
  179. package/dist/providers/claude/ClaudeSessionLocator.js.map +1 -1
  180. package/dist/providers/claude/ClaudeSessionParser.d.ts +2 -2
  181. package/dist/providers/claude/ClaudeSessionParser.d.ts.map +1 -1
  182. package/dist/providers/claude/ClaudeSessionParser.js +99 -99
  183. package/dist/providers/claude/ClaudeSessionParser.js.map +1 -1
  184. package/dist/providers/claude/durable/ClaudeCliProbe.d.ts.map +1 -1
  185. package/dist/providers/claude/durable/ClaudeCliProbe.js +16 -16
  186. package/dist/providers/claude/durable/ClaudeCliProbe.js.map +1 -1
  187. package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts +5 -5
  188. package/dist/providers/claude/durable/ClaudePrintAgentService.d.ts.map +1 -1
  189. package/dist/providers/claude/durable/ClaudePrintAgentService.js +10 -10
  190. package/dist/providers/claude/durable/ClaudePrintAgentService.js.map +1 -1
  191. package/dist/providers/claude/durable/ClaudePrintRunner.d.ts +4 -4
  192. package/dist/providers/claude/durable/ClaudePrintRunner.d.ts.map +1 -1
  193. package/dist/providers/claude/durable/ClaudePrintRunner.js +18 -18
  194. package/dist/providers/claude/durable/ClaudePrintRunner.js.map +1 -1
  195. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts +2 -2
  196. package/dist/providers/claude/durable/ClaudeStreamParser.d.ts.map +1 -1
  197. package/dist/providers/claude/durable/ClaudeStreamParser.js +17 -17
  198. package/dist/providers/claude/durable/ClaudeStreamParser.js.map +1 -1
  199. package/dist/providers/codex/CodexAdapter.d.ts +27 -0
  200. package/dist/providers/codex/CodexAdapter.d.ts.map +1 -0
  201. package/dist/providers/codex/CodexAdapter.js +162 -0
  202. package/dist/providers/codex/CodexAdapter.js.map +1 -0
  203. package/dist/providers/codex/CodexAgentMapper.d.ts +9 -0
  204. package/dist/providers/codex/CodexAgentMapper.d.ts.map +1 -0
  205. package/dist/providers/codex/CodexAgentMapper.js +35 -0
  206. package/dist/providers/codex/CodexAgentMapper.js.map +1 -0
  207. package/dist/providers/codex/CodexSessionLocator.d.ts +44 -0
  208. package/dist/providers/codex/CodexSessionLocator.d.ts.map +1 -0
  209. package/dist/providers/codex/CodexSessionLocator.js +207 -0
  210. package/dist/providers/codex/CodexSessionLocator.js.map +1 -0
  211. package/dist/providers/codex/CodexSessionMapping.d.ts +23 -0
  212. package/dist/providers/codex/CodexSessionMapping.d.ts.map +1 -0
  213. package/dist/providers/codex/CodexSessionMapping.js +68 -0
  214. package/dist/providers/codex/CodexSessionMapping.js.map +1 -0
  215. package/dist/providers/codex/CodexSessionParser.d.ts +58 -0
  216. package/dist/providers/codex/CodexSessionParser.d.ts.map +1 -0
  217. package/dist/providers/codex/CodexSessionParser.js +262 -0
  218. package/dist/providers/codex/CodexSessionParser.js.map +1 -0
  219. package/dist/providers/codex/durable/CodexCliProbe.d.ts.map +1 -1
  220. package/dist/providers/codex/durable/CodexCliProbe.js +22 -22
  221. package/dist/providers/codex/durable/CodexCliProbe.js.map +1 -1
  222. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts +6 -6
  223. package/dist/providers/codex/durable/CodexPrintAgentService.d.ts.map +1 -1
  224. package/dist/providers/codex/durable/CodexPrintAgentService.js +10 -10
  225. package/dist/providers/codex/durable/CodexPrintAgentService.js.map +1 -1
  226. package/dist/providers/codex/durable/CodexPrintRunner.d.ts +4 -4
  227. package/dist/providers/codex/durable/CodexPrintRunner.d.ts.map +1 -1
  228. package/dist/providers/codex/durable/CodexPrintRunner.js +20 -20
  229. package/dist/providers/codex/durable/CodexPrintRunner.js.map +1 -1
  230. package/dist/providers/codex/durable/CodexStreamParser.d.ts +2 -2
  231. package/dist/providers/codex/durable/CodexStreamParser.d.ts.map +1 -1
  232. package/dist/providers/codex/durable/CodexStreamParser.js +21 -21
  233. package/dist/providers/codex/durable/CodexStreamParser.js.map +1 -1
  234. package/dist/{adapters → providers/copilot}/CopilotAdapter.d.ts +11 -25
  235. package/dist/providers/copilot/CopilotAdapter.d.ts.map +1 -0
  236. package/dist/providers/copilot/CopilotAdapter.js +108 -0
  237. package/dist/providers/copilot/CopilotAdapter.js.map +1 -0
  238. package/dist/providers/copilot/CopilotAgentMapper.d.ts +9 -0
  239. package/dist/providers/copilot/CopilotAgentMapper.d.ts.map +1 -0
  240. package/dist/providers/copilot/CopilotAgentMapper.js +37 -0
  241. package/dist/providers/copilot/CopilotAgentMapper.js.map +1 -0
  242. package/dist/providers/copilot/CopilotSessionLocator.d.ts +19 -0
  243. package/dist/providers/copilot/CopilotSessionLocator.d.ts.map +1 -0
  244. package/dist/providers/copilot/CopilotSessionLocator.js +41 -0
  245. package/dist/providers/copilot/CopilotSessionLocator.js.map +1 -0
  246. package/dist/providers/copilot/CopilotSessionParser.d.ts +30 -0
  247. package/dist/providers/copilot/CopilotSessionParser.d.ts.map +1 -0
  248. package/dist/providers/copilot/CopilotSessionParser.js +181 -0
  249. package/dist/providers/copilot/CopilotSessionParser.js.map +1 -0
  250. package/dist/providers/gemini/GeminiAgentMapper.d.ts +9 -0
  251. package/dist/providers/gemini/GeminiAgentMapper.d.ts.map +1 -0
  252. package/dist/providers/gemini/GeminiAgentMapper.js +37 -0
  253. package/dist/providers/gemini/GeminiAgentMapper.js.map +1 -0
  254. package/dist/providers/gemini/GeminiCliAdapter.d.ts +52 -0
  255. package/dist/providers/gemini/GeminiCliAdapter.d.ts.map +1 -0
  256. package/dist/providers/gemini/GeminiCliAdapter.js +188 -0
  257. package/dist/providers/gemini/GeminiCliAdapter.js.map +1 -0
  258. package/dist/providers/gemini/GeminiSessionLocator.d.ts +30 -0
  259. package/dist/providers/gemini/GeminiSessionLocator.d.ts.map +1 -0
  260. package/dist/providers/gemini/GeminiSessionLocator.js +142 -0
  261. package/dist/providers/gemini/GeminiSessionLocator.js.map +1 -0
  262. package/dist/providers/gemini/GeminiSessionParser.d.ts +94 -0
  263. package/dist/providers/gemini/GeminiSessionParser.d.ts.map +1 -0
  264. package/dist/providers/gemini/GeminiSessionParser.js +166 -0
  265. package/dist/providers/gemini/GeminiSessionParser.js.map +1 -0
  266. package/dist/providers/pi/PiAdapter.d.ts +28 -0
  267. package/dist/providers/pi/PiAdapter.d.ts.map +1 -0
  268. package/dist/providers/pi/PiAdapter.js +153 -0
  269. package/dist/providers/pi/PiAdapter.js.map +1 -0
  270. package/dist/providers/pi/PiAgentMapper.d.ts +9 -0
  271. package/dist/providers/pi/PiAgentMapper.d.ts.map +1 -0
  272. package/dist/providers/pi/PiAgentMapper.js +37 -0
  273. package/dist/providers/pi/PiAgentMapper.js.map +1 -0
  274. package/dist/providers/pi/PiSessionLocator.d.ts +23 -0
  275. package/dist/providers/pi/PiSessionLocator.d.ts.map +1 -0
  276. package/dist/providers/pi/PiSessionLocator.js +79 -0
  277. package/dist/providers/pi/PiSessionLocator.js.map +1 -0
  278. package/dist/providers/pi/PiSessionParser.d.ts +36 -0
  279. package/dist/providers/pi/PiSessionParser.d.ts.map +1 -0
  280. package/dist/providers/pi/PiSessionParser.js +191 -0
  281. package/dist/providers/pi/PiSessionParser.js.map +1 -0
  282. package/dist/providers/pi/PiSessionTracker.d.ts +23 -0
  283. package/dist/providers/pi/PiSessionTracker.d.ts.map +1 -0
  284. package/dist/providers/pi/PiSessionTracker.js +69 -0
  285. package/dist/providers/pi/PiSessionTracker.js.map +1 -0
  286. package/dist/providers/pi/durable/PiCliProbe.d.ts.map +1 -1
  287. package/dist/providers/pi/durable/PiCliProbe.js +15 -15
  288. package/dist/providers/pi/durable/PiCliProbe.js.map +1 -1
  289. package/dist/providers/pi/durable/PiPrintAgentService.d.ts +6 -6
  290. package/dist/providers/pi/durable/PiPrintAgentService.d.ts.map +1 -1
  291. package/dist/providers/pi/durable/PiPrintAgentService.js +10 -10
  292. package/dist/providers/pi/durable/PiPrintAgentService.js.map +1 -1
  293. package/dist/providers/pi/durable/PiPrintProtocol.d.ts.map +1 -1
  294. package/dist/providers/pi/durable/PiPrintProtocol.js +18 -18
  295. package/dist/providers/pi/durable/PiPrintProtocol.js.map +1 -1
  296. package/dist/providers/pi/durable/PiPrintRunner.d.ts +4 -4
  297. package/dist/providers/pi/durable/PiPrintRunner.d.ts.map +1 -1
  298. package/dist/providers/pi/durable/PiPrintRunner.js +15 -15
  299. package/dist/providers/pi/durable/PiPrintRunner.js.map +1 -1
  300. package/dist/providers/pi/durable/PiStreamParser.d.ts +1 -1
  301. package/dist/providers/pi/durable/PiStreamParser.d.ts.map +1 -1
  302. package/dist/providers/pi/durable/PiStreamParser.js +16 -16
  303. package/dist/providers/pi/durable/PiStreamParser.js.map +1 -1
  304. package/dist/readiness/AgentReadiness.d.ts +6 -6
  305. package/dist/readiness/AgentReadiness.d.ts.map +1 -1
  306. package/dist/readiness/AgentReadiness.js +130 -130
  307. package/dist/readiness/AgentReadiness.js.map +1 -1
  308. package/dist/runtime/ManagedAgentRuntime.d.ts +69 -0
  309. package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -0
  310. package/dist/runtime/ManagedAgentRuntime.js +156 -0
  311. package/dist/runtime/ManagedAgentRuntime.js.map +1 -0
  312. package/dist/runtime/RuntimeFactory.d.ts +4 -0
  313. package/dist/runtime/RuntimeFactory.d.ts.map +1 -0
  314. package/dist/runtime/RuntimeFactory.js +11 -0
  315. package/dist/runtime/RuntimeFactory.js.map +1 -0
  316. package/dist/runtime/errors.d.ts +27 -0
  317. package/dist/runtime/errors.d.ts.map +1 -0
  318. package/dist/runtime/errors.js +42 -0
  319. package/dist/runtime/errors.js.map +1 -0
  320. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts +18 -0
  321. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -0
  322. package/dist/runtime/herdr/HerdrAgentDiscovery.js +183 -0
  323. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -0
  324. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts +36 -0
  325. package/dist/runtime/herdr/HerdrAgentRuntime.d.ts.map +1 -0
  326. package/dist/runtime/herdr/HerdrAgentRuntime.js +202 -0
  327. package/dist/runtime/herdr/HerdrAgentRuntime.js.map +1 -0
  328. package/dist/runtime/herdr/HerdrCliClient.d.ts +13 -0
  329. package/dist/runtime/herdr/HerdrCliClient.d.ts.map +1 -0
  330. package/dist/runtime/herdr/HerdrCliClient.js +60 -0
  331. package/dist/runtime/herdr/HerdrCliClient.js.map +1 -0
  332. package/dist/runtime/herdr/HerdrErrors.d.ts +7 -0
  333. package/dist/runtime/herdr/HerdrErrors.d.ts.map +1 -0
  334. package/dist/runtime/herdr/HerdrErrors.js +10 -0
  335. package/dist/runtime/herdr/HerdrErrors.js.map +1 -0
  336. package/dist/runtime/herdr/HerdrResponseParsers.d.ts +16 -0
  337. package/dist/runtime/herdr/HerdrResponseParsers.d.ts.map +1 -0
  338. package/dist/runtime/herdr/HerdrResponseParsers.js +107 -0
  339. package/dist/runtime/herdr/HerdrResponseParsers.js.map +1 -0
  340. package/dist/runtime/herdr/HerdrRuntimeRef.d.ts +9 -0
  341. package/dist/runtime/herdr/HerdrRuntimeRef.d.ts.map +1 -0
  342. package/dist/runtime/herdr/HerdrRuntimeRef.js +33 -0
  343. package/dist/runtime/herdr/HerdrRuntimeRef.js.map +1 -0
  344. package/dist/runtime/tmux/PidPolling.d.ts +5 -0
  345. package/dist/runtime/tmux/PidPolling.d.ts.map +1 -0
  346. package/dist/runtime/tmux/PidPolling.js +24 -0
  347. package/dist/runtime/tmux/PidPolling.js.map +1 -0
  348. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +17 -0
  349. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -0
  350. package/dist/runtime/tmux/TmuxAgentRuntime.js +80 -0
  351. package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -0
  352. package/dist/runtime/tmux/TmuxRuntimeRef.d.ts +5 -0
  353. package/dist/runtime/tmux/TmuxRuntimeRef.d.ts.map +1 -0
  354. package/dist/runtime/tmux/TmuxRuntimeRef.js +9 -0
  355. package/dist/runtime/tmux/TmuxRuntimeRef.js.map +1 -0
  356. package/dist/runtime/types.d.ts +54 -0
  357. package/dist/runtime/types.d.ts.map +1 -0
  358. package/dist/runtime/types.js +3 -0
  359. package/dist/runtime/types.js.map +1 -0
  360. package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
  361. package/dist/terminal/TerminalFocusManager.js +50 -50
  362. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  363. package/dist/terminal/TmuxManager.d.ts.map +1 -1
  364. package/dist/terminal/TmuxManager.js +47 -47
  365. package/dist/terminal/TmuxManager.js.map +1 -1
  366. package/dist/terminal/TtyWriter.d.ts +1 -1
  367. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  368. package/dist/terminal/TtyWriter.js +89 -89
  369. package/dist/terminal/TtyWriter.js.map +1 -1
  370. package/dist/terminal/index.d.ts +4 -4
  371. package/dist/terminal/index.js +3 -3
  372. package/dist/terminal/index.js.map +1 -1
  373. package/dist/utils/AgentRegistry.d.ts +10 -2
  374. package/dist/utils/AgentRegistry.d.ts.map +1 -1
  375. package/dist/utils/AgentRegistry.js +59 -26
  376. package/dist/utils/AgentRegistry.js.map +1 -1
  377. package/dist/utils/agent-requests.d.ts.map +1 -1
  378. package/dist/utils/agent-requests.js +5 -5
  379. package/dist/utils/agent-requests.js.map +1 -1
  380. package/dist/utils/agents.d.ts +2 -2
  381. package/dist/utils/agents.d.ts.map +1 -1
  382. package/dist/utils/agents.js +15 -15
  383. package/dist/utils/agents.js.map +1 -1
  384. package/dist/utils/applescript.js +1 -1
  385. package/dist/utils/applescript.js.map +1 -1
  386. package/dist/utils/index.d.ts +6 -6
  387. package/dist/utils/index.d.ts.map +1 -1
  388. package/dist/utils/index.js +4 -4
  389. package/dist/utils/index.js.map +1 -1
  390. package/dist/utils/matching.d.ts +2 -2
  391. package/dist/utils/matching.d.ts.map +1 -1
  392. package/dist/utils/matching.js +4 -4
  393. package/dist/utils/matching.js.map +1 -1
  394. package/dist/utils/process.d.ts +1 -1
  395. package/dist/utils/process.d.ts.map +1 -1
  396. package/dist/utils/process.js +74 -74
  397. package/dist/utils/process.js.map +1 -1
  398. package/dist/utils/session.d.ts +1 -1
  399. package/dist/utils/session.d.ts.map +1 -1
  400. package/dist/utils/session.js +7 -7
  401. package/dist/utils/session.js.map +1 -1
  402. package/dist/utils/sortAgents.d.ts +2 -2
  403. package/dist/utils/sortAgents.d.ts.map +1 -1
  404. package/dist/utils/sortAgents.js +5 -5
  405. package/dist/utils/sortAgents.js.map +1 -1
  406. package/package.json +18 -19
  407. package/src/AgentManager.ts +338 -274
  408. package/src/__tests__/AgentManager.test.ts +1143 -907
  409. package/src/__tests__/adapters/ClaudeCodeAdapter.test.ts +1657 -1368
  410. package/src/__tests__/adapters/CodexAdapter.test.ts +365 -1698
  411. package/src/__tests__/adapters/GrokCliAdapter.test.ts +325 -272
  412. package/src/__tests__/adapters/OpenCodeAdapter.test.ts +511 -455
  413. package/src/__tests__/capacity/codex.test.ts +178 -91
  414. package/src/__tests__/capacity/index.test.ts +27 -18
  415. package/src/__tests__/durable/CodexCliProbe.test.ts +75 -53
  416. package/src/__tests__/durable/CodexDurableAgentRepository.test.ts +51 -40
  417. package/src/__tests__/durable/CodexPrintAgent.integration.test.ts +89 -64
  418. package/src/__tests__/durable/CodexPrintAgentService.test.ts +196 -118
  419. package/src/__tests__/durable/CodexPrintRunner.test.ts +282 -162
  420. package/src/__tests__/fixtures/fake-claude.cjs +20 -13
  421. package/src/__tests__/fixtures/fake-codex.cjs +41 -30
  422. package/src/__tests__/print/ClaudeCliProbe.test.ts +27 -21
  423. package/src/__tests__/print/ClaudePrintAgent.integration.test.ts +48 -40
  424. package/src/__tests__/print/ClaudePrintAgentService.test.ts +118 -67
  425. package/src/__tests__/print/ClaudePrintRunner.test.ts +136 -83
  426. package/src/__tests__/print/DurableAgent.test.ts +19 -16
  427. package/src/__tests__/print/DurableAgentRepository.sqlite.test.ts +109 -86
  428. package/src/__tests__/print/DurableAgentRepository.test.ts +170 -137
  429. package/src/__tests__/providers/claude/ClaudeAgentMapper.test.ts +65 -65
  430. package/src/__tests__/providers/claude/ClaudeSessionLocator.test.ts +400 -377
  431. package/src/__tests__/providers/codex/CodexSessionLocator.test.ts +182 -0
  432. package/src/__tests__/providers/codex/CodexSessionMapping.test.ts +63 -0
  433. package/src/__tests__/providers/codex/CodexSessionParser.test.ts +192 -0
  434. package/src/__tests__/providers/copilot/CopilotAdapter.test.ts +602 -0
  435. package/src/__tests__/providers/gemini/GeminiCliAdapter.test.ts +1299 -0
  436. package/src/__tests__/providers/pi/PiAdapter.test.ts +310 -0
  437. package/src/__tests__/providers/pi/PiSessionParser.test.ts +190 -0
  438. package/src/__tests__/providers/pi/PiSessionTracker.test.ts +79 -0
  439. package/src/__tests__/providers/pi/durable/PiCliProbe.test.ts +48 -32
  440. package/src/__tests__/providers/pi/durable/PiDurableAgentRepository.test.ts +24 -23
  441. package/src/__tests__/providers/pi/durable/PiPrintAgentService.test.ts +131 -59
  442. package/src/__tests__/providers/pi/durable/PiPrintProtocol.test.ts +66 -28
  443. package/src/__tests__/providers/pi/durable/PiPrintRunner.test.ts +236 -90
  444. package/src/__tests__/providers/pi/durable/PiStreamParser.test.ts +20 -18
  445. package/src/__tests__/readiness/AgentReadiness.test.ts +217 -169
  446. package/src/__tests__/runtime/AgentRuntime.test.ts +8 -0
  447. package/src/__tests__/runtime/HerdrAgentDiscovery.test.ts +131 -0
  448. package/src/__tests__/runtime/HerdrAgentRuntime.test.ts +377 -0
  449. package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +555 -0
  450. package/src/__tests__/terminal/TerminalFocusManager.test.ts +254 -253
  451. package/src/__tests__/terminal/TmuxManager.test.ts +157 -157
  452. package/src/__tests__/terminal/TtyWriter.test.ts +423 -419
  453. package/src/__tests__/utils/AgentRegistry.test.ts +516 -402
  454. package/src/__tests__/utils/ClaudeSessionParser.test.ts +276 -242
  455. package/src/__tests__/utils/agent-requests.test.ts +76 -56
  456. package/src/__tests__/utils/agents.test.ts +20 -20
  457. package/src/__tests__/utils/matching.test.ts +206 -171
  458. package/src/__tests__/utils/process.test.ts +276 -262
  459. package/src/__tests__/utils/session.test.ts +133 -139
  460. package/src/adapters/AgentAdapter.ts +134 -126
  461. package/src/adapters/GrokCliAdapter.ts +353 -342
  462. package/src/adapters/OpenCodeAdapter.ts +289 -259
  463. package/src/adapters/index.ts +15 -9
  464. package/src/capacity/codex.ts +137 -84
  465. package/src/capacity/index.ts +18 -13
  466. package/src/capacity/types.ts +1 -1
  467. package/src/database/connection.ts +73 -68
  468. package/src/database/index.ts +6 -6
  469. package/src/database/migrations/005_agent_runtime.sql +2 -0
  470. package/src/database/schema.ts +40 -38
  471. package/src/durable/DurableAgent.ts +85 -85
  472. package/src/durable/DurableAgentRepository.ts +409 -260
  473. package/src/durable/process.ts +22 -21
  474. package/src/durable/run.ts +61 -51
  475. package/src/durable/utils.ts +46 -41
  476. package/src/index.ts +151 -103
  477. package/src/providers/claude/ClaudeAgentMapper.ts +51 -50
  478. package/src/providers/claude/ClaudeCodeAdapter.ts +144 -136
  479. package/src/providers/claude/ClaudeSessionLocator.ts +291 -293
  480. package/src/providers/claude/ClaudeSessionParser.ts +322 -338
  481. package/src/providers/claude/durable/ClaudeCliProbe.ts +42 -37
  482. package/src/providers/claude/durable/ClaudePrintAgentService.ts +63 -50
  483. package/src/providers/claude/durable/ClaudePrintRunner.ts +79 -69
  484. package/src/providers/claude/durable/ClaudeStreamParser.ts +91 -62
  485. package/src/providers/codex/CodexAdapter.ts +205 -0
  486. package/src/providers/codex/CodexAgentMapper.ts +35 -0
  487. package/src/providers/codex/CodexSessionLocator.ts +279 -0
  488. package/src/providers/codex/CodexSessionMapping.ts +89 -0
  489. package/src/providers/codex/CodexSessionParser.ts +385 -0
  490. package/src/providers/codex/durable/CodexCliProbe.ts +50 -42
  491. package/src/providers/codex/durable/CodexPrintAgentService.ts +63 -49
  492. package/src/providers/codex/durable/CodexPrintRunner.ts +79 -69
  493. package/src/providers/codex/durable/CodexStreamParser.ts +97 -84
  494. package/src/providers/copilot/CopilotAdapter.ts +145 -0
  495. package/src/providers/copilot/CopilotAgentMapper.ts +36 -0
  496. package/src/providers/copilot/CopilotSessionLocator.ts +57 -0
  497. package/src/providers/copilot/CopilotSessionParser.ts +289 -0
  498. package/src/providers/gemini/GeminiAgentMapper.ts +36 -0
  499. package/src/providers/gemini/GeminiCliAdapter.ts +266 -0
  500. package/src/providers/gemini/GeminiSessionLocator.ts +183 -0
  501. package/src/providers/gemini/GeminiSessionParser.ts +271 -0
  502. package/src/providers/pi/PiAdapter.ts +183 -0
  503. package/src/providers/pi/PiAgentMapper.ts +36 -0
  504. package/src/providers/pi/PiSessionLocator.ts +95 -0
  505. package/src/providers/pi/PiSessionParser.ts +284 -0
  506. package/src/providers/pi/PiSessionTracker.ts +87 -0
  507. package/src/providers/pi/durable/PiCliProbe.ts +42 -34
  508. package/src/providers/pi/durable/PiPrintAgentService.ts +61 -53
  509. package/src/providers/pi/durable/PiPrintProtocol.ts +31 -29
  510. package/src/providers/pi/durable/PiPrintRunner.ts +75 -68
  511. package/src/providers/pi/durable/PiStreamParser.ts +82 -72
  512. package/src/readiness/AgentReadiness.ts +585 -513
  513. package/src/runtime/ManagedAgentRuntime.ts +266 -0
  514. package/src/runtime/RuntimeFactory.ts +13 -0
  515. package/src/runtime/errors.ts +50 -0
  516. package/src/runtime/herdr/HerdrAgentDiscovery.ts +178 -0
  517. package/src/runtime/herdr/HerdrAgentRuntime.ts +216 -0
  518. package/src/runtime/herdr/HerdrCliClient.ts +78 -0
  519. package/src/runtime/herdr/HerdrErrors.ts +9 -0
  520. package/src/runtime/herdr/HerdrResponseParsers.ts +86 -0
  521. package/src/runtime/herdr/HerdrRuntimeRef.ts +41 -0
  522. package/src/runtime/tmux/PidPolling.ts +34 -0
  523. package/src/runtime/tmux/TmuxAgentRuntime.ts +113 -0
  524. package/src/runtime/tmux/TmuxRuntimeRef.ts +9 -0
  525. package/src/runtime/types.ts +56 -0
  526. package/src/terminal/TerminalFocusManager.ts +215 -212
  527. package/src/terminal/TmuxManager.ts +99 -92
  528. package/src/terminal/TtyWriter.ts +203 -194
  529. package/src/terminal/index.ts +4 -4
  530. package/src/utils/AgentRegistry.ts +301 -238
  531. package/src/utils/agent-requests.ts +16 -16
  532. package/src/utils/agents.ts +41 -38
  533. package/src/utils/applescript.ts +4 -4
  534. package/src/utils/index.ts +12 -6
  535. package/src/utils/matching.ts +43 -43
  536. package/src/utils/process.ts +261 -258
  537. package/src/utils/session.ts +66 -66
  538. package/src/utils/sortAgents.ts +35 -33
  539. package/tsconfig.json +2 -8
  540. package/vitest.config.ts +16 -16
  541. package/.eslintrc.json +0 -31
  542. package/dist/__tests__/adapters/CopilotAdapter.test.js +0 -630
  543. package/dist/__tests__/adapters/CopilotAdapter.test.js.map +0 -1
  544. package/dist/__tests__/adapters/GeminiCliAdapter.test.js +0 -1384
  545. package/dist/__tests__/adapters/GeminiCliAdapter.test.js.map +0 -1
  546. package/dist/__tests__/adapters/PiAdapter.test.js +0 -618
  547. package/dist/__tests__/adapters/PiAdapter.test.js.map +0 -1
  548. package/dist/__tests__/database/DurableAgentsDatabase.test.js +0 -128
  549. package/dist/__tests__/database/DurableAgentsDatabase.test.js.map +0 -1
  550. package/dist/adapters/CodexAdapter.d.ts +0 -109
  551. package/dist/adapters/CodexAdapter.d.ts.map +0 -1
  552. package/dist/adapters/CodexAdapter.js +0 -710
  553. package/dist/adapters/CodexAdapter.js.map +0 -1
  554. package/dist/adapters/CopilotAdapter.d.ts.map +0 -1
  555. package/dist/adapters/CopilotAdapter.js +0 -329
  556. package/dist/adapters/CopilotAdapter.js.map +0 -1
  557. package/dist/adapters/GeminiCliAdapter.d.ts +0 -98
  558. package/dist/adapters/GeminiCliAdapter.d.ts.map +0 -1
  559. package/dist/adapters/GeminiCliAdapter.js +0 -513
  560. package/dist/adapters/GeminiCliAdapter.js.map +0 -1
  561. package/dist/adapters/PiAdapter.d.ts +0 -63
  562. package/dist/adapters/PiAdapter.d.ts.map +0 -1
  563. package/dist/adapters/PiAdapter.js +0 -453
  564. package/dist/adapters/PiAdapter.js.map +0 -1
  565. package/src/__tests__/adapters/CopilotAdapter.test.ts +0 -510
  566. package/src/__tests__/adapters/GeminiCliAdapter.test.ts +0 -1259
  567. package/src/__tests__/adapters/PiAdapter.test.ts +0 -458
  568. package/src/__tests__/database/DurableAgentsDatabase.test.ts +0 -102
  569. package/src/adapters/CodexAdapter.ts +0 -923
  570. package/src/adapters/CopilotAdapter.ts +0 -473
  571. package/src/adapters/GeminiCliAdapter.ts +0 -701
  572. package/src/adapters/PiAdapter.ts +0 -598
@@ -1,11 +1,11 @@
1
1
  /**
2
2
  * Tests for AgentManager
3
- */ import fs from 'fs';
4
- import os from 'os';
5
- import path from 'path';
6
- import { AgentManager } from '../AgentManager.js';
7
- import { AgentStatus } from '../adapters/AgentAdapter.js';
8
- import { AgentRegistry } from '../utils/AgentRegistry.js';
3
+ */ import fs from "fs";
4
+ import os from "os";
5
+ import path from "path";
6
+ import { AgentManager } from "../AgentManager.js";
7
+ import { AgentStatus } from "../adapters/AgentAdapter.js";
8
+ import { AgentRegistry } from "../utils/AgentRegistry.js";
9
9
  // Mock adapter for testing
10
10
  let MockAdapter = class MockAdapter {
11
11
  type;
@@ -56,23 +56,23 @@ let MockAdapter = class MockAdapter {
56
56
  // Helper to create mock agent
57
57
  function createMockAgent(overrides = {}) {
58
58
  return {
59
- name: 'test-agent',
60
- type: 'claude',
59
+ name: "test-agent",
60
+ type: "claude",
61
61
  status: AgentStatus.RUNNING,
62
- summary: 'Test summary',
62
+ summary: "Test summary",
63
63
  pid: 12345,
64
- projectPath: '/test/path',
65
- sessionId: 'test-session-id',
64
+ projectPath: "/test/path",
65
+ sessionId: "test-session-id",
66
66
  lastActive: new Date(),
67
67
  ...overrides
68
68
  };
69
69
  }
70
- describe('AgentManager', ()=>{
70
+ describe("AgentManager", ()=>{
71
71
  let manager;
72
72
  let tmpDir;
73
73
  beforeEach(()=>{
74
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));
75
- manager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'agents.json')));
74
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-manager-"));
75
+ manager = new AgentManager(new AgentRegistry(path.join(tmpDir, "agents.json")));
76
76
  });
77
77
  afterEach(()=>{
78
78
  fs.rmSync(tmpDir, {
@@ -80,51 +80,51 @@ describe('AgentManager', ()=>{
80
80
  force: true
81
81
  });
82
82
  });
83
- describe('registerAdapter', ()=>{
84
- it('should register a new adapter', ()=>{
85
- const adapter = new MockAdapter('claude');
83
+ describe("registerAdapter", ()=>{
84
+ it("should register a new adapter", ()=>{
85
+ const adapter = new MockAdapter("claude");
86
86
  manager.registerAdapter(adapter);
87
- expect(manager.hasAdapter('claude')).toBe(true);
87
+ expect(manager.hasAdapter("claude")).toBe(true);
88
88
  expect(manager.getAdapterCount()).toBe(1);
89
89
  });
90
- it('should throw error when registering duplicate adapter type', ()=>{
91
- const adapter1 = new MockAdapter('claude');
92
- const adapter2 = new MockAdapter('claude');
90
+ it("should throw error when registering duplicate adapter type", ()=>{
91
+ const adapter1 = new MockAdapter("claude");
92
+ const adapter2 = new MockAdapter("claude");
93
93
  manager.registerAdapter(adapter1);
94
94
  expect(()=>manager.registerAdapter(adapter2)).toThrow('Adapter for type "claude" is already registered');
95
95
  });
96
- it('should allow registering multiple different adapter types', ()=>{
97
- const adapter1 = new MockAdapter('claude');
98
- const adapter2 = new MockAdapter('gemini_cli');
96
+ it("should allow registering multiple different adapter types", ()=>{
97
+ const adapter1 = new MockAdapter("claude");
98
+ const adapter2 = new MockAdapter("gemini_cli");
99
99
  manager.registerAdapter(adapter1);
100
100
  manager.registerAdapter(adapter2);
101
101
  expect(manager.getAdapterCount()).toBe(2);
102
- expect(manager.hasAdapter('claude')).toBe(true);
103
- expect(manager.hasAdapter('gemini_cli')).toBe(true);
102
+ expect(manager.hasAdapter("claude")).toBe(true);
103
+ expect(manager.hasAdapter("gemini_cli")).toBe(true);
104
104
  });
105
105
  });
106
- describe('unregisterAdapter', ()=>{
107
- it('should unregister an existing adapter', ()=>{
108
- const adapter = new MockAdapter('claude');
106
+ describe("unregisterAdapter", ()=>{
107
+ it("should unregister an existing adapter", ()=>{
108
+ const adapter = new MockAdapter("claude");
109
109
  manager.registerAdapter(adapter);
110
- const removed = manager.unregisterAdapter('claude');
110
+ const removed = manager.unregisterAdapter("claude");
111
111
  expect(removed).toBe(true);
112
- expect(manager.hasAdapter('claude')).toBe(false);
112
+ expect(manager.hasAdapter("claude")).toBe(false);
113
113
  expect(manager.getAdapterCount()).toBe(0);
114
114
  });
115
- it('should return false when unregistering non-existent adapter', ()=>{
116
- const removed = manager.unregisterAdapter('NonExistent');
115
+ it("should return false when unregistering non-existent adapter", ()=>{
116
+ const removed = manager.unregisterAdapter("NonExistent");
117
117
  expect(removed).toBe(false);
118
118
  });
119
119
  });
120
- describe('getAdapters', ()=>{
121
- it('should return empty array when no adapters registered', ()=>{
120
+ describe("getAdapters", ()=>{
121
+ it("should return empty array when no adapters registered", ()=>{
122
122
  const adapters = manager.getAdapters();
123
123
  expect(adapters).toEqual([]);
124
124
  });
125
- it('should return all registered adapters', ()=>{
126
- const adapter1 = new MockAdapter('claude');
127
- const adapter2 = new MockAdapter('gemini_cli');
125
+ it("should return all registered adapters", ()=>{
126
+ const adapter1 = new MockAdapter("claude");
127
+ const adapter2 = new MockAdapter("gemini_cli");
128
128
  manager.registerAdapter(adapter1);
129
129
  manager.registerAdapter(adapter2);
130
130
  const adapters = manager.getAdapters();
@@ -133,20 +133,20 @@ describe('AgentManager', ()=>{
133
133
  expect(adapters).toContain(adapter2);
134
134
  });
135
135
  });
136
- describe('listAgents', ()=>{
137
- it('shares one process capture while giving each adapter only its declared executables', async ()=>{
136
+ describe("listAgents", ()=>{
137
+ it("shares one process capture while giving each adapter only its declared executables", async ()=>{
138
138
  const processes = [
139
139
  {
140
140
  pid: 101,
141
- command: 'claude',
142
- cwd: '/claude',
143
- tty: 's001'
141
+ command: "claude",
142
+ cwd: "/claude",
143
+ tty: "s001"
144
144
  },
145
145
  {
146
146
  pid: 202,
147
- command: 'node /bin/pi',
148
- cwd: '/pi',
149
- tty: 's002'
147
+ command: "node /bin/pi",
148
+ cwd: "/pi",
149
+ tty: "s002"
150
150
  }
151
151
  ];
152
152
  const captureSnapshot = vi.fn(async ()=>processes);
@@ -158,22 +158,22 @@ describe('AgentManager', ()=>{
158
158
  getConversation: ()=>[],
159
159
  listSessions: async ()=>[]
160
160
  });
161
- const claude = createSnapshotAdapter('claude', [
162
- 'claude'
161
+ const claude = createSnapshotAdapter("claude", [
162
+ "claude"
163
163
  ]);
164
- const pi = createSnapshotAdapter('pi', [
165
- 'pi',
166
- 'node'
164
+ const pi = createSnapshotAdapter("pi", [
165
+ "pi",
166
+ "node"
167
167
  ]);
168
- const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'snapshot-agents.json')), captureSnapshot);
168
+ const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "snapshot-agents.json")), captureSnapshot);
169
169
  snapshotManager.registerAdapter(claude);
170
170
  snapshotManager.registerAdapter(pi);
171
171
  await snapshotManager.listAgents();
172
172
  expect(captureSnapshot).toHaveBeenCalledTimes(1);
173
173
  expect(captureSnapshot).toHaveBeenCalledWith([
174
- 'claude',
175
- 'pi',
176
- 'node'
174
+ "claude",
175
+ "pi",
176
+ "node"
177
177
  ]);
178
178
  expect(claude.detectAgents).toHaveBeenCalledWith({
179
179
  processes: [
@@ -186,31 +186,31 @@ describe('AgentManager', ()=>{
186
186
  ]
187
187
  });
188
188
  });
189
- it('does not expose foreign command arguments to broad Pi and Gemini matchers', async ()=>{
189
+ it("does not expose foreign command arguments to broad Pi and Gemini matchers", async ()=>{
190
190
  const processes = [
191
191
  {
192
192
  pid: 100,
193
- command: 'node /usr/local/lib/gemini.js',
194
- cwd: '/g',
195
- tty: 's001'
193
+ command: "node /usr/local/lib/gemini.js",
194
+ cwd: "/g",
195
+ tty: "s001"
196
196
  },
197
197
  {
198
198
  pid: 200,
199
- command: 'codex exec --cd /Users/x/repos/gemini',
200
- cwd: '/c',
201
- tty: 's002'
199
+ command: "codex exec --cd /Users/x/repos/gemini",
200
+ cwd: "/c",
201
+ tty: "s002"
202
202
  },
203
203
  {
204
204
  pid: 300,
205
- command: 'node /usr/local/lib/pi.js',
206
- cwd: '/p',
207
- tty: 's003'
205
+ command: "node /usr/local/lib/pi.js",
206
+ cwd: "/p",
207
+ tty: "s003"
208
208
  },
209
209
  {
210
210
  pid: 400,
211
- command: 'claude --resume /Users/x/pi/session.jsonl',
212
- cwd: '/a',
213
- tty: 's004'
211
+ command: "claude --resume /Users/x/pi/session.jsonl",
212
+ cwd: "/a",
213
+ tty: "s004"
214
214
  }
215
215
  ];
216
216
  const captureSnapshot = vi.fn(async ()=>processes);
@@ -222,20 +222,20 @@ describe('AgentManager', ()=>{
222
222
  getConversation: ()=>[],
223
223
  listSessions: async ()=>[]
224
224
  });
225
- const gemini = createSnapshotAdapter('gemini_cli', [
226
- 'node'
225
+ const gemini = createSnapshotAdapter("gemini_cli", [
226
+ "node"
227
227
  ]);
228
- const codex = createSnapshotAdapter('codex', [
229
- 'codex'
228
+ const codex = createSnapshotAdapter("codex", [
229
+ "codex"
230
230
  ]);
231
- const pi = createSnapshotAdapter('pi', [
232
- 'pi',
233
- 'node'
231
+ const pi = createSnapshotAdapter("pi", [
232
+ "pi",
233
+ "node"
234
234
  ]);
235
- const claude = createSnapshotAdapter('claude', [
236
- 'claude'
235
+ const claude = createSnapshotAdapter("claude", [
236
+ "claude"
237
237
  ]);
238
- const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'filtered-snapshot-agents.json')), captureSnapshot);
238
+ const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "filtered-snapshot-agents.json")), captureSnapshot);
239
239
  snapshotManager.registerAdapter(gemini);
240
240
  snapshotManager.registerAdapter(codex);
241
241
  snapshotManager.registerAdapter(pi);
@@ -243,10 +243,10 @@ describe('AgentManager', ()=>{
243
243
  await snapshotManager.listAgents();
244
244
  expect(captureSnapshot).toHaveBeenCalledTimes(1);
245
245
  expect(captureSnapshot).toHaveBeenCalledWith([
246
- 'node',
247
- 'codex',
248
- 'pi',
249
- 'claude'
246
+ "node",
247
+ "codex",
248
+ "pi",
249
+ "claude"
250
250
  ]);
251
251
  expect(gemini.detectAgents).toHaveBeenCalledWith({
252
252
  processes: [
@@ -271,107 +271,107 @@ describe('AgentManager', ()=>{
271
271
  ]
272
272
  });
273
273
  });
274
- it('does not pass a snapshot context to legacy adapters', async ()=>{
274
+ it("does not pass a snapshot context to legacy adapters", async ()=>{
275
275
  const captureSnapshot = vi.fn(async ()=>[]);
276
- const legacy = new MockAdapter('claude');
277
- const detect = vi.spyOn(legacy, 'detectAgents');
278
- const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, 'legacy-agents.json')), captureSnapshot);
276
+ const legacy = new MockAdapter("claude");
277
+ const detect = vi.spyOn(legacy, "detectAgents");
278
+ const snapshotManager = new AgentManager(new AgentRegistry(path.join(tmpDir, "legacy-agents.json")), captureSnapshot);
279
279
  snapshotManager.registerAdapter(legacy);
280
280
  await snapshotManager.listAgents();
281
281
  expect(captureSnapshot).not.toHaveBeenCalled();
282
282
  expect(detect).toHaveBeenCalledWith();
283
283
  });
284
- it('should return empty array when no adapters registered', async ()=>{
284
+ it("should return empty array when no adapters registered", async ()=>{
285
285
  const agents = await manager.listAgents();
286
286
  expect(agents).toEqual([]);
287
287
  });
288
- it('should return agents from single adapter', async ()=>{
288
+ it("should return agents from single adapter", async ()=>{
289
289
  const mockAgents = [
290
290
  createMockAgent({
291
- name: 'agent1'
291
+ name: "agent1"
292
292
  }),
293
293
  createMockAgent({
294
- name: 'agent2'
294
+ name: "agent2"
295
295
  })
296
296
  ];
297
- const adapter = new MockAdapter('claude', mockAgents);
297
+ const adapter = new MockAdapter("claude", mockAgents);
298
298
  manager.registerAdapter(adapter);
299
299
  const agents = await manager.listAgents();
300
300
  expect(agents).toHaveLength(2);
301
- expect(agents[0].name).toBe('agent1');
302
- expect(agents[1].name).toBe('agent2');
301
+ expect(agents[0].name).toBe("agent1");
302
+ expect(agents[1].name).toBe("agent2");
303
303
  });
304
- it('should aggregate agents from multiple adapters', async ()=>{
304
+ it("should aggregate agents from multiple adapters", async ()=>{
305
305
  const claudeAgents = [
306
306
  createMockAgent({
307
- name: 'claude-agent',
308
- type: 'claude'
307
+ name: "claude-agent",
308
+ type: "claude"
309
309
  })
310
310
  ];
311
311
  const geminiAgents = [
312
312
  createMockAgent({
313
- name: 'gemini-agent',
314
- type: 'gemini_cli'
313
+ name: "gemini-agent",
314
+ type: "gemini_cli"
315
315
  })
316
316
  ];
317
- manager.registerAdapter(new MockAdapter('claude', claudeAgents));
318
- manager.registerAdapter(new MockAdapter('gemini_cli', geminiAgents));
317
+ manager.registerAdapter(new MockAdapter("claude", claudeAgents));
318
+ manager.registerAdapter(new MockAdapter("gemini_cli", geminiAgents));
319
319
  const agents = await manager.listAgents();
320
320
  expect(agents).toHaveLength(2);
321
- expect(agents.find((a)=>a.name === 'claude-agent')).toBeDefined();
322
- expect(agents.find((a)=>a.name === 'gemini-agent')).toBeDefined();
321
+ expect(agents.find((a)=>a.name === "claude-agent")).toBeDefined();
322
+ expect(agents.find((a)=>a.name === "gemini-agent")).toBeDefined();
323
323
  });
324
- it('should sort agents by status priority (waiting first)', async ()=>{
324
+ it("should sort agents by status priority (waiting first)", async ()=>{
325
325
  const mockAgents = [
326
326
  createMockAgent({
327
- name: 'idle-agent',
327
+ name: "idle-agent",
328
328
  status: AgentStatus.IDLE
329
329
  }),
330
330
  createMockAgent({
331
- name: 'waiting-agent',
331
+ name: "waiting-agent",
332
332
  status: AgentStatus.WAITING
333
333
  }),
334
334
  createMockAgent({
335
- name: 'running-agent',
335
+ name: "running-agent",
336
336
  status: AgentStatus.RUNNING
337
337
  }),
338
338
  createMockAgent({
339
- name: 'unknown-agent',
339
+ name: "unknown-agent",
340
340
  status: AgentStatus.UNKNOWN
341
341
  })
342
342
  ];
343
- const adapter = new MockAdapter('claude', mockAgents);
343
+ const adapter = new MockAdapter("claude", mockAgents);
344
344
  manager.registerAdapter(adapter);
345
345
  const agents = await manager.listAgents();
346
- expect(agents[0].name).toBe('waiting-agent');
347
- expect(agents[1].name).toBe('running-agent');
348
- expect(agents[2].name).toBe('idle-agent');
349
- expect(agents[3].name).toBe('unknown-agent');
346
+ expect(agents[0].name).toBe("waiting-agent");
347
+ expect(agents[1].name).toBe("running-agent");
348
+ expect(agents[2].name).toBe("idle-agent");
349
+ expect(agents[3].name).toBe("unknown-agent");
350
350
  });
351
- it('should handle adapter errors gracefully', async ()=>{
352
- const goodAdapter = new MockAdapter('claude', [
351
+ it("should handle adapter errors gracefully", async ()=>{
352
+ const goodAdapter = new MockAdapter("claude", [
353
353
  createMockAgent({
354
- name: 'good-agent'
354
+ name: "good-agent"
355
355
  })
356
356
  ]);
357
- const badAdapter = new MockAdapter('gemini_cli', [], true); // Will fail
357
+ const badAdapter = new MockAdapter("gemini_cli", [], true); // Will fail
358
358
  manager.registerAdapter(goodAdapter);
359
359
  manager.registerAdapter(badAdapter);
360
360
  // Should not throw, should return results from working adapter
361
361
  const agents = await manager.listAgents();
362
362
  expect(agents).toHaveLength(1);
363
- expect(agents[0].name).toBe('good-agent');
363
+ expect(agents[0].name).toBe("good-agent");
364
364
  });
365
- it('should return empty array when all adapters fail', async ()=>{
366
- const adapter1 = new MockAdapter('claude', [], true);
367
- const adapter2 = new MockAdapter('gemini_cli', [], true);
365
+ it("should return empty array when all adapters fail", async ()=>{
366
+ const adapter1 = new MockAdapter("claude", [], true);
367
+ const adapter2 = new MockAdapter("gemini_cli", [], true);
368
368
  manager.registerAdapter(adapter1);
369
369
  manager.registerAdapter(adapter2);
370
370
  const agents = await manager.listAgents();
371
371
  expect(agents).toEqual([]);
372
372
  });
373
373
  });
374
- describe('listAgents — registry persistence', ()=>{
374
+ describe("listAgents — registry persistence", ()=>{
375
375
  let tmpDir;
376
376
  let regPath;
377
377
  let registry;
@@ -379,9 +379,9 @@ describe('AgentManager', ()=>{
379
379
  let nowMs;
380
380
  let databaseOperations;
381
381
  beforeEach(()=>{
382
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'agent-manager-'));
383
- regPath = path.join(tmpDir, 'agents.json');
384
- nowMs = Date.parse('2026-08-14T10:00:00.000Z');
382
+ tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "agent-manager-"));
383
+ regPath = path.join(tmpDir, "agents.json");
384
+ nowMs = Date.parse("2026-08-14T10:00:00.000Z");
385
385
  databaseOperations = [];
386
386
  registry = new AgentRegistry(regPath, {
387
387
  now: ()=>new Date(nowMs),
@@ -398,149 +398,375 @@ describe('AgentManager', ()=>{
398
398
  force: true
399
399
  });
400
400
  });
401
- it('persists every detected agent to the registry', async ()=>{
402
- scopedManager.registerAdapter(new MockAdapter('claude', [
401
+ function useHerdrPanes(panes) {
402
+ const fetchHerdrPanes = vi.fn().mockResolvedValue(panes);
403
+ scopedManager = new AgentManager(registry, undefined, {
404
+ runtimeProvider: "herdr",
405
+ fetchHerdrAgentPanes: fetchHerdrPanes
406
+ });
407
+ return fetchHerdrPanes;
408
+ }
409
+ it("persists every detected agent to the registry", async ()=>{
410
+ scopedManager.registerAdapter(new MockAdapter("claude", [
403
411
  createMockAgent({
404
- name: 'a',
412
+ name: "a",
405
413
  pid: process.pid,
406
- sessionId: 'sid-a',
407
- sessionFilePath: '/path/a.jsonl',
408
- projectPath: '/cwd/a'
414
+ sessionId: "sid-a",
415
+ sessionFilePath: "/path/a.jsonl",
416
+ projectPath: "/cwd/a"
409
417
  })
410
418
  ]));
411
419
  await scopedManager.listAgents();
412
420
  const entries = registry.list();
413
421
  expect(entries).toHaveLength(1);
414
422
  expect(entries[0]).toMatchObject({
415
- name: 'a',
416
- type: 'claude',
423
+ name: "a",
424
+ type: "claude",
417
425
  pid: process.pid,
418
- cwd: '/cwd/a',
419
- sessionId: 'sid-a',
420
- sessionFilePath: '/path/a.jsonl',
421
- tmuxSession: ''
426
+ cwd: "/cwd/a",
427
+ sessionId: "sid-a",
428
+ sessionFilePath: "/path/a.jsonl",
429
+ runtime: "tmux",
430
+ runtimeRef: null
422
431
  });
423
432
  expect(entries[0].startedAt).toMatch(/^\d{4}-\d{2}-\d{2}T/);
424
433
  });
425
- it('prunes entries for dead pids', async ()=>{
434
+ it("persists a Herdr runtime ref for manually discovered agents matched by session id", async ()=>{
435
+ useHerdrPanes([
436
+ {
437
+ agent: "codex",
438
+ agentSessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
439
+ cwd: "/cwd/ai-devkit",
440
+ paneId: "w31:p1",
441
+ workspaceId: "w31",
442
+ tabId: "w31:t1"
443
+ }
444
+ ]);
445
+ scopedManager.registerAdapter(new MockAdapter("codex", [
446
+ createMockAgent({
447
+ name: "ai-devkit-44290",
448
+ type: "codex",
449
+ pid: process.pid,
450
+ sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
451
+ sessionFilePath: "/path/codex.jsonl",
452
+ projectPath: "/cwd/ai-devkit"
453
+ })
454
+ ]));
455
+ await scopedManager.listAgents();
456
+ expect(registry.list()[0]).toMatchObject({
457
+ name: "ai-devkit-44290",
458
+ type: "codex",
459
+ pid: process.pid,
460
+ runtime: "herdr",
461
+ runtimeRef: {
462
+ session: "default",
463
+ paneId: "w31:p1",
464
+ workspaceId: "w31",
465
+ tabId: "w31:t1",
466
+ agentName: "ai-devkit-44290"
467
+ }
468
+ });
469
+ });
470
+ it("upgrades an existing tmux fallback row when Herdr later matches by session id", async ()=>{
471
+ useHerdrPanes([
472
+ {
473
+ agent: "codex",
474
+ agentSessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
475
+ cwd: "/cwd/ai-devkit",
476
+ paneId: "w31:p1"
477
+ }
478
+ ]);
426
479
  registry.register({
427
- name: 'dead',
428
- type: 'claude',
480
+ name: "ai-devkit-44290",
481
+ type: "codex",
482
+ pid: process.pid,
483
+ runtime: "tmux",
484
+ runtimeRef: null,
485
+ cwd: "/cwd/ai-devkit",
486
+ startedAt: "2026-09-14T00:00:00.000Z",
487
+ sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
488
+ sessionFilePath: "/path/codex.jsonl"
489
+ });
490
+ scopedManager.registerAdapter(new MockAdapter("codex", [
491
+ createMockAgent({
492
+ name: "ai-devkit-44290",
493
+ type: "codex",
494
+ pid: process.pid,
495
+ sessionId: "01a0a05e-751f-7850-9fd7-247e297f9ac5",
496
+ sessionFilePath: "/path/codex.jsonl",
497
+ projectPath: "/cwd/ai-devkit"
498
+ })
499
+ ]));
500
+ await scopedManager.listAgents();
501
+ expect(registry.lookup("ai-devkit-44290")).toMatchObject({
502
+ runtime: "herdr",
503
+ runtimeRef: {
504
+ session: "default",
505
+ paneId: "w31:p1",
506
+ agentName: "ai-devkit-44290"
507
+ }
508
+ });
509
+ });
510
+ it("upgrades an existing tmux fallback row when Herdr matches by foreground pid", async ()=>{
511
+ useHerdrPanes([
512
+ {
513
+ agent: "codex",
514
+ cwd: "/cwd/ai-devkit",
515
+ paneId: "w31:p1",
516
+ foregroundPids: [
517
+ 44290
518
+ ]
519
+ },
520
+ {
521
+ agent: "codex",
522
+ cwd: "/cwd/ai-devkit",
523
+ paneId: "w32:p1",
524
+ foregroundPids: [
525
+ process.pid
526
+ ]
527
+ }
528
+ ]);
529
+ registry.register({
530
+ name: "ai-devkit-64904",
531
+ type: "codex",
532
+ pid: process.pid,
533
+ runtime: "tmux",
534
+ runtimeRef: null,
535
+ cwd: "/cwd/ai-devkit",
536
+ startedAt: "2026-09-14T00:00:00.000Z",
537
+ sessionId: `pid-${process.pid}`,
538
+ sessionFilePath: ""
539
+ });
540
+ scopedManager.registerAdapter(new MockAdapter("codex", [
541
+ createMockAgent({
542
+ name: "ai-devkit-64904",
543
+ type: "codex",
544
+ pid: process.pid,
545
+ sessionId: `pid-${process.pid}`,
546
+ sessionFilePath: undefined,
547
+ projectPath: "/cwd/ai-devkit"
548
+ })
549
+ ]));
550
+ await scopedManager.listAgents();
551
+ expect(registry.lookup("ai-devkit-64904")).toMatchObject({
552
+ runtime: "herdr",
553
+ runtimeRef: {
554
+ session: "default",
555
+ paneId: "w32:p1",
556
+ agentName: "ai-devkit-64904"
557
+ }
558
+ });
559
+ });
560
+ it("fetches Herdr panes automatically when the configured runtime is Herdr", async ()=>{
561
+ const fetchHerdrPanes = useHerdrPanes([
562
+ {
563
+ agent: "codex",
564
+ cwd: "/cwd/ai-devkit",
565
+ paneId: "w32:p1",
566
+ foregroundPids: [
567
+ process.pid
568
+ ]
569
+ }
570
+ ]);
571
+ scopedManager.registerAdapter(new MockAdapter("codex", [
572
+ createMockAgent({
573
+ name: "ai-devkit-64904",
574
+ type: "codex",
575
+ pid: process.pid,
576
+ sessionId: `pid-${process.pid}`,
577
+ sessionFilePath: undefined,
578
+ projectPath: "/cwd/ai-devkit"
579
+ })
580
+ ]));
581
+ await scopedManager.listAgents();
582
+ expect(fetchHerdrPanes).toHaveBeenCalledOnce();
583
+ expect(registry.lookup("ai-devkit-64904")).toMatchObject({
584
+ runtime: "herdr",
585
+ runtimeRef: {
586
+ session: "default",
587
+ paneId: "w32:p1",
588
+ agentName: "ai-devkit-64904"
589
+ }
590
+ });
591
+ });
592
+ it("upgrades an existing tmux row with a malformed runtime ref when Herdr matches", async ()=>{
593
+ useHerdrPanes([
594
+ {
595
+ agent: "codex",
596
+ cwd: "/cwd/ai-devkit",
597
+ paneId: "w32:p1",
598
+ foregroundPids: [
599
+ process.pid
600
+ ]
601
+ }
602
+ ]);
603
+ registry.register({
604
+ name: "ai-devkit-64904",
605
+ type: "codex",
606
+ pid: process.pid,
607
+ runtime: "tmux",
608
+ runtimeRef: {},
609
+ cwd: "/cwd/ai-devkit",
610
+ startedAt: "2026-09-14T00:00:00.000Z",
611
+ sessionId: `pid-${process.pid}`,
612
+ sessionFilePath: ""
613
+ });
614
+ scopedManager.registerAdapter(new MockAdapter("codex", [
615
+ createMockAgent({
616
+ name: "ai-devkit-64904",
617
+ type: "codex",
618
+ pid: process.pid,
619
+ sessionId: `pid-${process.pid}`,
620
+ sessionFilePath: undefined,
621
+ projectPath: "/cwd/ai-devkit"
622
+ })
623
+ ]));
624
+ await scopedManager.listAgents();
625
+ expect(registry.lookup("ai-devkit-64904")).toMatchObject({
626
+ runtime: "herdr",
627
+ runtimeRef: {
628
+ session: "default",
629
+ paneId: "w32:p1",
630
+ agentName: "ai-devkit-64904"
631
+ }
632
+ });
633
+ });
634
+ it("prunes entries for dead pids", async ()=>{
635
+ registry.register({
636
+ name: "dead",
637
+ type: "claude",
429
638
  pid: 999999,
430
- tmuxSession: '',
431
- cwd: '/cwd/dead',
432
- startedAt: '2026-05-30T00:00:00.000Z',
433
- sessionId: 'sid-dead',
434
- sessionFilePath: '/path/dead.jsonl'
639
+ runtime: "tmux",
640
+ runtimeRef: null,
641
+ cwd: "/cwd/dead",
642
+ startedAt: "2026-05-30T00:00:00.000Z",
643
+ sessionId: "sid-dead",
644
+ sessionFilePath: "/path/dead.jsonl"
435
645
  });
436
- scopedManager.registerAdapter(new MockAdapter('claude', [
646
+ scopedManager.registerAdapter(new MockAdapter("claude", [
437
647
  createMockAgent({
438
- name: 'live',
648
+ name: "live",
439
649
  pid: process.pid
440
650
  })
441
651
  ]));
442
652
  await scopedManager.listAgents();
443
653
  const entries = registry.list();
444
654
  expect(entries.map((e)=>e.name)).toEqual([
445
- 'live'
655
+ "live"
446
656
  ]);
447
657
  });
448
658
  it('preserves an existing name (e.g. user-set "merry") across cycles', async ()=>{
449
659
  registry.register({
450
- name: 'merry',
451
- type: 'claude',
660
+ name: "merry",
661
+ type: "claude",
452
662
  pid: process.pid,
453
- tmuxSession: 'merry',
454
- cwd: '/cwd/merry',
455
- startedAt: '2026-05-30T00:00:00.000Z',
456
- sessionId: 'sid-merry',
457
- sessionFilePath: '/path/merry.jsonl'
663
+ runtime: "tmux",
664
+ runtimeRef: {
665
+ session: "merry"
666
+ },
667
+ cwd: "/cwd/merry",
668
+ startedAt: "2026-05-30T00:00:00.000Z",
669
+ sessionId: "sid-merry",
670
+ sessionFilePath: "/path/merry.jsonl"
458
671
  });
459
- scopedManager.registerAdapter(new MockAdapter('claude', [
672
+ scopedManager.registerAdapter(new MockAdapter("claude", [
460
673
  createMockAgent({
461
- name: 'default-name',
674
+ name: "default-name",
462
675
  pid: process.pid
463
676
  })
464
677
  ]));
465
678
  const agents = await scopedManager.listAgents();
466
- expect(agents[0].name).toBe('merry');
467
- expect(registry.list()[0].name).toBe('merry');
468
- expect(registry.list()[0].tmuxSession).toBe('merry');
469
- expect(registry.list()[0].startedAt).toBe('2026-05-30T00:00:00.000Z');
679
+ expect(agents[0].name).toBe("merry");
680
+ expect(registry.list()[0].name).toBe("merry");
681
+ expect(registry.list()[0].runtimeRef).toEqual({
682
+ session: "merry"
683
+ });
684
+ expect(registry.list()[0].startedAt).toBe("2026-05-30T00:00:00.000Z");
470
685
  });
471
- it('preserves custom name and tmux session across two EPERM refresh cycles', async ()=>{
686
+ it("preserves custom name and tmux runtime ref across two EPERM refresh cycles", async ()=>{
472
687
  registry.register({
473
- name: 'merry',
474
- type: 'claude',
688
+ name: "merry",
689
+ type: "claude",
475
690
  pid: process.pid,
476
- tmuxSession: 'merry-tmux',
477
- cwd: '/cwd/merry',
478
- startedAt: '2026-05-30T00:00:00.000Z',
479
- sessionId: 'sid-merry',
480
- sessionFilePath: '/path/merry.jsonl'
691
+ runtime: "tmux",
692
+ runtimeRef: {
693
+ session: "merry-tmux"
694
+ },
695
+ cwd: "/cwd/merry",
696
+ startedAt: "2026-05-30T00:00:00.000Z",
697
+ sessionId: "sid-merry",
698
+ sessionFilePath: "/path/merry.jsonl"
481
699
  });
482
- scopedManager.registerAdapter(new MockAdapter('claude', [
700
+ scopedManager.registerAdapter(new MockAdapter("claude", [
483
701
  createMockAgent({
484
702
  name: `ai-devkit-${process.pid}`,
485
703
  pid: process.pid
486
704
  })
487
705
  ]));
488
- vi.spyOn(process, 'kill').mockImplementation(()=>{
489
- throw Object.assign(new Error('operation not permitted'), {
490
- code: 'EPERM'
706
+ vi.spyOn(process, "kill").mockImplementation(()=>{
707
+ throw Object.assign(new Error("operation not permitted"), {
708
+ code: "EPERM"
491
709
  });
492
710
  });
493
711
  const firstRefresh = await scopedManager.listAgents();
494
712
  const secondRefresh = await scopedManager.listAgents();
495
- expect(firstRefresh[0].name).toBe('merry');
496
- expect(secondRefresh[0].name).toBe('merry');
497
- expect(registry.lookup('merry')).toMatchObject({
498
- name: 'merry',
499
- tmuxSession: 'merry-tmux'
713
+ expect(firstRefresh[0].name).toBe("merry");
714
+ expect(secondRefresh[0].name).toBe("merry");
715
+ expect(registry.lookup("merry")).toMatchObject({
716
+ name: "merry",
717
+ runtimeRef: {
718
+ session: "merry-tmux"
719
+ }
500
720
  });
501
721
  });
502
- it('preserves a user-managed name when a fallback row was written later for the same pid', async ()=>{
722
+ it("preserves a user-managed name when a fallback row was written later for the same pid", async ()=>{
503
723
  registry.register({
504
- name: 'agent-list-debug',
505
- type: 'codex',
724
+ name: "agent-list-debug",
725
+ type: "codex",
506
726
  pid: process.pid,
507
- tmuxSession: 'agent-list-debug',
508
- cwd: '/cwd/debug',
509
- startedAt: '2026-05-30T00:00:00.000Z',
510
- sessionId: 'pid-debug',
511
- sessionFilePath: ''
727
+ runtime: "tmux",
728
+ runtimeRef: {
729
+ session: "agent-list-debug"
730
+ },
731
+ cwd: "/cwd/debug",
732
+ startedAt: "2026-05-30T00:00:00.000Z",
733
+ sessionId: "pid-debug",
734
+ sessionFilePath: ""
512
735
  });
513
736
  registry.register({
514
737
  name: `ai-devkit-${process.pid}`,
515
- type: 'codex',
738
+ type: "codex",
516
739
  pid: process.pid,
517
- tmuxSession: '',
518
- cwd: '/cwd/debug',
519
- startedAt: '2026-05-31T00:00:00.000Z',
520
- sessionId: 'pid-debug',
521
- sessionFilePath: ''
740
+ runtime: "tmux",
741
+ runtimeRef: null,
742
+ cwd: "/cwd/debug",
743
+ startedAt: "2026-05-31T00:00:00.000Z",
744
+ sessionId: "pid-debug",
745
+ sessionFilePath: ""
522
746
  });
523
- scopedManager.registerAdapter(new MockAdapter('codex', [
747
+ scopedManager.registerAdapter(new MockAdapter("codex", [
524
748
  createMockAgent({
525
749
  name: `ai-devkit-${process.pid}`,
526
- type: 'codex',
750
+ type: "codex",
527
751
  pid: process.pid
528
752
  })
529
753
  ]));
530
754
  const agents = await scopedManager.listAgents();
531
- expect(agents[0].name).toBe('agent-list-debug');
755
+ expect(agents[0].name).toBe("agent-list-debug");
532
756
  expect(registry.list()).toHaveLength(1);
533
757
  expect(registry.list()[0]).toMatchObject({
534
- name: 'agent-list-debug',
758
+ name: "agent-list-debug",
535
759
  pid: process.pid,
536
- tmuxSession: 'agent-list-debug'
760
+ runtimeRef: {
761
+ session: "agent-list-debug"
762
+ }
537
763
  });
538
764
  });
539
- it('writes a fresh startedAt for new entries', async ()=>{
765
+ it("writes a fresh startedAt for new entries", async ()=>{
540
766
  const before = new Date().toISOString();
541
- scopedManager.registerAdapter(new MockAdapter('claude', [
767
+ scopedManager.registerAdapter(new MockAdapter("claude", [
542
768
  createMockAgent({
543
- name: 'new',
769
+ name: "new",
544
770
  pid: process.pid
545
771
  })
546
772
  ]));
@@ -548,36 +774,36 @@ describe('AgentManager', ()=>{
548
774
  const entry = registry.list()[0];
549
775
  expect(entry.startedAt >= before).toBe(true);
550
776
  });
551
- it('batches the write — a single registerBatch call per listAgents', async ()=>{
552
- const spy = vi.spyOn(registry, 'registerBatch');
553
- scopedManager.registerAdapter(new MockAdapter('claude', [
777
+ it("batches the write — a single registerBatch call per listAgents", async ()=>{
778
+ const spy = vi.spyOn(registry, "registerBatch");
779
+ scopedManager.registerAdapter(new MockAdapter("claude", [
554
780
  createMockAgent({
555
- name: 'a',
781
+ name: "a",
556
782
  pid: process.pid
557
783
  })
558
784
  ]));
559
- scopedManager.registerAdapter(new MockAdapter('codex', [
785
+ scopedManager.registerAdapter(new MockAdapter("codex", [
560
786
  createMockAgent({
561
- name: 'b',
562
- type: 'codex',
787
+ name: "b",
788
+ type: "codex",
563
789
  pid: process.pid + 1
564
790
  })
565
791
  ]));
566
792
  await scopedManager.listAgents();
567
793
  expect(spy).toHaveBeenCalledTimes(1);
568
794
  expect(spy.mock.calls[0][0].map((e)=>e.name).sort()).toEqual([
569
- 'a',
570
- 'b'
795
+ "a",
796
+ "b"
571
797
  ]);
572
798
  });
573
- it('performs zero database writes on an unchanged refresh', async ()=>{
574
- const adapter = new MockAdapter('claude', [
799
+ it("performs zero database writes on an unchanged refresh", async ()=>{
800
+ const adapter = new MockAdapter("claude", [
575
801
  createMockAgent({
576
- name: 'stable',
802
+ name: "stable",
577
803
  pid: process.pid,
578
- projectPath: '/cwd/stable',
579
- sessionId: 'stable-session',
580
- sessionFilePath: '/sessions/stable.jsonl'
804
+ projectPath: "/cwd/stable",
805
+ sessionId: "stable-session",
806
+ sessionFilePath: "/sessions/stable.jsonl"
581
807
  })
582
808
  ]);
583
809
  scopedManager.registerAdapter(adapter);
@@ -587,66 +813,66 @@ describe('AgentManager', ()=>{
587
813
  const writes = databaseOperations.filter((sql)=>/^\s*(BEGIN|COMMIT|INSERT|UPDATE|DELETE)/i.test(sql));
588
814
  expect(writes).toEqual([]);
589
815
  });
590
- it('exposes a persisted pin and preserves it across a changed poll refresh', async ()=>{
591
- const adapter = new MockAdapter('claude', [
816
+ it("exposes a persisted pin and preserves it across a changed poll refresh", async ()=>{
817
+ const adapter = new MockAdapter("claude", [
592
818
  createMockAgent({
593
- name: 'pinned',
819
+ name: "pinned",
594
820
  pid: process.pid,
595
- sessionId: 'before'
821
+ sessionId: "before"
596
822
  })
597
823
  ]);
598
824
  scopedManager.registerAdapter(adapter);
599
825
  await scopedManager.listAgents();
600
- registry.togglePin('claude', process.pid);
826
+ registry.togglePin("claude", process.pid);
601
827
  adapter.setAgents([
602
828
  createMockAgent({
603
- name: 'pinned',
829
+ name: "pinned",
604
830
  pid: process.pid,
605
- sessionId: 'after'
831
+ sessionId: "after"
606
832
  })
607
833
  ]);
608
834
  const agents = await scopedManager.listAgents();
609
835
  expect(agents[0].pinned).toBe(true);
610
- expect(registry.lookup('pinned')).toMatchObject({
611
- sessionId: 'after',
836
+ expect(registry.lookup("pinned")).toMatchObject({
837
+ sessionId: "after",
612
838
  pinned: true
613
839
  });
614
840
  });
615
- it('uses registry updated_at as lastActive for pinned recency ordering', async ()=>{
616
- const adapter = new MockAdapter('claude', [
841
+ it("uses registry updated_at as lastActive for pinned recency ordering", async ()=>{
842
+ const adapter = new MockAdapter("claude", [
617
843
  createMockAgent({
618
- name: 'recently-pinned',
844
+ name: "recently-pinned",
619
845
  pid: process.pid,
620
- lastActive: new Date('2026-01-01T00:00:00.000Z')
846
+ lastActive: new Date("2026-01-01T00:00:00.000Z")
621
847
  })
622
848
  ]);
623
849
  scopedManager.registerAdapter(adapter);
624
850
  await scopedManager.listAgents();
625
851
  nowMs += 60_000;
626
- scopedManager.togglePin('recently-pinned');
852
+ scopedManager.togglePin("recently-pinned");
627
853
  const agents = await scopedManager.listAgents();
628
854
  expect(agents[0].pinned).toBe(true);
629
- expect(agents[0].lastActive.toISOString()).toBe('2026-08-14T10:01:00.000Z');
855
+ expect(agents[0].lastActive.toISOString()).toBe("2026-08-14T10:01:00.000Z");
630
856
  });
631
- it('preserves adapter lastActive for unpinned agents', async ()=>{
632
- scopedManager.registerAdapter(new MockAdapter('claude', [
857
+ it("preserves adapter lastActive for unpinned agents", async ()=>{
858
+ scopedManager.registerAdapter(new MockAdapter("claude", [
633
859
  createMockAgent({
634
- name: 'unpinned',
860
+ name: "unpinned",
635
861
  pid: process.pid,
636
- lastActive: new Date('2026-01-01T00:00:00.000Z')
862
+ lastActive: new Date("2026-01-01T00:00:00.000Z")
637
863
  })
638
864
  ]));
639
865
  await scopedManager.listAgents();
640
866
  const agents = await scopedManager.listAgents();
641
867
  expect(agents[0].pinned).toBe(false);
642
- expect(agents[0].lastActive.toISOString()).toBe('2026-01-01T00:00:00.000Z');
868
+ expect(agents[0].lastActive.toISOString()).toBe("2026-01-01T00:00:00.000Z");
643
869
  });
644
- it('persists changed fields once in one write transaction', async ()=>{
645
- const adapter = new MockAdapter('claude', [
870
+ it("does not persist passive cwd changes when an existing cwd is present", async ()=>{
871
+ const adapter = new MockAdapter("claude", [
646
872
  createMockAgent({
647
- name: 'changing',
873
+ name: "changing",
648
874
  pid: process.pid,
649
- projectPath: '/cwd/before'
875
+ projectPath: "/cwd/before"
650
876
  })
651
877
  ]);
652
878
  scopedManager.registerAdapter(adapter);
@@ -655,350 +881,356 @@ describe('AgentManager', ()=>{
655
881
  nowMs += 1_000;
656
882
  adapter.setAgents([
657
883
  createMockAgent({
658
- name: 'changing',
884
+ name: "changing",
659
885
  pid: process.pid,
660
- projectPath: '/cwd/after'
886
+ projectPath: "/cwd/after"
661
887
  })
662
888
  ]);
663
889
  await scopedManager.listAgents();
664
890
  const upserts = databaseOperations.filter((sql)=>/^\s*INSERT INTO agents/i.test(sql));
665
891
  const transactions = databaseOperations.filter((sql)=>/^\s*(BEGIN|COMMIT)/i.test(sql));
666
- expect(upserts).toHaveLength(1);
667
- expect(upserts[0]).toContain("'2026-08-14T10:00:01.000Z'");
668
- expect(transactions).toHaveLength(2);
669
- expect(registry.lookup('changing')?.cwd).toBe('/cwd/after');
892
+ expect(upserts).toHaveLength(0);
893
+ expect(transactions).toHaveLength(0);
894
+ expect(registry.lookup("changing")?.cwd).toBe("/cwd/before");
670
895
  });
671
- it('prunes newly dead entries only when the passive cadence is due', async ()=>{
896
+ it("prunes newly dead entries only when the passive cadence is due", async ()=>{
672
897
  registry.register({
673
- name: 'cadenced',
674
- type: 'claude',
898
+ name: "cadenced",
899
+ type: "claude",
675
900
  pid: process.pid,
676
- tmuxSession: '',
677
- cwd: '/cwd/cadenced',
678
- startedAt: '2026-05-30T00:00:00.000Z',
679
- sessionId: 'sid-cadenced',
680
- sessionFilePath: ''
901
+ runtime: "tmux",
902
+ runtimeRef: null,
903
+ cwd: "/cwd/cadenced",
904
+ startedAt: "2026-05-30T00:00:00.000Z",
905
+ sessionId: "sid-cadenced",
906
+ sessionFilePath: ""
681
907
  });
682
- const alive = vi.spyOn(registry, 'isAlive').mockReturnValue(true);
908
+ const alive = vi.spyOn(registry, "isAlive").mockReturnValue(true);
683
909
  await scopedManager.listAgents();
684
910
  expect(alive).toHaveBeenCalledTimes(1);
685
911
  alive.mockReturnValue(false);
686
912
  nowMs += 29_999;
687
913
  await scopedManager.listAgents();
688
914
  expect(alive).toHaveBeenCalledTimes(1);
689
- expect(registry.lookup('cadenced')).not.toBeNull();
915
+ expect(registry.lookup("cadenced")).not.toBeNull();
690
916
  nowMs += 1;
691
917
  await scopedManager.listAgents();
692
918
  expect(alive).toHaveBeenCalledTimes(2);
693
- expect(registry.lookup('cadenced')).toBeNull();
919
+ expect(registry.lookup("cadenced")).toBeNull();
694
920
  });
695
- it('does not inherit a name when the same pid is reused by another agent type', async ()=>{
921
+ it("does not inherit a name when the same pid is reused by another agent type", async ()=>{
696
922
  registry.register({
697
- name: 'old-claude',
698
- type: 'claude',
923
+ name: "old-claude",
924
+ type: "claude",
699
925
  pid: process.pid,
700
- tmuxSession: 'old-claude',
701
- cwd: '/cwd/old',
702
- startedAt: '2026-05-30T00:00:00.000Z',
703
- sessionId: 'old-session',
704
- sessionFilePath: ''
926
+ runtime: "tmux",
927
+ runtimeRef: {
928
+ session: "old-claude"
929
+ },
930
+ cwd: "/cwd/old",
931
+ startedAt: "2026-05-30T00:00:00.000Z",
932
+ sessionId: "old-session",
933
+ sessionFilePath: ""
705
934
  });
706
- scopedManager.registerAdapter(new MockAdapter('codex', [
935
+ scopedManager.registerAdapter(new MockAdapter("codex", [
707
936
  createMockAgent({
708
- name: 'new-codex',
709
- type: 'codex',
937
+ name: "new-codex",
938
+ type: "codex",
710
939
  pid: process.pid,
711
- projectPath: '/cwd/new',
712
- sessionId: 'new-session'
940
+ projectPath: "/cwd/new",
941
+ sessionId: "new-session"
713
942
  })
714
943
  ]));
715
944
  const agents = await scopedManager.listAgents();
716
- expect(agents[0].name).toBe('new-codex');
717
- expect(registry.lookup('old-claude')).toBeNull();
718
- expect(registry.lookup('new-codex')).toMatchObject({
719
- type: 'codex',
945
+ expect(agents[0].name).toBe("new-codex");
946
+ expect(registry.lookup("old-claude")).toBeNull();
947
+ expect(registry.lookup("new-codex")).toMatchObject({
948
+ type: "codex",
720
949
  pid: process.pid
721
950
  });
722
951
  });
723
- it('skips registerBatch when no agents are detected and prune is not due', async ()=>{
724
- const writeSpy = vi.spyOn(registry, 'registerBatch');
725
- const pruneSpy = vi.spyOn(registry, 'pruneIfDue');
726
- scopedManager.registerAdapter(new MockAdapter('claude', []));
952
+ it("skips registerBatch when no agents are detected and prune is not due", async ()=>{
953
+ const writeSpy = vi.spyOn(registry, "registerBatch");
954
+ const pruneSpy = vi.spyOn(registry, "pruneIfDue");
955
+ scopedManager.registerAdapter(new MockAdapter("claude", []));
727
956
  await scopedManager.listAgents();
728
957
  await scopedManager.listAgents();
729
958
  expect(writeSpy).not.toHaveBeenCalled();
730
959
  expect(pruneSpy).toHaveBeenCalledTimes(2);
731
960
  });
732
961
  });
733
- describe('togglePin', ()=>{
734
- it('resolves the agent name to its process identity and toggles the pin', ()=>{
735
- const registry = new AgentRegistry(path.join(tmpDir, 'toggle.json'));
962
+ describe("togglePin", ()=>{
963
+ it("resolves the agent name to its process identity and toggles the pin", ()=>{
964
+ const registry = new AgentRegistry(path.join(tmpDir, "toggle.json"));
736
965
  const scopedManager = new AgentManager(registry);
737
966
  registry.register({
738
- name: 'renamed-agent',
739
- type: 'claude',
967
+ name: "renamed-agent",
968
+ type: "claude",
740
969
  pid: process.pid,
741
- tmuxSession: '',
742
- cwd: '/tmp',
743
- startedAt: '2026-08-16T00:00:00.000Z',
744
- sessionId: 'session',
745
- sessionFilePath: '',
970
+ runtime: "tmux",
971
+ runtimeRef: null,
972
+ cwd: "/tmp",
973
+ startedAt: "2026-08-16T00:00:00.000Z",
974
+ sessionId: "session",
975
+ sessionFilePath: "",
746
976
  pinned: false
747
977
  });
748
- expect(scopedManager.togglePin('renamed-agent')).toBe(true);
749
- expect(registry.lookup('renamed-agent')?.pinned).toBe(true);
978
+ expect(scopedManager.togglePin("renamed-agent")).toBe(true);
979
+ expect(registry.lookup("renamed-agent")?.pinned).toBe(true);
750
980
  });
751
- it('reports when the agent is no longer running', ()=>{
752
- expect(()=>manager.togglePin('missing')).toThrow(/no longer running/i);
981
+ it("reports when the agent is no longer running", ()=>{
982
+ expect(()=>manager.togglePin("missing")).toThrow(/no longer running/i);
753
983
  });
754
- it('rejects a dead process and prunes its row', ()=>{
755
- const registry = new AgentRegistry(path.join(tmpDir, 'dead-toggle.json'));
984
+ it("rejects a dead process and prunes its row", ()=>{
985
+ const registry = new AgentRegistry(path.join(tmpDir, "dead-toggle.json"));
756
986
  const scopedManager = new AgentManager(registry);
757
987
  registry.register({
758
- name: 'dead',
759
- type: 'claude',
988
+ name: "dead",
989
+ type: "claude",
760
990
  pid: 999999,
761
- tmuxSession: '',
762
- cwd: '/tmp',
763
- startedAt: '2026-08-16T00:00:00.000Z',
764
- sessionId: 'session',
765
- sessionFilePath: '',
991
+ runtime: "tmux",
992
+ runtimeRef: null,
993
+ cwd: "/tmp",
994
+ startedAt: "2026-08-16T00:00:00.000Z",
995
+ sessionId: "session",
996
+ sessionFilePath: "",
766
997
  pinned: false
767
998
  });
768
- expect(()=>scopedManager.togglePin('dead')).toThrow(/no longer running/i);
769
- expect(registry.lookup('dead')).toBeNull();
999
+ expect(()=>scopedManager.togglePin("dead")).toThrow(/no longer running/i);
1000
+ expect(registry.lookup("dead")).toBeNull();
770
1001
  });
771
- it('surfaces a clear readonly mutation error', ()=>{
772
- const regPath = path.join(tmpDir, 'readonly-toggle.json');
1002
+ it("surfaces a clear readonly mutation error", ()=>{
1003
+ const regPath = path.join(tmpDir, "readonly-toggle.json");
773
1004
  const writable = new AgentRegistry(regPath);
774
1005
  writable.register({
775
- name: 'readonly-agent',
776
- type: 'claude',
1006
+ name: "readonly-agent",
1007
+ type: "claude",
777
1008
  pid: process.pid,
778
- tmuxSession: '',
779
- cwd: '/tmp',
780
- startedAt: '2026-08-16T00:00:00.000Z',
781
- sessionId: 'session',
782
- sessionFilePath: '',
1009
+ runtime: "tmux",
1010
+ runtimeRef: null,
1011
+ cwd: "/tmp",
1012
+ startedAt: "2026-08-16T00:00:00.000Z",
1013
+ sessionId: "session",
1014
+ sessionFilePath: "",
783
1015
  pinned: false
784
1016
  });
785
1017
  const readonlyManager = new AgentManager(new AgentRegistry(regPath, {
786
1018
  readonly: true
787
1019
  }));
788
- expect(()=>readonlyManager.togglePin('readonly-agent')).toThrow('Agent registry is readonly; cannot toggle pin.');
1020
+ expect(()=>readonlyManager.togglePin("readonly-agent")).toThrow("Agent registry is readonly; cannot toggle pin.");
789
1021
  });
790
1022
  });
791
- describe('clear', ()=>{
792
- it('should remove all adapters', ()=>{
793
- manager.registerAdapter(new MockAdapter('claude'));
794
- manager.registerAdapter(new MockAdapter('gemini_cli'));
1023
+ describe("clear", ()=>{
1024
+ it("should remove all adapters", ()=>{
1025
+ manager.registerAdapter(new MockAdapter("claude"));
1026
+ manager.registerAdapter(new MockAdapter("gemini_cli"));
795
1027
  manager.clear();
796
1028
  expect(manager.getAdapterCount()).toBe(0);
797
1029
  expect(manager.getAdapters()).toEqual([]);
798
1030
  });
799
1031
  });
800
- describe('listSessions', ()=>{
1032
+ describe("listSessions", ()=>{
801
1033
  function createMockSession(overrides = {}) {
802
1034
  return {
803
- type: 'claude',
804
- sessionId: 'session-1',
805
- cwd: '/repo',
806
- firstUserMessage: 'hello',
807
- lastActive: new Date('2025-01-01T00:00:00Z'),
808
- startedAt: new Date('2025-01-01T00:00:00Z'),
809
- sessionFilePath: '/tmp/session-1.jsonl',
1035
+ type: "claude",
1036
+ sessionId: "session-1",
1037
+ cwd: "/repo",
1038
+ firstUserMessage: "hello",
1039
+ lastActive: new Date("2025-01-01T00:00:00Z"),
1040
+ startedAt: new Date("2025-01-01T00:00:00Z"),
1041
+ sessionFilePath: "/tmp/session-1.jsonl",
810
1042
  ...overrides
811
1043
  };
812
1044
  }
813
- it('returns empty array when no adapters are registered', async ()=>{
1045
+ it("returns empty array when no adapters are registered", async ()=>{
814
1046
  const result = await manager.listSessions();
815
1047
  expect(result).toEqual([]);
816
1048
  });
817
- it('merges sessions from every registered adapter', async ()=>{
1049
+ it("merges sessions from every registered adapter", async ()=>{
818
1050
  const claudeSession = createMockSession({
819
- type: 'claude',
820
- sessionId: 'c1'
1051
+ type: "claude",
1052
+ sessionId: "c1"
821
1053
  });
822
1054
  const codexSession = createMockSession({
823
- type: 'codex',
824
- sessionId: 'cx1'
1055
+ type: "codex",
1056
+ sessionId: "cx1"
825
1057
  });
826
- manager.registerAdapter(new MockAdapter('claude', [], false, [
1058
+ manager.registerAdapter(new MockAdapter("claude", [], false, [
827
1059
  claudeSession
828
1060
  ]));
829
- manager.registerAdapter(new MockAdapter('codex', [], false, [
1061
+ manager.registerAdapter(new MockAdapter("codex", [], false, [
830
1062
  codexSession
831
1063
  ]));
832
1064
  const result = await manager.listSessions();
833
1065
  expect(result).toHaveLength(2);
834
1066
  expect(result.map((s)=>s.sessionId).sort()).toEqual([
835
- 'c1',
836
- 'cx1'
1067
+ "c1",
1068
+ "cx1"
837
1069
  ]);
838
1070
  });
839
- it('sorts merged sessions by lastActive descending', async ()=>{
1071
+ it("sorts merged sessions by lastActive descending", async ()=>{
840
1072
  const older = createMockSession({
841
- sessionId: 'older',
842
- lastActive: new Date('2025-01-01T00:00:00Z')
1073
+ sessionId: "older",
1074
+ lastActive: new Date("2025-01-01T00:00:00Z")
843
1075
  });
844
1076
  const newer = createMockSession({
845
- type: 'codex',
846
- sessionId: 'newer',
847
- lastActive: new Date('2025-06-01T00:00:00Z')
1077
+ type: "codex",
1078
+ sessionId: "newer",
1079
+ lastActive: new Date("2025-06-01T00:00:00Z")
848
1080
  });
849
- manager.registerAdapter(new MockAdapter('claude', [], false, [
1081
+ manager.registerAdapter(new MockAdapter("claude", [], false, [
850
1082
  older
851
1083
  ]));
852
- manager.registerAdapter(new MockAdapter('codex', [], false, [
1084
+ manager.registerAdapter(new MockAdapter("codex", [], false, [
853
1085
  newer
854
1086
  ]));
855
1087
  const result = await manager.listSessions();
856
1088
  expect(result.map((s)=>s.sessionId)).toEqual([
857
- 'newer',
858
- 'older'
1089
+ "newer",
1090
+ "older"
859
1091
  ]);
860
1092
  });
861
- it('skips adapters whose type does not match opts.type', async ()=>{
862
- const claudeAdapter = new MockAdapter('claude', [], false, [
1093
+ it("skips adapters whose type does not match opts.type", async ()=>{
1094
+ const claudeAdapter = new MockAdapter("claude", [], false, [
863
1095
  createMockSession({
864
- type: 'claude',
865
- sessionId: 'c1'
1096
+ type: "claude",
1097
+ sessionId: "c1"
866
1098
  })
867
1099
  ]);
868
- const codexAdapter = new MockAdapter('codex', [], false, [
1100
+ const codexAdapter = new MockAdapter("codex", [], false, [
869
1101
  createMockSession({
870
- type: 'codex',
871
- sessionId: 'cx1'
1102
+ type: "codex",
1103
+ sessionId: "cx1"
872
1104
  })
873
1105
  ]);
874
1106
  manager.registerAdapter(claudeAdapter);
875
1107
  manager.registerAdapter(codexAdapter);
876
1108
  const result = await manager.listSessions({
877
- type: 'claude'
1109
+ type: "claude"
878
1110
  });
879
1111
  expect(result).toHaveLength(1);
880
- expect(result[0].sessionId).toBe('c1');
1112
+ expect(result[0].sessionId).toBe("c1");
881
1113
  // Codex adapter must not have been called
882
1114
  expect(codexAdapter.lastListSessionsOpts).toBeUndefined();
883
1115
  expect(claudeAdapter.lastListSessionsOpts).toEqual({
884
- type: 'claude'
1116
+ type: "claude"
885
1117
  });
886
1118
  });
887
- it('tolerates an adapter that throws and still returns the others', async ()=>{
1119
+ it("tolerates an adapter that throws and still returns the others", async ()=>{
888
1120
  const goodSession = createMockSession({
889
- sessionId: 'good'
1121
+ sessionId: "good"
890
1122
  });
891
- manager.registerAdapter(new MockAdapter('claude', [], false, [
1123
+ manager.registerAdapter(new MockAdapter("claude", [], false, [
892
1124
  goodSession
893
1125
  ]));
894
- manager.registerAdapter(new MockAdapter('codex', [], false, [], true));
895
- const consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(()=>undefined);
1126
+ manager.registerAdapter(new MockAdapter("codex", [], false, [], true));
1127
+ const consoleErrorSpy = vi.spyOn(console, "error").mockImplementation(()=>undefined);
896
1128
  try {
897
1129
  const result = await manager.listSessions();
898
1130
  expect(result).toHaveLength(1);
899
- expect(result[0].sessionId).toBe('good');
1131
+ expect(result[0].sessionId).toBe("good");
900
1132
  expect(consoleErrorSpy).toHaveBeenCalled();
901
1133
  } finally{
902
1134
  consoleErrorSpy.mockRestore();
903
1135
  }
904
1136
  });
905
- it('passes the same opts to every called adapter', async ()=>{
906
- const a = new MockAdapter('claude', [], false, []);
907
- const b = new MockAdapter('codex', [], false, []);
1137
+ it("passes the same opts to every called adapter", async ()=>{
1138
+ const a = new MockAdapter("claude", [], false, []);
1139
+ const b = new MockAdapter("codex", [], false, []);
908
1140
  manager.registerAdapter(a);
909
1141
  manager.registerAdapter(b);
910
1142
  await manager.listSessions({
911
- cwd: '/Users/test/proj'
1143
+ cwd: "/Users/test/proj"
912
1144
  });
913
1145
  expect(a.lastListSessionsOpts).toEqual({
914
- cwd: '/Users/test/proj'
1146
+ cwd: "/Users/test/proj"
915
1147
  });
916
1148
  expect(b.lastListSessionsOpts).toEqual({
917
- cwd: '/Users/test/proj'
1149
+ cwd: "/Users/test/proj"
918
1150
  });
919
1151
  });
920
1152
  });
921
- describe('resolveAgent', ()=>{
922
- it('should return null for empty input or empty agents list', ()=>{
1153
+ describe("resolveAgent", ()=>{
1154
+ it("should return null for empty input or empty agents list", ()=>{
923
1155
  const agent = createMockAgent({
924
- name: 'test-agent'
1156
+ name: "test-agent"
925
1157
  });
926
- expect(manager.resolveAgent('', [
1158
+ expect(manager.resolveAgent("", [
927
1159
  agent
928
1160
  ])).toBeNull();
929
- expect(manager.resolveAgent('test', [])).toBeNull();
1161
+ expect(manager.resolveAgent("test", [])).toBeNull();
930
1162
  });
931
- it('should resolve exact match (case-insensitive)', ()=>{
1163
+ it("should resolve exact match (case-insensitive)", ()=>{
932
1164
  const agent = createMockAgent({
933
- name: 'My-Agent'
1165
+ name: "My-Agent"
934
1166
  });
935
1167
  const agents = [
936
1168
  agent,
937
1169
  createMockAgent({
938
- name: 'Other'
1170
+ name: "Other"
939
1171
  })
940
1172
  ];
941
1173
  // Exact match
942
- expect(manager.resolveAgent('My-Agent', agents)).toBe(agent);
1174
+ expect(manager.resolveAgent("My-Agent", agents)).toBe(agent);
943
1175
  // Case-insensitive
944
- expect(manager.resolveAgent('my-agent', agents)).toBe(agent);
1176
+ expect(manager.resolveAgent("my-agent", agents)).toBe(agent);
945
1177
  });
946
- it('should resolve unique partial match', ()=>{
1178
+ it("should resolve unique partial match", ()=>{
947
1179
  const agent = createMockAgent({
948
- name: 'ai-devkit'
1180
+ name: "ai-devkit"
949
1181
  });
950
1182
  const agents = [
951
1183
  agent,
952
1184
  createMockAgent({
953
- name: 'other-project'
1185
+ name: "other-project"
954
1186
  })
955
1187
  ];
956
- const result = manager.resolveAgent('dev', agents);
1188
+ const result = manager.resolveAgent("dev", agents);
957
1189
  expect(result).toBe(agent);
958
1190
  });
959
- it('should return array for ambiguous partial match', ()=>{
1191
+ it("should return array for ambiguous partial match", ()=>{
960
1192
  const agent1 = createMockAgent({
961
- name: 'my-website'
1193
+ name: "my-website"
962
1194
  });
963
1195
  const agent2 = createMockAgent({
964
- name: 'my-app'
1196
+ name: "my-app"
965
1197
  });
966
1198
  const agents = [
967
1199
  agent1,
968
1200
  agent2,
969
1201
  createMockAgent({
970
- name: 'other'
1202
+ name: "other"
971
1203
  })
972
1204
  ];
973
- const result = manager.resolveAgent('my', agents);
1205
+ const result = manager.resolveAgent("my", agents);
974
1206
  expect(Array.isArray(result)).toBe(true);
975
1207
  const matches = result;
976
1208
  expect(matches).toHaveLength(2);
977
1209
  expect(matches).toContain(agent1);
978
1210
  expect(matches).toContain(agent2);
979
1211
  });
980
- it('should return null for no match', ()=>{
1212
+ it("should return null for no match", ()=>{
981
1213
  const agents = [
982
1214
  createMockAgent({
983
- name: 'ai-devkit'
1215
+ name: "ai-devkit"
984
1216
  })
985
1217
  ];
986
- expect(manager.resolveAgent('xyz', agents)).toBeNull();
1218
+ expect(manager.resolveAgent("xyz", agents)).toBeNull();
987
1219
  });
988
- it('should prefer exact match over partial matches', ()=>{
1220
+ it("should prefer exact match over partial matches", ()=>{
989
1221
  // Edge case: "test" matches "test" (exact) and "testing" (partial)
990
1222
  // Should return exact "test"
991
1223
  const exact = createMockAgent({
992
- name: 'test'
1224
+ name: "test"
993
1225
  });
994
1226
  const partial = createMockAgent({
995
- name: 'testing'
1227
+ name: "testing"
996
1228
  });
997
1229
  const agents = [
998
1230
  exact,
999
1231
  partial
1000
1232
  ];
1001
- expect(manager.resolveAgent('test', agents)).toBe(exact);
1233
+ expect(manager.resolveAgent("test", agents)).toBe(exact);
1002
1234
  });
1003
1235
  });
1004
1236
  });