@ai-devkit/agent-manager 0.37.0 → 0.39.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (357) hide show
  1. package/dist/AgentManager.d.ts +0 -4
  2. package/dist/AgentManager.d.ts.map +1 -1
  3. package/dist/AgentManager.js +2 -2
  4. package/dist/AgentManager.js.map +1 -1
  5. package/dist/__tests__/capacity/anthropic.test.js +115 -0
  6. package/dist/__tests__/capacity/anthropic.test.js.map +1 -0
  7. package/dist/__tests__/capacity/boundaries.test.js +45 -0
  8. package/dist/__tests__/capacity/boundaries.test.js.map +1 -0
  9. package/dist/__tests__/capacity/claude.test.js +3 -3
  10. package/dist/__tests__/capacity/claude.test.js.map +1 -1
  11. package/dist/__tests__/capacity/codex.test.js +3 -1
  12. package/dist/__tests__/capacity/codex.test.js.map +1 -1
  13. package/dist/__tests__/capacity/openai.test.js +21 -12
  14. package/dist/__tests__/capacity/openai.test.js.map +1 -1
  15. package/dist/__tests__/capacity/wham.test.js +1 -1
  16. package/dist/__tests__/capacity/wham.test.js.map +1 -1
  17. package/dist/__tests__/capacity/zai.test.js +7 -8
  18. package/dist/__tests__/capacity/zai.test.js.map +1 -1
  19. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js +2 -1
  20. package/dist/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.js.map +1 -1
  21. package/dist/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.js.map +1 -1
  22. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js +7 -15
  23. package/dist/__tests__/harnesses/codex/durable/CodexCliProbe.test.js.map +1 -1
  24. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js +3 -1
  25. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.js.map +1 -1
  26. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js +11 -26
  27. package/dist/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.js.map +1 -1
  28. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js +3 -7
  29. package/dist/__tests__/harnesses/codex/durable/CodexPrintRunner.test.js.map +1 -1
  30. package/dist/__tests__/harnesses/devin/DevinAdapter.test.js +597 -0
  31. package/dist/__tests__/harnesses/devin/DevinAdapter.test.js.map +1 -0
  32. package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js +91 -0
  33. package/dist/__tests__/harnesses/devin/DevinAgentMapper.test.js.map +1 -0
  34. package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js +192 -0
  35. package/dist/__tests__/harnesses/devin/DevinSessionLocator.test.js.map +1 -0
  36. package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js +407 -0
  37. package/dist/__tests__/harnesses/devin/DevinSessionParser.test.js.map +1 -0
  38. package/dist/__tests__/harnesses/devin/fixtures.js +95 -0
  39. package/dist/__tests__/harnesses/devin/fixtures.js.map +1 -0
  40. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js +5 -11
  41. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
  42. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
  43. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
  44. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
  45. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
  46. package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +95 -11
  47. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
  48. package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
  49. package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
  50. package/dist/__tests__/print/DurableAgent.test.js +4 -6
  51. package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
  52. package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
  53. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
  54. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
  55. package/dist/__tests__/terminal/GhosttyBackend.test.js +334 -0
  56. package/dist/__tests__/terminal/GhosttyBackend.test.js.map +1 -0
  57. package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
  58. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
  59. package/dist/adapters/AgentAdapter.d.ts +1 -1
  60. package/dist/adapters/AgentAdapter.d.ts.map +1 -1
  61. package/dist/adapters/AgentAdapter.js +2 -1
  62. package/dist/adapters/AgentAdapter.js.map +1 -1
  63. package/dist/capacity/index.d.ts +9 -19
  64. package/dist/capacity/index.d.ts.map +1 -1
  65. package/dist/capacity/index.js +11 -4
  66. package/dist/capacity/index.js.map +1 -1
  67. package/dist/capacity/providers/anthropic.d.ts +9 -0
  68. package/dist/capacity/providers/anthropic.d.ts.map +1 -0
  69. package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
  70. package/dist/capacity/providers/anthropic.js.map +1 -0
  71. package/dist/capacity/providers/openai-platform.d.ts +14 -0
  72. package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
  73. package/dist/capacity/providers/openai-platform.js +120 -0
  74. package/dist/capacity/providers/openai-platform.js.map +1 -0
  75. package/dist/capacity/providers/openai-subscription.d.ts +20 -0
  76. package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
  77. package/dist/capacity/providers/openai-subscription.js +129 -0
  78. package/dist/capacity/providers/openai-subscription.js.map +1 -0
  79. package/dist/capacity/providers/zai.d.ts +8 -0
  80. package/dist/capacity/providers/zai.d.ts.map +1 -0
  81. package/dist/capacity/{zai.js → providers/zai.js} +17 -62
  82. package/dist/capacity/providers/zai.js.map +1 -0
  83. package/dist/capacity/sources/claude.d.ts +9 -0
  84. package/dist/capacity/sources/claude.d.ts.map +1 -0
  85. package/dist/capacity/sources/claude.js +17 -0
  86. package/dist/capacity/sources/claude.js.map +1 -0
  87. package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
  88. package/dist/capacity/sources/codex.d.ts.map +1 -0
  89. package/dist/capacity/{codex.js → sources/codex.js} +31 -84
  90. package/dist/capacity/sources/codex.js.map +1 -0
  91. package/dist/capacity/sources/pi.d.ts +30 -0
  92. package/dist/capacity/sources/pi.d.ts.map +1 -0
  93. package/dist/capacity/sources/pi.js +81 -0
  94. package/dist/capacity/sources/pi.js.map +1 -0
  95. package/dist/capacity/types.d.ts +2 -0
  96. package/dist/capacity/types.d.ts.map +1 -1
  97. package/dist/capacity/types.js +1 -1
  98. package/dist/capacity/types.js.map +1 -1
  99. package/dist/harnesses/claude/credentials.d.ts +10 -0
  100. package/dist/harnesses/claude/credentials.d.ts.map +1 -0
  101. package/dist/harnesses/claude/credentials.js +112 -0
  102. package/dist/harnesses/claude/credentials.js.map +1 -0
  103. package/dist/harnesses/claude/readiness.d.ts +3 -0
  104. package/dist/harnesses/claude/readiness.d.ts.map +1 -0
  105. package/dist/harnesses/claude/readiness.js +63 -0
  106. package/dist/harnesses/claude/readiness.js.map +1 -0
  107. package/dist/harnesses/codex/credentials.d.ts +15 -0
  108. package/dist/harnesses/codex/credentials.d.ts.map +1 -0
  109. package/dist/harnesses/codex/credentials.js +58 -0
  110. package/dist/harnesses/codex/credentials.js.map +1 -0
  111. package/dist/harnesses/codex/readiness.d.ts +3 -0
  112. package/dist/harnesses/codex/readiness.d.ts.map +1 -0
  113. package/dist/harnesses/codex/readiness.js +60 -0
  114. package/dist/harnesses/codex/readiness.js.map +1 -0
  115. package/dist/harnesses/copilot/readiness.d.ts +3 -0
  116. package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
  117. package/dist/harnesses/copilot/readiness.js +39 -0
  118. package/dist/harnesses/copilot/readiness.js.map +1 -0
  119. package/dist/harnesses/devin/DevinAdapter.d.ts +39 -0
  120. package/dist/harnesses/devin/DevinAdapter.d.ts.map +1 -0
  121. package/dist/harnesses/devin/DevinAdapter.js +177 -0
  122. package/dist/harnesses/devin/DevinAdapter.js.map +1 -0
  123. package/dist/harnesses/devin/DevinAgentMapper.d.ts +11 -0
  124. package/dist/harnesses/devin/DevinAgentMapper.d.ts.map +1 -0
  125. package/dist/harnesses/devin/DevinAgentMapper.js +35 -0
  126. package/dist/harnesses/devin/DevinAgentMapper.js.map +1 -0
  127. package/dist/harnesses/devin/DevinSessionLocator.d.ts +46 -0
  128. package/dist/harnesses/devin/DevinSessionLocator.d.ts.map +1 -0
  129. package/dist/harnesses/devin/DevinSessionLocator.js +176 -0
  130. package/dist/harnesses/devin/DevinSessionLocator.js.map +1 -0
  131. package/dist/harnesses/devin/DevinSessionParser.d.ts +29 -0
  132. package/dist/harnesses/devin/DevinSessionParser.d.ts.map +1 -0
  133. package/dist/harnesses/devin/DevinSessionParser.js +166 -0
  134. package/dist/harnesses/devin/DevinSessionParser.js.map +1 -0
  135. package/dist/harnesses/devin/readiness.d.ts +3 -0
  136. package/dist/harnesses/devin/readiness.d.ts.map +1 -0
  137. package/dist/harnesses/devin/readiness.js +39 -0
  138. package/dist/harnesses/devin/readiness.js.map +1 -0
  139. package/dist/harnesses/index.d.ts.map +1 -1
  140. package/dist/harnesses/index.js +3 -1
  141. package/dist/harnesses/index.js.map +1 -1
  142. package/dist/harnesses/opencode/readiness.d.ts +3 -0
  143. package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
  144. package/dist/harnesses/opencode/readiness.js +50 -0
  145. package/dist/harnesses/opencode/readiness.js.map +1 -0
  146. package/dist/harnesses/pi/credentials.d.ts +22 -0
  147. package/dist/harnesses/pi/credentials.d.ts.map +1 -0
  148. package/dist/harnesses/pi/credentials.js +98 -0
  149. package/dist/harnesses/pi/credentials.js.map +1 -0
  150. package/dist/harnesses/pi/readiness.d.ts +3 -0
  151. package/dist/harnesses/pi/readiness.d.ts.map +1 -0
  152. package/dist/harnesses/pi/readiness.js +96 -0
  153. package/dist/harnesses/pi/readiness.js.map +1 -0
  154. package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
  155. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
  156. package/dist/harnesses/readiness/AgentReadiness.js +70 -0
  157. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
  158. package/dist/harnesses/readiness/checks.d.ts +31 -0
  159. package/dist/harnesses/readiness/checks.d.ts.map +1 -0
  160. package/dist/harnesses/readiness/checks.js +270 -0
  161. package/dist/harnesses/readiness/checks.js.map +1 -0
  162. package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
  163. package/dist/harnesses/readiness/types.d.ts.map +1 -0
  164. package/dist/harnesses/readiness/types.js +4 -0
  165. package/dist/harnesses/readiness/types.js.map +1 -0
  166. package/dist/harnesses/runtimeProfiles.d.ts +11 -0
  167. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
  168. package/dist/harnesses/runtimeProfiles.js +100 -0
  169. package/dist/harnesses/runtimeProfiles.js.map +1 -0
  170. package/dist/index.d.ts +16 -42
  171. package/dist/index.d.ts.map +1 -1
  172. package/dist/index.js +9 -23
  173. package/dist/index.js.map +1 -1
  174. package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
  175. package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
  176. package/dist/runtime/ManagedAgentRuntime.js +6 -20
  177. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  178. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
  179. package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
  180. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  181. package/dist/runtime/tmux/PidPolling.d.ts +1 -1
  182. package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
  183. package/dist/runtime/tmux/PidPolling.js.map +1 -1
  184. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
  185. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
  186. package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
  187. package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
  188. package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
  189. package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
  190. package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
  191. package/dist/runtime/tmux/TmuxManager.js.map +1 -0
  192. package/dist/runtime/types.d.ts +1 -1
  193. package/dist/runtime/types.d.ts.map +1 -1
  194. package/dist/runtime/types.js.map +1 -1
  195. package/dist/terminal/TerminalFocusManager.d.ts +4 -32
  196. package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
  197. package/dist/terminal/TerminalFocusManager.js +17 -267
  198. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  199. package/dist/terminal/TtyWriter.d.ts +1 -44
  200. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  201. package/dist/terminal/TtyWriter.js +12 -347
  202. package/dist/terminal/TtyWriter.js.map +1 -1
  203. package/dist/terminal/backends/ghostty.d.ts +3 -0
  204. package/dist/terminal/backends/ghostty.d.ts.map +1 -0
  205. package/dist/terminal/backends/ghostty.js +161 -0
  206. package/dist/terminal/backends/ghostty.js.map +1 -0
  207. package/dist/terminal/backends/index.d.ts +5 -0
  208. package/dist/terminal/backends/index.d.ts.map +1 -0
  209. package/dist/terminal/backends/index.js +21 -0
  210. package/dist/terminal/backends/index.js.map +1 -0
  211. package/dist/terminal/backends/iterm2.d.ts +3 -0
  212. package/dist/terminal/backends/iterm2.d.ts.map +1 -0
  213. package/dist/terminal/backends/iterm2.js +135 -0
  214. package/dist/terminal/backends/iterm2.js.map +1 -0
  215. package/dist/terminal/backends/shared.d.ts +9 -0
  216. package/dist/terminal/backends/shared.d.ts.map +1 -0
  217. package/dist/terminal/backends/shared.js +38 -0
  218. package/dist/terminal/backends/shared.js.map +1 -0
  219. package/dist/terminal/backends/terminal-app.d.ts +3 -0
  220. package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
  221. package/dist/terminal/backends/terminal-app.js +129 -0
  222. package/dist/terminal/backends/terminal-app.js.map +1 -0
  223. package/dist/terminal/backends/tmux.d.ts +3 -0
  224. package/dist/terminal/backends/tmux.d.ts.map +1 -0
  225. package/dist/terminal/backends/tmux.js +76 -0
  226. package/dist/terminal/backends/tmux.js.map +1 -0
  227. package/dist/terminal/backends/wezterm.d.ts +3 -0
  228. package/dist/terminal/backends/wezterm.d.ts.map +1 -0
  229. package/dist/terminal/backends/wezterm.js +71 -0
  230. package/dist/terminal/backends/wezterm.js.map +1 -0
  231. package/dist/terminal/types.d.ts +22 -0
  232. package/dist/terminal/types.d.ts.map +1 -0
  233. package/dist/terminal/types.js +11 -0
  234. package/dist/terminal/types.js.map +1 -0
  235. package/dist/utils/process.d.ts +2 -0
  236. package/dist/utils/process.d.ts.map +1 -1
  237. package/dist/utils/process.js +3 -0
  238. package/dist/utils/process.js.map +1 -1
  239. package/package.json +1 -1
  240. package/src/AgentManager.ts +1 -1
  241. package/src/__tests__/capacity/anthropic.test.ts +105 -0
  242. package/src/__tests__/capacity/boundaries.test.ts +36 -0
  243. package/src/__tests__/capacity/claude.test.ts +3 -8
  244. package/src/__tests__/capacity/codex.test.ts +12 -35
  245. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
  246. package/src/__tests__/capacity/openai.test.ts +15 -15
  247. package/src/__tests__/capacity/wham.test.ts +1 -1
  248. package/src/__tests__/capacity/zai.test.ts +27 -62
  249. package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
  250. package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
  251. package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
  252. package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
  253. package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
  254. package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
  255. package/src/__tests__/harnesses/devin/DevinAdapter.test.ts +292 -0
  256. package/src/__tests__/harnesses/devin/DevinAgentMapper.test.ts +88 -0
  257. package/src/__tests__/harnesses/devin/DevinSessionLocator.test.ts +139 -0
  258. package/src/__tests__/harnesses/devin/DevinSessionParser.test.ts +218 -0
  259. package/src/__tests__/harnesses/devin/fixtures.ts +144 -0
  260. package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
  261. package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
  262. package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
  263. package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +77 -9
  264. package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
  265. package/src/__tests__/print/DurableAgent.test.ts +4 -13
  266. package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
  267. package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
  268. package/src/__tests__/terminal/GhosttyBackend.test.ts +271 -0
  269. package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
  270. package/src/adapters/AgentAdapter.ts +1 -0
  271. package/src/capacity/index.ts +29 -23
  272. package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
  273. package/src/capacity/providers/openai-platform.ts +150 -0
  274. package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
  275. package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
  276. package/src/capacity/sources/claude.ts +30 -0
  277. package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
  278. package/src/capacity/sources/pi.ts +131 -0
  279. package/src/capacity/types.ts +3 -0
  280. package/src/harnesses/claude/credentials.ts +122 -0
  281. package/src/harnesses/claude/readiness.ts +69 -0
  282. package/src/harnesses/codex/credentials.ts +81 -0
  283. package/src/harnesses/codex/readiness.ts +72 -0
  284. package/src/harnesses/copilot/readiness.ts +33 -0
  285. package/src/harnesses/devin/DevinAdapter.ts +224 -0
  286. package/src/harnesses/devin/DevinAgentMapper.ts +43 -0
  287. package/src/harnesses/devin/DevinSessionLocator.ts +230 -0
  288. package/src/harnesses/devin/DevinSessionParser.ts +236 -0
  289. package/src/harnesses/devin/readiness.ts +36 -0
  290. package/src/harnesses/index.ts +2 -0
  291. package/src/harnesses/opencode/readiness.ts +48 -0
  292. package/src/harnesses/pi/credentials.ts +129 -0
  293. package/src/harnesses/pi/readiness.ts +104 -0
  294. package/src/harnesses/readiness/AgentReadiness.ts +89 -0
  295. package/src/harnesses/readiness/checks.ts +336 -0
  296. package/src/harnesses/readiness/types.ts +90 -0
  297. package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +35 -38
  298. package/src/index.ts +15 -128
  299. package/src/runtime/ManagedAgentRuntime.ts +7 -22
  300. package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
  301. package/src/runtime/tmux/PidPolling.ts +1 -1
  302. package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
  303. package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
  304. package/src/runtime/types.ts +1 -1
  305. package/src/terminal/TerminalFocusManager.ts +19 -292
  306. package/src/terminal/TtyWriter.ts +15 -335
  307. package/src/terminal/backends/ghostty.ts +180 -0
  308. package/src/terminal/backends/index.ts +23 -0
  309. package/src/terminal/backends/iterm2.ts +126 -0
  310. package/src/terminal/backends/shared.ts +47 -0
  311. package/src/terminal/backends/terminal-app.ts +122 -0
  312. package/src/terminal/backends/tmux.ts +62 -0
  313. package/src/terminal/backends/wezterm.ts +65 -0
  314. package/src/terminal/types.ts +23 -0
  315. package/src/utils/process.ts +5 -0
  316. package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
  317. package/dist/__tests__/utils/agents.test.js +0 -93
  318. package/dist/__tests__/utils/agents.test.js.map +0 -1
  319. package/dist/capacity/claude.d.ts +0 -17
  320. package/dist/capacity/claude.d.ts.map +0 -1
  321. package/dist/capacity/claude.js.map +0 -1
  322. package/dist/capacity/codex.d.ts.map +0 -1
  323. package/dist/capacity/codex.js.map +0 -1
  324. package/dist/capacity/openai.d.ts +0 -47
  325. package/dist/capacity/openai.d.ts.map +0 -1
  326. package/dist/capacity/openai.js +0 -307
  327. package/dist/capacity/openai.js.map +0 -1
  328. package/dist/capacity/wham.d.ts +0 -16
  329. package/dist/capacity/wham.d.ts.map +0 -1
  330. package/dist/capacity/wham.js +0 -66
  331. package/dist/capacity/wham.js.map +0 -1
  332. package/dist/capacity/zai.d.ts +0 -14
  333. package/dist/capacity/zai.d.ts.map +0 -1
  334. package/dist/capacity/zai.js.map +0 -1
  335. package/dist/readiness/AgentReadiness.d.ts.map +0 -1
  336. package/dist/readiness/AgentReadiness.js +0 -614
  337. package/dist/readiness/AgentReadiness.js.map +0 -1
  338. package/dist/terminal/TmuxManager.d.ts +0 -28
  339. package/dist/terminal/TmuxManager.d.ts.map +0 -1
  340. package/dist/terminal/TmuxManager.js.map +0 -1
  341. package/dist/terminal/index.d.ts +0 -5
  342. package/dist/terminal/index.d.ts.map +0 -1
  343. package/dist/terminal/index.js +0 -5
  344. package/dist/terminal/index.js.map +0 -1
  345. package/dist/utils/agents.d.ts +0 -16
  346. package/dist/utils/agents.d.ts.map +0 -1
  347. package/dist/utils/agents.js +0 -117
  348. package/dist/utils/agents.js.map +0 -1
  349. package/dist/utils/index.d.ts +0 -8
  350. package/dist/utils/index.d.ts.map +0 -1
  351. package/dist/utils/index.js +0 -6
  352. package/dist/utils/index.js.map +0 -1
  353. package/src/__tests__/utils/agents.test.ts +0 -105
  354. package/src/capacity/openai.ts +0 -355
  355. package/src/readiness/AgentReadiness.ts +0 -730
  356. package/src/terminal/index.ts +0 -4
  357. package/src/utils/index.ts +0 -14
@@ -1,21 +1,23 @@
1
1
  import path from "node:path";
2
2
  import { describe, expect, it, vi } from "vitest";
3
3
  import {
4
- AGENTS,
5
4
  getAgentReadinessReport,
6
5
  getAgentReadinessReports,
7
6
  type AgentReadinessOptions,
8
7
  type ReadinessAgentType,
9
- } from "../../index.js";
8
+ } from "../../../harnesses/readiness/AgentReadiness.js";
9
+ import { HARNESS_RUNTIME_PROFILES } from "../../../harnesses/runtimeProfiles.js";
10
10
 
11
11
  type Files = Record<string, string>;
12
12
 
13
13
  const builtInSkillNames = ["agent-management", "memory", "verify"];
14
- const skillRoots: Record<ReadinessAgentType, string> = {
14
+ const skillRoots: Record<string, string> = {
15
15
  claude: ".claude/skills",
16
16
  codex: ".codex/skills",
17
17
  copilot: ".copilot/skills",
18
+ gemini_cli: ".gemini/skills",
18
19
  grok_cli: ".grok/skills",
20
+ antigravity_cli: ".gemini/config/skills",
19
21
  opencode: ".config/opencode/skill",
20
22
  pi: ".pi/agent/skills",
21
23
  };
@@ -56,7 +58,18 @@ function fixture(overrides: Partial<AgentReadinessOptions> = {}) {
56
58
  litellm: { key: "litellm-secret", env: { LITELLM_BASE_URL: "https://litellm.example.test" } },
57
59
  }),
58
60
  };
59
- for (const directory of [".claude", ".codex", ".copilot", ".grok", ".config/opencode", ".pi"]) {
61
+ for (const directory of [
62
+ ".claude",
63
+ ".codex",
64
+ ".copilot",
65
+ ".gemini",
66
+ ".grok",
67
+ ".kiro",
68
+ ".gemini/antigravity-cli",
69
+ ".config/opencode",
70
+ ".config/devin",
71
+ ".pi",
72
+ ]) {
60
73
  files[path.join(homeDir, directory)] = "<dir>";
61
74
  }
62
75
  for (const root of Object.values(skillRoots)) {
@@ -64,7 +77,9 @@ function fixture(overrides: Partial<AgentReadinessOptions> = {}) {
64
77
  files[path.join(homeDir, root, skill, "SKILL.md")] = "# skill";
65
78
  }
66
79
  }
67
- const executablePaths = Object.values(AGENTS).map((agent) => path.join("/bin", agent.command));
80
+ const executablePaths = Object.values(HARNESS_RUNTIME_PROFILES).map((agent) =>
81
+ path.join("/bin", agent.command),
82
+ );
68
83
  const options: AgentReadinessOptions = {
69
84
  homeDir,
70
85
  path: "/bin",
@@ -86,6 +101,8 @@ function fixture(overrides: Partial<AgentReadinessOptions> = {}) {
86
101
  return { stdout: "github.com\n Logged in to github.com account test-user\n", stderr: "" };
87
102
  if (command === "opencode")
88
103
  return { stdout: "● litellm api\n● OpenAI oauth\n", stderr: "" };
104
+ if (command === "devin")
105
+ return { stdout: "Logged in (via Devin).\n\nUser:\n Name: test-user\n", stderr: "" };
89
106
  throw new Error(`unexpected command ${command}`);
90
107
  }),
91
108
  codexAuth: async () => true,
@@ -103,20 +120,31 @@ describe("agent readiness", () => {
103
120
  expect(Object.keys(reports)).toEqual([
104
121
  "claude",
105
122
  "codex",
106
- "copilot",
123
+ "gemini_cli",
107
124
  "grok_cli",
125
+ "kiro",
126
+ "antigravity_cli",
108
127
  "opencode",
128
+ "copilot",
109
129
  "pi",
130
+ "devin",
110
131
  ]);
111
- expect(reports).not.toHaveProperty("gemini_cli");
112
132
  for (const [agent, report] of Object.entries(reports) as Array<
113
133
  [ReadinessAgentType, (typeof reports)[ReadinessAgentType]]
114
134
  >) {
115
135
  expect(report.type).toBe(agent);
116
136
  expect(report.executable.status).toBe("pass");
117
137
  expect(report.globalConfig.status).toBe("pass");
118
- expect(report.builtInSkills).toMatchObject({ status: "info", present: 3, required: 3 });
138
+ expect(report.builtInSkills.status).toBe("info");
119
139
  }
140
+ expect(reports.gemini_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });
141
+ expect(reports.antigravity_cli.builtInSkills).toMatchObject({ present: 3, required: 3 });
142
+ expect(reports.kiro.builtInSkills).toMatchObject({
143
+ path: null,
144
+ present: 0,
145
+ required: 3,
146
+ missing: builtInSkillNames,
147
+ });
120
148
  expect(reports.codex.integration).toMatchObject({
121
149
  label: "ai-devkit hook",
122
150
  installed: true,
@@ -133,13 +161,16 @@ describe("agent readiness", () => {
133
161
  status: "pass",
134
162
  });
135
163
  expect(reports.copilot.integration).toBeUndefined();
164
+ expect(reports.gemini_cli.integration).toBeUndefined();
136
165
  expect(reports.grok_cli.integration).toBeUndefined();
166
+ expect(reports.kiro.integration).toBeUndefined();
167
+ expect(reports.antigravity_cli.integration).toBeUndefined();
137
168
  expect(reports.opencode.integration).toBeUndefined();
138
169
  });
139
170
 
140
171
  it("resolves agent executables from PATH directories containing spaces", async () => {
141
172
  const spacedBin = "/Applications/Some App.app/Contents/Resources";
142
- const codexPath = path.join(spacedBin, AGENTS.codex.command);
173
+ const codexPath = path.join(spacedBin, HARNESS_RUNTIME_PROFILES.codex.command);
143
174
  const { options } = fixture({
144
175
  path: [spacedBin, "/usr/local/bin"].join(path.delimiter),
145
176
  access: async (target) => {
@@ -255,4 +286,41 @@ describe("agent readiness", () => {
255
286
  status: "pass",
256
287
  });
257
288
  });
289
+
290
+ it("checks Devin auth from devin auth status output", async () => {
291
+ const { options } = fixture({
292
+ runCommand: vi.fn(async (command) => {
293
+ if (command === "devin")
294
+ return { stdout: "Logged in (via Devin).\n\nUser:\n Name: test-user\n", stderr: "" };
295
+ if (command === "pi") return { stdout: "npm:@ai-devkit/pi-session-tracker\n", stderr: "" };
296
+ if (command === "claude") return { stdout: JSON.stringify({ loggedIn: true }), stderr: "" };
297
+ throw new Error(`unexpected command ${command}`);
298
+ }),
299
+ });
300
+
301
+ const report = await getAgentReadinessReport("devin", options);
302
+
303
+ expect(report.auth).toMatchObject({
304
+ state: "authenticated",
305
+ source: "devin auth status",
306
+ provider: null,
307
+ availableProviders: ["devin"],
308
+ status: "pass",
309
+ });
310
+ });
311
+
312
+ it("reports Devin as unauthenticated without a login line", async () => {
313
+ const { options } = fixture({
314
+ runCommand: vi.fn(async (command) => {
315
+ if (command === "devin") return { stdout: "Not logged in\n", stderr: "" };
316
+ if (command === "pi") return { stdout: "npm:@ai-devkit/pi-session-tracker\n", stderr: "" };
317
+ if (command === "claude") return { stdout: JSON.stringify({ loggedIn: true }), stderr: "" };
318
+ throw new Error(`unexpected command ${command}`);
319
+ }),
320
+ });
321
+
322
+ const report = await getAgentReadinessReport("devin", options);
323
+
324
+ expect(report.auth).toMatchObject({ state: "unauthenticated", status: "fail" });
325
+ });
258
326
  });
@@ -0,0 +1,110 @@
1
+ import fs from "node:fs";
2
+ import os from "node:os";
3
+ import path from "node:path";
4
+ import { afterEach, beforeEach, describe, expect, it } from "vitest";
5
+ import {
6
+ HARNESS_RUNTIME_PROFILES,
7
+ runtimeAgentMatchesHarness,
8
+ } from "../../harnesses/runtimeProfiles.js";
9
+
10
+ describe("harness runtime profiles", () => {
11
+ it("keeps launch and runtime identity metadata together", () => {
12
+ expect(HARNESS_RUNTIME_PROFILES.antigravity_cli).toMatchObject({
13
+ command: "agy",
14
+ runtimeKind: "agy",
15
+ discoveryAliases: ["agy", "antigravity", "antigravity_cli"],
16
+ });
17
+ expect(HARNESS_RUNTIME_PROFILES.antigravity_cli.matches("/usr/local/bin/agy")).toBe(true);
18
+ });
19
+
20
+ it("recognizes agent commands without matching similar argument text", () => {
21
+ expect(HARNESS_RUNTIME_PROFILES.copilot.matches("/opt/copilot-cli/1.0/copilot")).toBe(true);
22
+ expect(HARNESS_RUNTIME_PROFILES.copilot.matches("node /repo/copilot-cli/script.js")).toBe(
23
+ false,
24
+ );
25
+ expect(HARNESS_RUNTIME_PROFILES.grok_cli.matches("/Users/dev/.grok/bin/grok --approve")).toBe(
26
+ true,
27
+ );
28
+ expect(HARNESS_RUNTIME_PROFILES.grok_cli.matches("node /repo/grok-cli/script.js")).toBe(false);
29
+ expect(
30
+ HARNESS_RUNTIME_PROFILES.gemini_cli.matches("node --no-warnings /usr/local/bin/gemini"),
31
+ ).toBe(true);
32
+ });
33
+
34
+ it("matches runtime discovery names through the owning harness profile", () => {
35
+ expect(runtimeAgentMatchesHarness("kiro-cli", "kiro")).toBe(true);
36
+ expect(runtimeAgentMatchesHarness("gemini", "gemini_cli")).toBe(true);
37
+ expect(runtimeAgentMatchesHarness("codex", "claude")).toBe(false);
38
+ });
39
+ });
40
+
41
+ describe("harness matchers with executables under paths containing spaces", () => {
42
+ let root: string;
43
+
44
+ function install(relativePath: string): string {
45
+ const fullPath = path.join(root, relativePath);
46
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
47
+ fs.writeFileSync(fullPath, "");
48
+ return fullPath;
49
+ }
50
+
51
+ beforeEach(() => {
52
+ root = fs.mkdtempSync(path.join(os.tmpdir(), "runtime-profiles-spaces-"));
53
+ });
54
+
55
+ afterEach(() => {
56
+ fs.rmSync(root, { recursive: true, force: true });
57
+ });
58
+
59
+ it("recognizes argv[0] under a spaced directory", () => {
60
+ const codex = install("Applications/Some App.app/Contents/Resources/codex");
61
+ const claude = install("Users/dev/My Tools/bin/claude");
62
+
63
+ expect(HARNESS_RUNTIME_PROFILES.codex.matches(`${codex} --sandbox workspace-write`)).toBe(true);
64
+ expect(HARNESS_RUNTIME_PROFILES.codex.matches(codex)).toBe(true);
65
+ expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${claude} --continue`)).toBe(true);
66
+ expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${codex} exec --cd /repo`)).toBe(false);
67
+ });
68
+
69
+ it("recognizes argv[0] by distribution name under a spaced directory", () => {
70
+ const copilot = install("opt/Home Brew/Caskroom/copilot-cli/1.0.60/copilot");
71
+
72
+ expect(HARNESS_RUNTIME_PROFILES.copilot.matches(`${copilot} --allow-all-tools`)).toBe(true);
73
+ });
74
+
75
+ it("does not treat arguments containing spaces as the executable", () => {
76
+ const vim = install("usr/bin/vim");
77
+ const codex = install("Applications/Some App.app/Contents/Resources/codex");
78
+
79
+ expect(HARNESS_RUNTIME_PROFILES.claude.matches(`${vim} My Notes/claude`)).toBe(false);
80
+ expect(
81
+ HARNESS_RUNTIME_PROFILES.claude.matches(`${codex} exec review the Other Dir/claude`),
82
+ ).toBe(false);
83
+ expect(HARNESS_RUNTIME_PROFILES.copilot.matches(`${vim} Some Dir/copilot-cli/notes.md`)).toBe(
84
+ false,
85
+ );
86
+ expect(HARNESS_RUNTIME_PROFILES.claude.matches("/usr/bin/vim My Notes/claude")).toBe(false);
87
+ });
88
+
89
+ it("recognizes spaced argv[0] in script-aware matchers", () => {
90
+ const pi = install("Users/dev/Node Tools/bin/pi");
91
+ const kiro = install("Applications/Kiro CLI.app/Contents/MacOS/kiro-cli");
92
+ const node = install("Applications/Dev Tools/node/bin/node");
93
+
94
+ expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${pi} --model x`)).toBe(true);
95
+ expect(HARNESS_RUNTIME_PROFILES.kiro.matches(`${kiro} chat`)).toBe(true);
96
+ expect(
97
+ HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /Users/dev/.npm/bin/gemini --yolo`),
98
+ ).toBe(true);
99
+ expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} /opt/pi-tools/script.js`)).toBe(false);
100
+ });
101
+
102
+ it("does not match agent words inside an executable directory", () => {
103
+ const node = install("opt/Dev pi gemini/bin/node");
104
+
105
+ expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} /repo/script.js`)).toBe(false);
106
+ expect(HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /repo/script.js`)).toBe(false);
107
+ expect(HARNESS_RUNTIME_PROFILES.gemini_cli.matches(`${node} /repo/bin/gemini`)).toBe(true);
108
+ expect(HARNESS_RUNTIME_PROFILES.pi.matches(`${node} --title pi`)).toBe(true);
109
+ });
110
+ });
@@ -1,18 +1,9 @@
1
1
  import { describe, expect, it } from "vitest";
2
+ import { DurableAgentBusyError } from "../../durable/DurableAgent.js";
2
3
 
3
- describe("durable-agent public domain", () => {
4
- it("exports a classified busy error without exposing prompt data", async () => {
5
- const api = (await import("../../index.js")) as Record<string, unknown>;
6
-
7
- expect(api).toHaveProperty("DurableAgentBusyError");
8
- const ErrorType = api.DurableAgentBusyError as new (
9
- agentId: string,
10
- name: string,
11
- ) => Error & {
12
- code: string;
13
- agentId: string;
14
- };
15
- const error = new ErrorType("agent-id", "reviewer");
4
+ describe("durable-agent errors", () => {
5
+ it("classifies a busy error without exposing prompt data", () => {
6
+ const error = new DurableAgentBusyError("agent-id", "reviewer");
16
7
 
17
8
  expect(error).toMatchObject({
18
9
  name: "DurableAgentBusyError",
@@ -2,6 +2,7 @@ import fs from "fs";
2
2
  import os from "os";
3
3
  import path from "path";
4
4
  import { afterEach, describe, expect, it } from "vitest";
5
+ import { DurableAgentRepository } from "../../durable/DurableAgentRepository.js";
5
6
 
6
7
  const tempDirs: string[] = [];
7
8
 
@@ -9,12 +10,6 @@ afterEach(() => {
9
10
  for (const dir of tempDirs.splice(0)) fs.rmSync(dir, { recursive: true, force: true });
10
11
  });
11
12
 
12
- async function loadStore(): Promise<any> {
13
- const api = (await import("../../index.js")) as Record<string, unknown>;
14
- expect(api).toHaveProperty("DurableAgentRepository");
15
- return api.DurableAgentRepository;
16
- }
17
-
18
13
  function fixture(): { root: string; cwd: string; dbPath: string } {
19
14
  const root = fs.mkdtempSync(path.join(os.tmpdir(), "durable-agent-store-"));
20
15
  tempDirs.push(root);
@@ -25,7 +20,6 @@ function fixture(): { root: string; cwd: string; dbPath: string } {
25
20
 
26
21
  describe("DurableAgentRepository create/list/resolve", () => {
27
22
  it("creates distinct durable identities with a canonical cwd and lists them", async () => {
28
- const DurableAgentRepository = await loadStore();
29
23
  const { cwd, dbPath } = fixture();
30
24
  const repository = new DurableAgentRepository({
31
25
  dbPath,
@@ -51,7 +45,6 @@ describe("DurableAgentRepository create/list/resolve", () => {
51
45
  });
52
46
 
53
47
  it("resolves exact ids and names and rejects duplicate names", async () => {
54
- const DurableAgentRepository = await loadStore();
55
48
  const { cwd, dbPath } = fixture();
56
49
  const repository = new DurableAgentRepository({ dbPath });
57
50
  const agent = await repository.create({ name: "Reviewer", cwd });
@@ -65,7 +58,6 @@ describe("DurableAgentRepository create/list/resolve", () => {
65
58
  });
66
59
 
67
60
  it("rejects a missing cwd", async () => {
68
- const DurableAgentRepository = await loadStore();
69
61
  const { root, dbPath } = fixture();
70
62
  const repository = new DurableAgentRepository({ dbPath });
71
63
 
@@ -77,7 +69,6 @@ describe("DurableAgentRepository create/list/resolve", () => {
77
69
 
78
70
  describe("DurableAgentRepository run ownership", () => {
79
71
  it("fails fast when another exact owner is live and completes only for its token", async () => {
80
- const DurableAgentRepository = await loadStore();
81
72
  const { cwd, dbPath } = fixture();
82
73
  const live = new Map<number, string>([[process.pid, "owner-start"]]);
83
74
  const processInspector = {
@@ -112,7 +103,6 @@ describe("DurableAgentRepository run ownership", () => {
112
103
  });
113
104
 
114
105
  it("retains busy for a live provider then recovers a dead run without signaling it", async () => {
115
- const DurableAgentRepository = await loadStore();
116
106
  const { cwd, dbPath } = fixture();
117
107
  const live = new Map<number, string>([
118
108
  [process.pid, "owner-start"],
@@ -151,7 +141,6 @@ describe("DurableAgentRepository run ownership", () => {
151
141
  });
152
142
 
153
143
  it("reconciles an interrupted run to degraded during list", async () => {
154
- const DurableAgentRepository = await loadStore();
155
144
  const { cwd, dbPath } = fixture();
156
145
  const live = new Map<number, string>([[process.pid, "owner-start"]]);
157
146
  const repository = new DurableAgentRepository({
@@ -179,7 +168,6 @@ describe("DurableAgentRepository run ownership", () => {
179
168
  });
180
169
 
181
170
  it("rejects send acquisition when the bound cwd is replaced by a symlink", async () => {
182
- const DurableAgentRepository = await loadStore();
183
171
  const { root, cwd, dbPath } = fixture();
184
172
  const repository = new DurableAgentRepository({ dbPath });
185
173
  const agent = await repository.create({ name: "bound", cwd });
@@ -15,7 +15,7 @@ import {
15
15
  } from "../../runtime/ManagedAgentRuntime.js";
16
16
  import type { AgentInfo } from "../../adapters/AgentAdapter.js";
17
17
  import type { AgentRegistry, RegistryEntry } from "../../utils/AgentRegistry.js";
18
- import type { TmuxManager } from "../../terminal/TmuxManager.js";
18
+ import type { TmuxManager } from "../../runtime/tmux/TmuxManager.js";
19
19
  import type { InteractiveAgentRuntime } from "../../runtime/types.js";
20
20
 
21
21
  function makeAgent(overrides: Partial<AgentInfo> = {}): AgentInfo {
@@ -0,0 +1,271 @@
1
+ import { execFile } from "child_process";
2
+ import { writeFile } from "fs/promises";
3
+ import type { Mock } from "vitest";
4
+
5
+ import { ghosttyBackend } from "../../terminal/backends/ghostty.js";
6
+ import { TerminalType, type TerminalLocation } from "../../terminal/types.js";
7
+ import { TtyWriter } from "../../terminal/TtyWriter.js";
8
+ import { getProcessCwdsAsync } from "../../utils/process.js";
9
+
10
+ vi.mock("child_process", async () => {
11
+ const actual = await vi.importActual<typeof import("child_process")>("child_process");
12
+ return { ...actual, execFile: vi.fn() };
13
+ });
14
+
15
+ vi.mock("fs/promises", async () => {
16
+ const actual = await vi.importActual<typeof import("fs/promises")>("fs/promises");
17
+ return { ...actual, writeFile: vi.fn() };
18
+ });
19
+
20
+ vi.mock("../../utils/process.js", async () => {
21
+ const actual =
22
+ await vi.importActual<typeof import("../../utils/process.js")>("../../utils/process.js");
23
+ return { ...actual, getProcessCwdsAsync: vi.fn() };
24
+ });
25
+
26
+ const mockedExecFile = execFile as unknown as Mock;
27
+ const mockedGetProcessCwds = getProcessCwdsAsync as unknown as Mock;
28
+ const mockedWriteFile = writeFile as unknown as Mock;
29
+ const SEP = "\x1f";
30
+
31
+ interface FakeTerminal {
32
+ id: string;
33
+ name: string;
34
+ dir: string;
35
+ }
36
+
37
+ /**
38
+ * Fake `ps`, process cwds and Ghostty AppleScript. `processes` rows are
39
+ * `pid ppid tty comm`; `cwds` maps pid to its working directory. Writing an
40
+ * OSC 2 title to a TTY renames the terminal listed in `ttyOwner`.
41
+ */
42
+ function fakeSystem(opts: {
43
+ processes: string[];
44
+ cwds?: Record<number, string>;
45
+ terminals: FakeTerminal[];
46
+ ttyOwner?: Record<string, string>;
47
+ osascriptResult?: string;
48
+ }) {
49
+ const terminals = opts.terminals.map((t) => ({ ...t }));
50
+ mockedGetProcessCwds.mockImplementation(async (pids: number[]) => {
51
+ const cwds = new Map<number, string>();
52
+ for (const pid of pids) if (opts.cwds?.[pid]) cwds.set(pid, opts.cwds[pid]);
53
+ return cwds;
54
+ });
55
+ mockedWriteFile.mockImplementation(async (tty: string, data: string) => {
56
+ // eslint-disable-next-line no-control-regex
57
+ const title = /^\x1b\]2;(.*)\x07$/.exec(data)?.[1];
58
+ const owner = terminals.find((t) => t.id === opts.ttyOwner?.[tty]);
59
+ if (owner && title !== undefined) owner.name = title;
60
+ });
61
+ mockedExecFile.mockImplementation((...args: unknown[]) => {
62
+ const [cmd, argv] = args as [string, string[]];
63
+ const cb = args[args.length - 1] as (err: Error | null, r?: { stdout: string }) => void;
64
+ let stdout = "";
65
+ if (cmd === "ps") stdout = opts.processes.join("\n") + "\n";
66
+ else if (cmd === "osascript" && argv[1].includes("repeat with t in terminals")) {
67
+ stdout = terminals.map((t) => [t.id, t.name, t.dir].join(SEP)).join("\n") + "\n";
68
+ } else if (cmd === "osascript") stdout = opts.osascriptResult ?? "ok\n";
69
+ cb(null, { stdout });
70
+ return {};
71
+ });
72
+ return terminals;
73
+ }
74
+
75
+ const GHOSTTY = "500 1 ?? /Applications/Ghostty.app/Contents/MacOS/ghostty";
76
+
77
+ describe("Ghostty terminal backend", () => {
78
+ beforeEach(() => {
79
+ vi.clearAllMocks();
80
+ });
81
+
82
+ describe("find", () => {
83
+ it("ignores TTYs when Ghostty is not running", async () => {
84
+ fakeSystem({ processes: ["600 1 ttys001 /usr/bin/login"], terminals: [] });
85
+
86
+ expect(await ghosttyBackend.find("/dev/ttys001")).toBeNull();
87
+ expect(mockedExecFile).not.toHaveBeenCalledWith(
88
+ "osascript",
89
+ expect.anything(),
90
+ expect.anything(),
91
+ );
92
+ });
93
+
94
+ it("ignores TTYs whose session was not started by Ghostty", async () => {
95
+ fakeSystem({
96
+ processes: [GHOSTTY, "600 500 ttys001 /usr/bin/login", "700 1 ttys002 /usr/bin/login"],
97
+ terminals: [{ id: "T1", name: "zsh", dir: "/repo" }],
98
+ });
99
+
100
+ expect(await ghosttyBackend.find("/dev/ttys002")).toBeNull();
101
+ expect(mockedWriteFile).not.toHaveBeenCalled();
102
+ });
103
+
104
+ it("matches by working directory when it pairs one terminal with one TTY", async () => {
105
+ fakeSystem({
106
+ processes: [
107
+ GHOSTTY,
108
+ "600 500 ttys001 /usr/bin/login",
109
+ "601 600 ttys001 -zsh",
110
+ "602 601 ttys001 claude",
111
+ "700 500 ttys002 /usr/bin/login",
112
+ "701 700 ttys002 -zsh",
113
+ ],
114
+ cwds: { 601: "/repo/a", 602: "/repo/a", 701: "/repo/b" },
115
+ terminals: [
116
+ { id: "T1", name: "claude", dir: "/repo/a" },
117
+ { id: "T2", name: "zsh", dir: "/repo/b" },
118
+ ],
119
+ });
120
+
121
+ expect(await ghosttyBackend.find("/dev/ttys001")).toEqual({
122
+ type: TerminalType.GHOSTTY,
123
+ identifier: "T1",
124
+ tty: "/dev/ttys001",
125
+ });
126
+ expect(mockedWriteFile).not.toHaveBeenCalled();
127
+ });
128
+
129
+ it("uses a title marker when two terminals share the directory, then restores the title", async () => {
130
+ const terminals = fakeSystem({
131
+ processes: [
132
+ GHOSTTY,
133
+ "600 500 ttys001 /usr/bin/login",
134
+ "601 600 ttys001 claude",
135
+ "700 500 ttys002 /usr/bin/login",
136
+ "701 700 ttys002 codex",
137
+ ],
138
+ cwds: { 601: "/repo", 701: "/repo" },
139
+ terminals: [
140
+ { id: "T1", name: "first agent", dir: "/repo" },
141
+ { id: "T2", name: "second agent", dir: "/repo" },
142
+ ],
143
+ ttyOwner: { "/dev/ttys001": "T1", "/dev/ttys002": "T2" },
144
+ });
145
+
146
+ const location = await ghosttyBackend.find("/dev/ttys002");
147
+
148
+ expect(location?.identifier).toBe("T2");
149
+ const titles = mockedWriteFile.mock.calls.map(([tty, data]) => [tty, data]);
150
+ expect(titles).toHaveLength(2);
151
+ expect(titles[0][0]).toBe("/dev/ttys002");
152
+ // eslint-disable-next-line no-control-regex
153
+ expect(titles[0][1]).toMatch(/^\x1b\]2;ai-devkit-\d+-\d+\x07$/);
154
+ expect(titles[1]).toEqual(["/dev/ttys002", "\x1b]2;second agent\x07"]);
155
+ expect(terminals.find((t) => t.id === "T2")?.name).toBe("second agent");
156
+ });
157
+
158
+ it("does not trust a unique directory match when another Ghostty TTY has a process there", async () => {
159
+ // T1's own working directory is unreported; T2 sits in the agent's directory.
160
+ fakeSystem({
161
+ processes: [
162
+ GHOSTTY,
163
+ "600 500 ttys001 /usr/bin/login",
164
+ "601 600 ttys001 claude",
165
+ "700 500 ttys002 /usr/bin/login",
166
+ "701 700 ttys002 -zsh",
167
+ ],
168
+ cwds: { 601: "/repo", 701: "/repo" },
169
+ terminals: [
170
+ { id: "T1", name: "claude", dir: "" },
171
+ { id: "T2", name: "zsh", dir: "/repo" },
172
+ ],
173
+ ttyOwner: { "/dev/ttys001": "T1", "/dev/ttys002": "T2" },
174
+ });
175
+
176
+ expect((await ghosttyBackend.find("/dev/ttys001"))?.identifier).toBe("T1");
177
+ expect(mockedWriteFile).toHaveBeenCalled();
178
+ });
179
+
180
+ it("returns null when no terminal shows the marker", async () => {
181
+ vi.useFakeTimers();
182
+ try {
183
+ fakeSystem({
184
+ processes: [GHOSTTY, "600 500 ttys001 /usr/bin/login"],
185
+ terminals: [
186
+ { id: "T1", name: "a", dir: "/x" },
187
+ { id: "T2", name: "b", dir: "/y" },
188
+ ],
189
+ });
190
+
191
+ const result = ghosttyBackend.find("/dev/ttys001");
192
+ await vi.runAllTimersAsync();
193
+
194
+ expect(await result).toBeNull();
195
+ } finally {
196
+ vi.useRealTimers();
197
+ }
198
+ });
199
+
200
+ it("returns null instead of throwing when Ghostty is not scriptable", async () => {
201
+ mockedGetProcessCwds.mockResolvedValue(new Map());
202
+ mockedExecFile.mockImplementation((...args: unknown[]) => {
203
+ const [cmd] = args as [string];
204
+ const cb = args[args.length - 1] as (err: Error | null, r?: { stdout: string }) => void;
205
+ if (cmd === "osascript") cb(new Error("Not authorized to send Apple events"));
206
+ else cb(null, { stdout: `${GHOSTTY}\n600 500 ttys001 /usr/bin/login\n` });
207
+ return {};
208
+ });
209
+
210
+ expect(await ghosttyBackend.find("/dev/ttys001")).toBeNull();
211
+ });
212
+ });
213
+
214
+ describe("focus, send and sendKey", () => {
215
+ const location: TerminalLocation = {
216
+ type: TerminalType.GHOSTTY,
217
+ identifier: "T1",
218
+ tty: "/dev/ttys001",
219
+ };
220
+ const scripts = () =>
221
+ mockedExecFile.mock.calls.filter(([cmd]) => cmd === "osascript").map(([, argv]) => argv[1]);
222
+
223
+ it("focuses the terminal by id and activates Ghostty", async () => {
224
+ fakeSystem({ processes: [], terminals: [] });
225
+
226
+ expect(await ghosttyBackend.focus(location)).toBe(true);
227
+ expect(scripts()[0]).toContain('every terminal whose id is "T1"');
228
+ expect(scripts()[0]).toContain("focus t");
229
+ expect(scripts()[0]).toContain("activate");
230
+ });
231
+
232
+ it("reports a failed focus when the terminal is gone", async () => {
233
+ fakeSystem({ processes: [], terminals: [], osascriptResult: "not_found\n" });
234
+
235
+ expect(await ghosttyBackend.focus(location)).toBe(false);
236
+ });
237
+
238
+ it("pastes the message with input text, then types a carriage return", async () => {
239
+ fakeSystem({ processes: [], terminals: [] });
240
+
241
+ await TtyWriter.send(location, 'say "hi"\nnext');
242
+
243
+ expect(scripts()).toHaveLength(2);
244
+ expect(scripts()[0]).toContain('input text "say \\"hi\\"\\nnext" to t');
245
+ expect(scripts()[1]).toContain('perform action "text:\\\\x0d" on t');
246
+ });
247
+
248
+ it("throws when the terminal is gone", async () => {
249
+ fakeSystem({ processes: [], terminals: [], osascriptResult: "not_found\n" });
250
+
251
+ await expect(TtyWriter.send(location, "hello")).rejects.toThrow(
252
+ "Ghostty terminal not found for TTY /dev/ttys001",
253
+ );
254
+ });
255
+
256
+ // Ghostty parses `text:` with Zig escapes; AppleScript doubles each backslash.
257
+ it.each([
258
+ ["2", "text:2"],
259
+ ["\x1b", "text:\\\\x1b"],
260
+ ["\r", "text:\\\\x0d"],
261
+ ["\\", "text:\\\\\\\\"],
262
+ ])("types key %j with the Ghostty action %s", async (key, action) => {
263
+ fakeSystem({ processes: [], terminals: [] });
264
+
265
+ await TtyWriter.sendKey(location, key);
266
+
267
+ expect(scripts()[0]).toContain(`perform action "${action}" on t`);
268
+ expect(scripts()[0]).not.toContain("send key");
269
+ });
270
+ });
271
+ });
@@ -1,6 +1,6 @@
1
1
  import { execFile } from "child_process";
2
2
  import type { MockedFunction } from "vitest";
3
- import { TmuxManager } from "../../terminal/TmuxManager.js";
3
+ import { TmuxManager } from "../../runtime/tmux/TmuxManager.js";
4
4
 
5
5
  vi.mock("child_process", () => ({
6
6
  execFile: vi.fn(),
@@ -16,6 +16,7 @@ export const AGENT_TYPES = [
16
16
  "opencode",
17
17
  "copilot",
18
18
  "pi",
19
+ "devin",
19
20
  ] as const;
20
21
 
21
22
  /**