@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 +1 @@
1
- {"version":3,"sources":["../../src/utils/process.ts"],"sourcesContent":["/**\n * Process Detection Utilities\n *\n * Shared shell command wrappers for detecting and inspecting running processes.\n * Built-in refresh discovery uses captureProcessSnapshot(); synchronous helpers\n * remain exported for compatibility with existing consumers.\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { execFile, execFileSync } from \"child_process\";\nimport type { ProcessInfo } from \"../adapters/AgentAdapter.js\";\n\nconst PROCESS_EXEC_MAX_BUFFER = 10 * 1024 * 1024;\nconst VALID_EXECUTABLE_NAME = /^[a-zA-Z0-9_-]+$/;\nconst ABSOLUTE_PATH = /^(?:\\/|[a-zA-Z]:[\\\\/])/;\nconst PATH_SEPARATOR = /[\\\\/]/;\n\nfunction pathBasename(executable: string): string {\n return path.basename(executable.replace(/\\\\/g, \"/\")).toLowerCase();\n}\n\nfunction isFile(filePath: string): boolean {\n try {\n return fs.statSync(filePath, { throwIfNoEntry: false })?.isFile() ?? false;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve argv[0] from a `ps` command line.\n *\n * `ps` joins argv with spaces, so an executable installed under a directory\n * containing spaces (`/Applications/Some App.app/Contents/Resources/codex`)\n * spans several whitespace tokens. When the first token of an absolute path\n * is not itself a file, longer space-joined prefixes ending in a token with a\n * path separator are tried, and the first one that exists on disk wins.\n * Otherwise the first token is returned, so arguments are never folded into\n * the executable path unless that combined path really is a file.\n */\nexport function executablePath(command: string): string {\n const parts = command.trim().split(/(\\s+)/);\n const first = parts[0] ?? \"\";\n if (parts.length === 1 || !ABSOLUTE_PATH.test(first) || isFile(first)) return first;\n\n let prefix = first;\n for (let i = 2; i < parts.length; i += 2) {\n prefix += parts[i - 1] + parts[i];\n if (PATH_SEPARATOR.test(parts[i]) && isFile(prefix)) return prefix;\n }\n return first;\n}\n\nexport function executableBasename(command: string): string {\n return pathBasename(executablePath(command));\n}\n\nfunction normalizeExecutableName(name: string): string {\n const lower = name.toLowerCase();\n return lower.endsWith(\".exe\") ? lower.slice(0, -4) : lower;\n}\n\nfunction normalizedProcessNames(namePatterns: readonly string[]): Set<string> {\n return new Set(namePatterns.filter(Boolean).map(normalizeExecutableName));\n}\n\n/**\n * Whether argv[0] of `command` is one of `names`.\n *\n * The first token is checked with string operations only, as before. The\n * filesystem is consulted only for absolute commands where a later\n * path-like token ends in one of the names, i.e. a possible executable under\n * a directory containing spaces.\n */\nfunction matchesExecutableName(command: string, names: ReadonlySet<string>): boolean {\n const tokens = command.trim().split(/\\s+/);\n const first = tokens[0] ?? \"\";\n if (names.has(normalizeExecutableName(pathBasename(first)))) return true;\n if (!ABSOLUTE_PATH.test(first)) return false;\n\n const mayContinuePath = tokens\n .slice(1)\n .some(\n (token) =>\n PATH_SEPARATOR.test(token) && names.has(normalizeExecutableName(pathBasename(token))),\n );\n return mayContinuePath && names.has(normalizeExecutableName(executableBasename(command)));\n}\n\nexport function filterByProcessNames(\n processes: readonly ProcessInfo[],\n namePatterns: readonly string[],\n): ProcessInfo[] {\n const names = normalizedProcessNames(namePatterns);\n if (names.size === 0) return [];\n return processes.filter((process) => matchesExecutableName(process.command, names));\n}\n\n/**\n * List running processes matching an agent executable name.\n *\n * Uses `ps -axo` then filters in JS for exact executable basename match.\n * This avoids shell pipelines and string interpolation.\n *\n * Returned ProcessInfo has pid, ppid, command, tty populated.\n * cwd and startTime are NOT populated — call enrichProcesses() to fill them.\n */\nexport function listAgentProcesses(namePattern: string): ProcessInfo[] {\n // Validate pattern contains only safe characters (alphanumeric, dash, underscore)\n if (!namePattern || !VALID_EXECUTABLE_NAME.test(namePattern)) {\n return [];\n }\n\n try {\n const output = execFileSync(\"ps\", [\"-axo\", \"pid=,ppid=,tty=,command=\"], { encoding: \"utf-8\" });\n\n const names = normalizedProcessNames([namePattern]);\n const processes: ProcessInfo[] = [];\n\n for (const line of output.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const ppid = parseInt(match[2], 10);\n if (Number.isNaN(pid) || Number.isNaN(ppid)) continue;\n\n const tty = match[3];\n const command = match[4];\n\n if (!matchesExecutableName(command, names)) continue;\n\n const ttyShort = tty.startsWith(\"/dev/\") ? tty.slice(5) : tty;\n\n processes.push({\n pid,\n ppid,\n command,\n cwd: \"\",\n tty: ttyShort,\n });\n }\n\n return processes;\n } catch {\n return [];\n }\n}\n\n/** Runs an executable and resolves with its stdout. Injectable for tests. */\nexport type ProcessExec = (file: string, args: readonly string[]) => Promise<string>;\n\nexport interface ProcessSnapshotOptions {\n /**\n * Decides which name-matched processes are worth enriching with cwd and\n * start time. Non-candidates are still returned (pid, ppid, tty, command\n * only) so adapters can walk parent chains. Omitted = enrich everything.\n */\n isCandidate?: (process: ProcessInfo) => boolean;\n}\n\nexport type ProcessSnapshotCapture = (\n namePatterns: readonly string[],\n options?: ProcessSnapshotOptions,\n) => Promise<ProcessInfo[]>;\n\nconst execFileText: ProcessExec = (file, args) =>\n new Promise((resolve, reject) => {\n execFile(\n file,\n args,\n { encoding: \"utf-8\", maxBuffer: PROCESS_EXEC_MAX_BUFFER },\n (error, stdout) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n },\n );\n });\n\ninterface ParsedProcessList {\n /** Every PID in the listing, used to evict cache entries for exited processes. */\n allPids: Set<number>;\n /** Processes whose executable matches one of the requested names. */\n matched: ProcessInfo[];\n}\n\nfunction parseProcessList(output: string, namePatterns: ReadonlySet<string>): ParsedProcessList {\n const allPids = new Set<number>();\n const matched: ProcessInfo[] = [];\n\n for (const line of output.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const ppid = parseInt(match[2], 10);\n if (Number.isNaN(pid) || Number.isNaN(ppid)) continue;\n allPids.add(pid);\n\n const tty = match[3];\n const command = match[4];\n if (!matchesExecutableName(command, namePatterns)) continue;\n\n matched.push({\n pid,\n ppid,\n command,\n cwd: \"\",\n tty: tty.startsWith(\"/dev/\") ? tty.slice(5) : tty,\n });\n }\n\n return { allPids, matched };\n}\n\nasync function batchGetProcessCwdsAsync(\n exec: ProcessExec,\n pids: number[],\n): Promise<Map<number, string>> {\n const result = new Map<number, string>();\n if (pids.length === 0) return result;\n\n try {\n const output = await exec(\"lsof\", [\"-a\", \"-d\", \"cwd\", \"-Fn\", \"-p\", pids.join(\",\")]);\n let currentPid: number | null = null;\n for (const line of output.trim().split(\"\\n\")) {\n if (line.startsWith(\"p\")) {\n currentPid = parseInt(line.slice(1), 10);\n } else if (line.startsWith(\"n\") && currentPid !== null) {\n result.set(currentPid, line.slice(1));\n currentPid = null;\n }\n }\n return result;\n } catch {\n const entries = await Promise.all(\n pids.map(async (pid) => {\n try {\n const output = await exec(\"pwdx\", [String(pid)]);\n const match = output.match(/^\\d+:\\s*(.+)$/);\n return match ? ([pid, match[1].trim()] as const) : null;\n } catch {\n return null;\n }\n }),\n );\n for (const entry of entries) {\n if (entry) result.set(entry[0], entry[1]);\n }\n return result;\n }\n}\n\nasync function batchGetProcessStartTimesAsync(\n exec: ProcessExec,\n pids: number[],\n): Promise<Map<number, Date>> {\n const result = new Map<number, Date>();\n if (pids.length === 0) return result;\n\n try {\n const output = await exec(\"ps\", [\"-o\", \"pid=,lstart=\", \"-p\", pids.join(\",\")]);\n for (const rawLine of output.split(\"\\n\")) {\n const match = rawLine.trim().match(/^(\\d+)\\s+(.+)$/);\n if (!match) continue;\n const pid = parseInt(match[1], 10);\n const date = new Date(match[2].trim());\n if (Number.isFinite(pid) && !Number.isNaN(date.getTime())) result.set(pid, date);\n }\n } catch {\n // Return partial/empty data, matching the synchronous helper.\n }\n return result;\n}\n\ninterface CachedStartTime {\n /** ppid + command seen when the start time was read; a mismatch means PID reuse. */\n identity: string;\n startTime: Date;\n}\n\nfunction processIdentity(process: ProcessInfo): string {\n return `${process.ppid ?? \"\"}\\0${process.command}`;\n}\n\n/**\n * Create a snapshot capture function with its own start-time cache.\n *\n * Start times never change for a live process, so each PID is queried with\n * `ps lstart` once and reused on later refreshes. A cache entry is dropped\n * when its PID disappears from the base listing, and ignored when the PID's\n * ppid/command no longer match (PID reuse). cwd is re-read every refresh\n * because a process can change directory.\n */\nexport function createProcessSnapshotCapture(\n deps: { exec?: ProcessExec } = {},\n): ProcessSnapshotCapture {\n const exec = deps.exec ?? execFileText;\n const startTimeCache = new Map<number, CachedStartTime>();\n\n const cachedStartTime = (process: ProcessInfo): Date | undefined => {\n const cached = startTimeCache.get(process.pid);\n return cached?.identity === processIdentity(process) ? cached.startTime : undefined;\n };\n\n return async (namePatterns, options = {}) => {\n const names = normalizedProcessNames(\n namePatterns.filter((name) => Boolean(name) && VALID_EXECUTABLE_NAME.test(name)),\n );\n if (names.size === 0) return [];\n\n try {\n const output = await exec(\"ps\", [\"-axo\", \"pid=,ppid=,tty=,command=\"]);\n const { allPids, matched } = parseProcessList(output, names);\n\n for (const pid of startTimeCache.keys()) {\n if (!allPids.has(pid)) startTimeCache.delete(pid);\n }\n\n const { isCandidate } = options;\n const candidates = isCandidate ? matched.filter((process) => isCandidate(process)) : matched;\n const candidatePids = candidates.map((process) => process.pid);\n const uncachedPids = candidates\n .filter((process) => cachedStartTime(process) === undefined)\n .map((process) => process.pid);\n\n const [cwdMap, startTimeMap] = await Promise.all([\n batchGetProcessCwdsAsync(exec, candidatePids),\n batchGetProcessStartTimesAsync(exec, uncachedPids),\n ]);\n\n for (const process of candidates) {\n const startTime = startTimeMap.get(process.pid);\n if (startTime) {\n startTimeCache.set(process.pid, { identity: processIdentity(process), startTime });\n }\n }\n\n const candidateSet = new Set(candidatePids);\n return matched.map((process) =>\n candidateSet.has(process.pid)\n ? {\n ...process,\n cwd: cwdMap.get(process.pid) || \"\",\n startTime: cachedStartTime(process),\n }\n : process,\n );\n } catch {\n return [];\n }\n };\n}\n\n/**\n * Capture and enrich relevant processes without blocking the event loop.\n * One base process listing is shared by every requested executable name;\n * only processes accepted by `options.isCandidate` are enriched.\n */\nexport const captureProcessSnapshot: ProcessSnapshotCapture = createProcessSnapshotCapture();\n\n/**\n * Batch-get current working directories for multiple PIDs.\n *\n * Single `lsof -a -d cwd -Fn -p PID1,PID2,...` call.\n * Returns partial results — if lsof fails for one PID, others still return.\n */\nexport function batchGetProcessCwds(pids: number[]): Map<number, string> {\n const result = new Map<number, string>();\n if (pids.length === 0) return result;\n\n try {\n const output = execFileSync(\"lsof\", [\"-a\", \"-d\", \"cwd\", \"-Fn\", \"-p\", pids.join(\",\")], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n\n // lsof output format: p{PID}\\nn{path}\\np{PID}\\nn{path}...\n let currentPid: number | null = null;\n for (const line of output.trim().split(\"\\n\")) {\n if (line.startsWith(\"p\")) {\n currentPid = parseInt(line.slice(1), 10);\n } else if (line.startsWith(\"n\") && currentPid !== null) {\n result.set(currentPid, line.slice(1));\n currentPid = null;\n }\n }\n } catch {\n // Try per-PID fallback with pwdx (Linux)\n for (const pid of pids) {\n try {\n const output = execFileSync(\"pwdx\", [String(pid)], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n const match = output.match(/^\\d+:\\s*(.+)$/);\n if (match) {\n result.set(pid, match[1].trim());\n }\n } catch {\n // Skip this PID\n }\n }\n }\n\n return result;\n}\n\n/**\n * Batch-get process start times for multiple PIDs.\n *\n * Single `ps -o pid=,lstart= -p PID1,PID2,...` call.\n * Uses lstart format which gives full timestamp (e.g., \"Thu Feb 5 16:00:57 2026\").\n * Returns partial results.\n */\nexport function batchGetProcessStartTimes(pids: number[]): Map<number, Date> {\n const result = new Map<number, Date>();\n if (pids.length === 0) return result;\n\n try {\n const output = execFileSync(\"ps\", [\"-o\", \"pid=,lstart=\", \"-p\", pids.join(\",\")], {\n encoding: \"utf-8\",\n });\n\n for (const rawLine of output.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Format: \" PID DAY MON DD HH:MM:SS YYYY\"\n // e.g., \" 78070 Wed Mar 18 23:18:01 2026\"\n const match = line.match(/^\\s*(\\d+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const dateStr = match[2].trim();\n\n if (!Number.isFinite(pid)) continue;\n\n const date = new Date(dateStr);\n if (!Number.isNaN(date.getTime())) {\n result.set(pid, date);\n }\n }\n } catch {\n // Return whatever we have\n }\n\n return result;\n}\n\n/**\n * Enrich ProcessInfo array with cwd and startTime.\n *\n * Calls batchGetProcessCwds and batchGetProcessStartTimes in batched shell calls,\n * then populates each ProcessInfo in-place. Returns partial results —\n * if a PID fails, that process keeps empty cwd / undefined startTime.\n */\nexport function enrichProcesses(processes: ProcessInfo[]): ProcessInfo[] {\n if (processes.length === 0) return processes;\n\n const pids = processes.map((p) => p.pid);\n const cwdMap = batchGetProcessCwds(pids);\n const startTimeMap = batchGetProcessStartTimes(pids);\n\n for (const proc of processes) {\n proc.cwd = cwdMap.get(proc.pid) || \"\";\n proc.startTime = startTimeMap.get(proc.pid);\n }\n\n return processes;\n}\n\nfunction isSameTerminalProcess(proc: ProcessInfo, matched: ProcessInfo): boolean {\n const sameTty = proc.tty !== \"\" && proc.tty !== \"?\" && proc.tty === matched.tty;\n const sameCwd = proc.cwd !== \"\" && proc.cwd === matched.cwd;\n\n return sameTty && (sameCwd || proc.cwd === \"\" || matched.cwd === \"\");\n}\n\nfunction matchesProcessIdentity(proc: ProcessInfo, matched: ProcessInfo): boolean {\n return proc.pid === matched.pid || isSameTerminalProcess(proc, matched);\n}\n\nexport function findWrapperProcess(\n processes: ProcessInfo[],\n child: ProcessInfo,\n): ProcessInfo | undefined {\n return processes.find(\n (proc) =>\n proc.pid !== child.pid && child.ppid === proc.pid && matchesProcessIdentity(proc, child),\n );\n}\n\n/**\n * Find parent wrapper processes that should not be reported as separate agents.\n *\n * A process is considered a wrapper when it is the parent of another candidate\n * agent process in the same terminal/worktree, or when it points at the same\n * terminal/worktree as an already session-matched process.\n */\nexport function findWrapperProcessPids(\n processes: ProcessInfo[],\n matchedProcesses: ProcessInfo[] = [],\n): Set<number> {\n const wrappers = new Set<number>();\n\n for (const child of processes) {\n const wrapper = findWrapperProcess(processes, child);\n if (wrapper) {\n wrappers.add(wrapper.pid);\n }\n }\n\n for (const proc of processes) {\n if (\n matchedProcesses.some(\n (matched) => proc.pid !== matched.pid && isSameTerminalProcess(proc, matched),\n )\n ) {\n wrappers.add(proc.pid);\n }\n }\n\n return wrappers;\n}\n\n/**\n * Get the TTY device for a specific process\n */\nexport function getProcessTty(pid: number): string {\n try {\n const output = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"tty=\"], { encoding: \"utf-8\" });\n\n const tty = output.trim();\n return tty.startsWith(\"/dev/\") ? tty.slice(5) : tty;\n } catch {\n return \"?\";\n }\n}\n"],"names":["fs","path","execFile","execFileSync","PROCESS_EXEC_MAX_BUFFER","VALID_EXECUTABLE_NAME","ABSOLUTE_PATH","PATH_SEPARATOR","pathBasename","executable","basename","replace","toLowerCase","isFile","filePath","statSync","throwIfNoEntry","executablePath","command","parts","trim","split","first","length","test","prefix","i","executableBasename","normalizeExecutableName","name","lower","endsWith","slice","normalizedProcessNames","namePatterns","Set","filter","Boolean","map","matchesExecutableName","names","tokens","has","mayContinuePath","some","token","filterByProcessNames","processes","size","process","listAgentProcesses","namePattern","output","encoding","line","match","pid","parseInt","ppid","Number","isNaN","tty","ttyShort","startsWith","push","cwd","execFileText","file","args","Promise","resolve","reject","maxBuffer","error","stdout","parseProcessList","allPids","matched","add","batchGetProcessCwdsAsync","exec","pids","result","Map","join","currentPid","set","entries","all","String","entry","batchGetProcessStartTimesAsync","rawLine","date","Date","isFinite","getTime","processIdentity","createProcessSnapshotCapture","deps","startTimeCache","cachedStartTime","cached","get","identity","startTime","undefined","options","keys","delete","isCandidate","candidates","candidatePids","uncachedPids","cwdMap","startTimeMap","candidateSet","captureProcessSnapshot","batchGetProcessCwds","stdio","batchGetProcessStartTimes","dateStr","enrichProcesses","p","proc","isSameTerminalProcess","sameTty","sameCwd","matchesProcessIdentity","findWrapperProcess","child","find","findWrapperProcessPids","matchedProcesses","wrappers","wrapper","getProcessTty"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utils/process.ts"],"sourcesContent":["/**\n * Process Detection Utilities\n *\n * Shared shell command wrappers for detecting and inspecting running processes.\n * Built-in refresh discovery uses captureProcessSnapshot(); synchronous helpers\n * remain exported for compatibility with existing consumers.\n */\n\nimport * as fs from \"fs\";\nimport * as path from \"path\";\nimport { execFile, execFileSync } from \"child_process\";\nimport type { ProcessInfo } from \"../adapters/AgentAdapter.js\";\n\nconst PROCESS_EXEC_MAX_BUFFER = 10 * 1024 * 1024;\nconst VALID_EXECUTABLE_NAME = /^[a-zA-Z0-9_-]+$/;\nconst ABSOLUTE_PATH = /^(?:\\/|[a-zA-Z]:[\\\\/])/;\nconst PATH_SEPARATOR = /[\\\\/]/;\n\nfunction pathBasename(executable: string): string {\n return path.basename(executable.replace(/\\\\/g, \"/\")).toLowerCase();\n}\n\nfunction isFile(filePath: string): boolean {\n try {\n return fs.statSync(filePath, { throwIfNoEntry: false })?.isFile() ?? false;\n } catch {\n return false;\n }\n}\n\n/**\n * Resolve argv[0] from a `ps` command line.\n *\n * `ps` joins argv with spaces, so an executable installed under a directory\n * containing spaces (`/Applications/Some App.app/Contents/Resources/codex`)\n * spans several whitespace tokens. When the first token of an absolute path\n * is not itself a file, longer space-joined prefixes ending in a token with a\n * path separator are tried, and the first one that exists on disk wins.\n * Otherwise the first token is returned, so arguments are never folded into\n * the executable path unless that combined path really is a file.\n */\nexport function executablePath(command: string): string {\n const parts = command.trim().split(/(\\s+)/);\n const first = parts[0] ?? \"\";\n if (parts.length === 1 || !ABSOLUTE_PATH.test(first) || isFile(first)) return first;\n\n let prefix = first;\n for (let i = 2; i < parts.length; i += 2) {\n prefix += parts[i - 1] + parts[i];\n if (PATH_SEPARATOR.test(parts[i]) && isFile(prefix)) return prefix;\n }\n return first;\n}\n\nexport function executableBasename(command: string): string {\n return pathBasename(executablePath(command));\n}\n\nfunction normalizeExecutableName(name: string): string {\n const lower = name.toLowerCase();\n return lower.endsWith(\".exe\") ? lower.slice(0, -4) : lower;\n}\n\nfunction normalizedProcessNames(namePatterns: readonly string[]): Set<string> {\n return new Set(namePatterns.filter(Boolean).map(normalizeExecutableName));\n}\n\n/**\n * Whether argv[0] of `command` is one of `names`.\n *\n * The first token is checked with string operations only, as before. The\n * filesystem is consulted only for absolute commands where a later\n * path-like token ends in one of the names, i.e. a possible executable under\n * a directory containing spaces.\n */\nfunction matchesExecutableName(command: string, names: ReadonlySet<string>): boolean {\n const tokens = command.trim().split(/\\s+/);\n const first = tokens[0] ?? \"\";\n if (names.has(normalizeExecutableName(pathBasename(first)))) return true;\n if (!ABSOLUTE_PATH.test(first)) return false;\n\n const mayContinuePath = tokens\n .slice(1)\n .some(\n (token) =>\n PATH_SEPARATOR.test(token) && names.has(normalizeExecutableName(pathBasename(token))),\n );\n return mayContinuePath && names.has(normalizeExecutableName(executableBasename(command)));\n}\n\nexport function filterByProcessNames(\n processes: readonly ProcessInfo[],\n namePatterns: readonly string[],\n): ProcessInfo[] {\n const names = normalizedProcessNames(namePatterns);\n if (names.size === 0) return [];\n return processes.filter((process) => matchesExecutableName(process.command, names));\n}\n\n/**\n * List running processes matching an agent executable name.\n *\n * Uses `ps -axo` then filters in JS for exact executable basename match.\n * This avoids shell pipelines and string interpolation.\n *\n * Returned ProcessInfo has pid, ppid, command, tty populated.\n * cwd and startTime are NOT populated — call enrichProcesses() to fill them.\n */\nexport function listAgentProcesses(namePattern: string): ProcessInfo[] {\n // Validate pattern contains only safe characters (alphanumeric, dash, underscore)\n if (!namePattern || !VALID_EXECUTABLE_NAME.test(namePattern)) {\n return [];\n }\n\n try {\n const output = execFileSync(\"ps\", [\"-axo\", \"pid=,ppid=,tty=,command=\"], { encoding: \"utf-8\" });\n\n const names = normalizedProcessNames([namePattern]);\n const processes: ProcessInfo[] = [];\n\n for (const line of output.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const ppid = parseInt(match[2], 10);\n if (Number.isNaN(pid) || Number.isNaN(ppid)) continue;\n\n const tty = match[3];\n const command = match[4];\n\n if (!matchesExecutableName(command, names)) continue;\n\n const ttyShort = tty.startsWith(\"/dev/\") ? tty.slice(5) : tty;\n\n processes.push({\n pid,\n ppid,\n command,\n cwd: \"\",\n tty: ttyShort,\n });\n }\n\n return processes;\n } catch {\n return [];\n }\n}\n\n/** Runs an executable and resolves with its stdout. Injectable for tests. */\nexport type ProcessExec = (file: string, args: readonly string[]) => Promise<string>;\n\nexport interface ProcessSnapshotOptions {\n /**\n * Decides which name-matched processes are worth enriching with cwd and\n * start time. Non-candidates are still returned (pid, ppid, tty, command\n * only) so adapters can walk parent chains. Omitted = enrich everything.\n */\n isCandidate?: (process: ProcessInfo) => boolean;\n}\n\nexport type ProcessSnapshotCapture = (\n namePatterns: readonly string[],\n options?: ProcessSnapshotOptions,\n) => Promise<ProcessInfo[]>;\n\nconst execFileText: ProcessExec = (file, args) =>\n new Promise((resolve, reject) => {\n execFile(\n file,\n args,\n { encoding: \"utf-8\", maxBuffer: PROCESS_EXEC_MAX_BUFFER },\n (error, stdout) => {\n if (error) {\n reject(error);\n return;\n }\n resolve(stdout);\n },\n );\n });\n\ninterface ParsedProcessList {\n /** Every PID in the listing, used to evict cache entries for exited processes. */\n allPids: Set<number>;\n /** Processes whose executable matches one of the requested names. */\n matched: ProcessInfo[];\n}\n\nfunction parseProcessList(output: string, namePatterns: ReadonlySet<string>): ParsedProcessList {\n const allPids = new Set<number>();\n const matched: ProcessInfo[] = [];\n\n for (const line of output.trim().split(\"\\n\")) {\n if (!line.trim()) continue;\n\n const match = line.match(/^\\s*(\\d+)\\s+(\\d+)\\s+(\\S+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const ppid = parseInt(match[2], 10);\n if (Number.isNaN(pid) || Number.isNaN(ppid)) continue;\n allPids.add(pid);\n\n const tty = match[3];\n const command = match[4];\n if (!matchesExecutableName(command, namePatterns)) continue;\n\n matched.push({\n pid,\n ppid,\n command,\n cwd: \"\",\n tty: tty.startsWith(\"/dev/\") ? tty.slice(5) : tty,\n });\n }\n\n return { allPids, matched };\n}\n\n/** Working directories of `pids` (async `lsof`); PIDs it cannot read are omitted. */\nexport function getProcessCwdsAsync(pids: number[]): Promise<Map<number, string>> {\n return batchGetProcessCwdsAsync(execFileText, pids);\n}\n\nasync function batchGetProcessCwdsAsync(\n exec: ProcessExec,\n pids: number[],\n): Promise<Map<number, string>> {\n const result = new Map<number, string>();\n if (pids.length === 0) return result;\n\n try {\n const output = await exec(\"lsof\", [\"-a\", \"-d\", \"cwd\", \"-Fn\", \"-p\", pids.join(\",\")]);\n let currentPid: number | null = null;\n for (const line of output.trim().split(\"\\n\")) {\n if (line.startsWith(\"p\")) {\n currentPid = parseInt(line.slice(1), 10);\n } else if (line.startsWith(\"n\") && currentPid !== null) {\n result.set(currentPid, line.slice(1));\n currentPid = null;\n }\n }\n return result;\n } catch {\n const entries = await Promise.all(\n pids.map(async (pid) => {\n try {\n const output = await exec(\"pwdx\", [String(pid)]);\n const match = output.match(/^\\d+:\\s*(.+)$/);\n return match ? ([pid, match[1].trim()] as const) : null;\n } catch {\n return null;\n }\n }),\n );\n for (const entry of entries) {\n if (entry) result.set(entry[0], entry[1]);\n }\n return result;\n }\n}\n\nasync function batchGetProcessStartTimesAsync(\n exec: ProcessExec,\n pids: number[],\n): Promise<Map<number, Date>> {\n const result = new Map<number, Date>();\n if (pids.length === 0) return result;\n\n try {\n const output = await exec(\"ps\", [\"-o\", \"pid=,lstart=\", \"-p\", pids.join(\",\")]);\n for (const rawLine of output.split(\"\\n\")) {\n const match = rawLine.trim().match(/^(\\d+)\\s+(.+)$/);\n if (!match) continue;\n const pid = parseInt(match[1], 10);\n const date = new Date(match[2].trim());\n if (Number.isFinite(pid) && !Number.isNaN(date.getTime())) result.set(pid, date);\n }\n } catch {\n // Return partial/empty data, matching the synchronous helper.\n }\n return result;\n}\n\ninterface CachedStartTime {\n /** ppid + command seen when the start time was read; a mismatch means PID reuse. */\n identity: string;\n startTime: Date;\n}\n\nfunction processIdentity(process: ProcessInfo): string {\n return `${process.ppid ?? \"\"}\\0${process.command}`;\n}\n\n/**\n * Create a snapshot capture function with its own start-time cache.\n *\n * Start times never change for a live process, so each PID is queried with\n * `ps lstart` once and reused on later refreshes. A cache entry is dropped\n * when its PID disappears from the base listing, and ignored when the PID's\n * ppid/command no longer match (PID reuse). cwd is re-read every refresh\n * because a process can change directory.\n */\nexport function createProcessSnapshotCapture(\n deps: { exec?: ProcessExec } = {},\n): ProcessSnapshotCapture {\n const exec = deps.exec ?? execFileText;\n const startTimeCache = new Map<number, CachedStartTime>();\n\n const cachedStartTime = (process: ProcessInfo): Date | undefined => {\n const cached = startTimeCache.get(process.pid);\n return cached?.identity === processIdentity(process) ? cached.startTime : undefined;\n };\n\n return async (namePatterns, options = {}) => {\n const names = normalizedProcessNames(\n namePatterns.filter((name) => Boolean(name) && VALID_EXECUTABLE_NAME.test(name)),\n );\n if (names.size === 0) return [];\n\n try {\n const output = await exec(\"ps\", [\"-axo\", \"pid=,ppid=,tty=,command=\"]);\n const { allPids, matched } = parseProcessList(output, names);\n\n for (const pid of startTimeCache.keys()) {\n if (!allPids.has(pid)) startTimeCache.delete(pid);\n }\n\n const { isCandidate } = options;\n const candidates = isCandidate ? matched.filter((process) => isCandidate(process)) : matched;\n const candidatePids = candidates.map((process) => process.pid);\n const uncachedPids = candidates\n .filter((process) => cachedStartTime(process) === undefined)\n .map((process) => process.pid);\n\n const [cwdMap, startTimeMap] = await Promise.all([\n batchGetProcessCwdsAsync(exec, candidatePids),\n batchGetProcessStartTimesAsync(exec, uncachedPids),\n ]);\n\n for (const process of candidates) {\n const startTime = startTimeMap.get(process.pid);\n if (startTime) {\n startTimeCache.set(process.pid, { identity: processIdentity(process), startTime });\n }\n }\n\n const candidateSet = new Set(candidatePids);\n return matched.map((process) =>\n candidateSet.has(process.pid)\n ? {\n ...process,\n cwd: cwdMap.get(process.pid) || \"\",\n startTime: cachedStartTime(process),\n }\n : process,\n );\n } catch {\n return [];\n }\n };\n}\n\n/**\n * Capture and enrich relevant processes without blocking the event loop.\n * One base process listing is shared by every requested executable name;\n * only processes accepted by `options.isCandidate` are enriched.\n */\nexport const captureProcessSnapshot: ProcessSnapshotCapture = createProcessSnapshotCapture();\n\n/**\n * Batch-get current working directories for multiple PIDs.\n *\n * Single `lsof -a -d cwd -Fn -p PID1,PID2,...` call.\n * Returns partial results — if lsof fails for one PID, others still return.\n */\nexport function batchGetProcessCwds(pids: number[]): Map<number, string> {\n const result = new Map<number, string>();\n if (pids.length === 0) return result;\n\n try {\n const output = execFileSync(\"lsof\", [\"-a\", \"-d\", \"cwd\", \"-Fn\", \"-p\", pids.join(\",\")], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n\n // lsof output format: p{PID}\\nn{path}\\np{PID}\\nn{path}...\n let currentPid: number | null = null;\n for (const line of output.trim().split(\"\\n\")) {\n if (line.startsWith(\"p\")) {\n currentPid = parseInt(line.slice(1), 10);\n } else if (line.startsWith(\"n\") && currentPid !== null) {\n result.set(currentPid, line.slice(1));\n currentPid = null;\n }\n }\n } catch {\n // Try per-PID fallback with pwdx (Linux)\n for (const pid of pids) {\n try {\n const output = execFileSync(\"pwdx\", [String(pid)], {\n encoding: \"utf-8\",\n stdio: [\"pipe\", \"pipe\", \"ignore\"],\n });\n const match = output.match(/^\\d+:\\s*(.+)$/);\n if (match) {\n result.set(pid, match[1].trim());\n }\n } catch {\n // Skip this PID\n }\n }\n }\n\n return result;\n}\n\n/**\n * Batch-get process start times for multiple PIDs.\n *\n * Single `ps -o pid=,lstart= -p PID1,PID2,...` call.\n * Uses lstart format which gives full timestamp (e.g., \"Thu Feb 5 16:00:57 2026\").\n * Returns partial results.\n */\nexport function batchGetProcessStartTimes(pids: number[]): Map<number, Date> {\n const result = new Map<number, Date>();\n if (pids.length === 0) return result;\n\n try {\n const output = execFileSync(\"ps\", [\"-o\", \"pid=,lstart=\", \"-p\", pids.join(\",\")], {\n encoding: \"utf-8\",\n });\n\n for (const rawLine of output.split(\"\\n\")) {\n const line = rawLine.trim();\n if (!line) continue;\n\n // Format: \" PID DAY MON DD HH:MM:SS YYYY\"\n // e.g., \" 78070 Wed Mar 18 23:18:01 2026\"\n const match = line.match(/^\\s*(\\d+)\\s+(.+)$/);\n if (!match) continue;\n\n const pid = parseInt(match[1], 10);\n const dateStr = match[2].trim();\n\n if (!Number.isFinite(pid)) continue;\n\n const date = new Date(dateStr);\n if (!Number.isNaN(date.getTime())) {\n result.set(pid, date);\n }\n }\n } catch {\n // Return whatever we have\n }\n\n return result;\n}\n\n/**\n * Enrich ProcessInfo array with cwd and startTime.\n *\n * Calls batchGetProcessCwds and batchGetProcessStartTimes in batched shell calls,\n * then populates each ProcessInfo in-place. Returns partial results —\n * if a PID fails, that process keeps empty cwd / undefined startTime.\n */\nexport function enrichProcesses(processes: ProcessInfo[]): ProcessInfo[] {\n if (processes.length === 0) return processes;\n\n const pids = processes.map((p) => p.pid);\n const cwdMap = batchGetProcessCwds(pids);\n const startTimeMap = batchGetProcessStartTimes(pids);\n\n for (const proc of processes) {\n proc.cwd = cwdMap.get(proc.pid) || \"\";\n proc.startTime = startTimeMap.get(proc.pid);\n }\n\n return processes;\n}\n\nfunction isSameTerminalProcess(proc: ProcessInfo, matched: ProcessInfo): boolean {\n const sameTty = proc.tty !== \"\" && proc.tty !== \"?\" && proc.tty === matched.tty;\n const sameCwd = proc.cwd !== \"\" && proc.cwd === matched.cwd;\n\n return sameTty && (sameCwd || proc.cwd === \"\" || matched.cwd === \"\");\n}\n\nfunction matchesProcessIdentity(proc: ProcessInfo, matched: ProcessInfo): boolean {\n return proc.pid === matched.pid || isSameTerminalProcess(proc, matched);\n}\n\nexport function findWrapperProcess(\n processes: ProcessInfo[],\n child: ProcessInfo,\n): ProcessInfo | undefined {\n return processes.find(\n (proc) =>\n proc.pid !== child.pid && child.ppid === proc.pid && matchesProcessIdentity(proc, child),\n );\n}\n\n/**\n * Find parent wrapper processes that should not be reported as separate agents.\n *\n * A process is considered a wrapper when it is the parent of another candidate\n * agent process in the same terminal/worktree, or when it points at the same\n * terminal/worktree as an already session-matched process.\n */\nexport function findWrapperProcessPids(\n processes: ProcessInfo[],\n matchedProcesses: ProcessInfo[] = [],\n): Set<number> {\n const wrappers = new Set<number>();\n\n for (const child of processes) {\n const wrapper = findWrapperProcess(processes, child);\n if (wrapper) {\n wrappers.add(wrapper.pid);\n }\n }\n\n for (const proc of processes) {\n if (\n matchedProcesses.some(\n (matched) => proc.pid !== matched.pid && isSameTerminalProcess(proc, matched),\n )\n ) {\n wrappers.add(proc.pid);\n }\n }\n\n return wrappers;\n}\n\n/**\n * Get the TTY device for a specific process\n */\nexport function getProcessTty(pid: number): string {\n try {\n const output = execFileSync(\"ps\", [\"-p\", String(pid), \"-o\", \"tty=\"], { encoding: \"utf-8\" });\n\n const tty = output.trim();\n return tty.startsWith(\"/dev/\") ? tty.slice(5) : tty;\n } catch {\n return \"?\";\n }\n}\n"],"names":["fs","path","execFile","execFileSync","PROCESS_EXEC_MAX_BUFFER","VALID_EXECUTABLE_NAME","ABSOLUTE_PATH","PATH_SEPARATOR","pathBasename","executable","basename","replace","toLowerCase","isFile","filePath","statSync","throwIfNoEntry","executablePath","command","parts","trim","split","first","length","test","prefix","i","executableBasename","normalizeExecutableName","name","lower","endsWith","slice","normalizedProcessNames","namePatterns","Set","filter","Boolean","map","matchesExecutableName","names","tokens","has","mayContinuePath","some","token","filterByProcessNames","processes","size","process","listAgentProcesses","namePattern","output","encoding","line","match","pid","parseInt","ppid","Number","isNaN","tty","ttyShort","startsWith","push","cwd","execFileText","file","args","Promise","resolve","reject","maxBuffer","error","stdout","parseProcessList","allPids","matched","add","getProcessCwdsAsync","pids","batchGetProcessCwdsAsync","exec","result","Map","join","currentPid","set","entries","all","String","entry","batchGetProcessStartTimesAsync","rawLine","date","Date","isFinite","getTime","processIdentity","createProcessSnapshotCapture","deps","startTimeCache","cachedStartTime","cached","get","identity","startTime","undefined","options","keys","delete","isCandidate","candidates","candidatePids","uncachedPids","cwdMap","startTimeMap","candidateSet","captureProcessSnapshot","batchGetProcessCwds","stdio","batchGetProcessStartTimes","dateStr","enrichProcesses","p","proc","isSameTerminalProcess","sameTty","sameCwd","matchesProcessIdentity","findWrapperProcess","child","find","findWrapperProcessPids","matchedProcesses","wrappers","wrapper","getProcessTty"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ai-devkit/agent-manager",
3
- "version": "0.37.0",
3
+ "version": "0.39.0",
4
4
  "description": "Standalone agent detection and management utilities for AI DevKit",
5
5
  "keywords": [
6
6
  "agent",
@@ -47,7 +47,7 @@ export interface AgentManagerOptions {
47
47
  onRuntimeDiscoveryError?: (error: unknown) => void;
48
48
  }
49
49
 
50
- export class AgentNotRunningError extends Error {
50
+ class AgentNotRunningError extends Error {
51
51
  constructor(public agentName: string) {
52
52
  super(`Agent "${agentName}" is no longer running.`);
53
53
  this.name = "AgentNotRunningError";
@@ -0,0 +1,105 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import * as capacity from "../../capacity/index.js";
3
+ import * as agentManager from "../../index.js";
4
+
5
+ const checkedAt = "2026-09-30T12:00:00.000Z";
6
+ const freshAuth = JSON.stringify({
7
+ anthropic: {
8
+ type: "oauth",
9
+ access: "fake-pi-anthropic-token",
10
+ refresh: "fake-pi-anthropic-refresh",
11
+ expires: Date.parse("2026-10-01T12:00:00.000Z"),
12
+ },
13
+ });
14
+
15
+ describe("Pi Anthropic capacity", () => {
16
+ it("exports a Pi Anthropic capacity report reader", () => {
17
+ expect(capacity).toHaveProperty("getAnthropicCapacityReport");
18
+ expect(agentManager).toHaveProperty("getAnthropicCapacityReport");
19
+ });
20
+
21
+ it("reads Pi's Anthropic OAuth credential and reports the Pi harness", async () => {
22
+ const readFile = vi.fn(async () => freshAuth);
23
+ const fetch = vi.fn(
24
+ async () =>
25
+ new Response(JSON.stringify({ five_hour: { utilization: 20 } }), {
26
+ status: 200,
27
+ }),
28
+ );
29
+
30
+ const report = await capacity.getAnthropicCapacityReport({
31
+ now: () => new Date(checkedAt),
32
+ env: { HOME: "/users/test" },
33
+ readFile,
34
+ fetch,
35
+ });
36
+
37
+ expect(readFile).toHaveBeenCalledWith("/users/test/.pi/agent/auth.json", "utf8");
38
+ expect(fetch).toHaveBeenCalledWith(
39
+ "https://api.anthropic.com/api/oauth/usage",
40
+ expect.objectContaining({
41
+ headers: expect.objectContaining({
42
+ Authorization: "Bearer fake-pi-anthropic-token",
43
+ }),
44
+ }),
45
+ );
46
+ expect(report).toMatchObject({
47
+ harness: "pi",
48
+ provider: "anthropic",
49
+ generatedAt: checkedAt,
50
+ authenticated: true,
51
+ available: "yes",
52
+ });
53
+ expect(JSON.stringify(report)).not.toContain("fake-pi-anthropic-token");
54
+ expect(JSON.stringify(report)).not.toContain("fake-pi-anthropic-refresh");
55
+ });
56
+
57
+ it("does not fetch for an expired Pi login and returns a stale report", async () => {
58
+ const fetch = vi.fn();
59
+ const report = await capacity.getAnthropicCapacityReport({
60
+ now: () => new Date(checkedAt),
61
+ env: { HOME: "/users/test" },
62
+ readFile: async () =>
63
+ JSON.stringify({
64
+ anthropic: {
65
+ type: "oauth",
66
+ access: "fake-expired-token",
67
+ expires: 1,
68
+ },
69
+ }),
70
+ fetch,
71
+ });
72
+
73
+ expect(fetch).not.toHaveBeenCalled();
74
+ expect(report).toEqual({
75
+ harness: "pi",
76
+ provider: "anthropic",
77
+ generatedAt: checkedAt,
78
+ authenticated: true,
79
+ available: "unknown",
80
+ windows: [],
81
+ creditsRemaining: null,
82
+ });
83
+ });
84
+
85
+ it.each([
86
+ [
87
+ "missing file",
88
+ async () => Promise.reject(Object.assign(new Error("missing"), { code: "ENOENT" })),
89
+ ],
90
+ ["malformed file", async () => "{"],
91
+ ["missing credential", async () => "{}"],
92
+ ["malformed credential", async () => JSON.stringify({ anthropic: { type: "oauth" } })],
93
+ ])("rejects a %s without fetching", async (_name, readFile) => {
94
+ const fetch = vi.fn();
95
+ await expect(
96
+ capacity.getAnthropicCapacityReport({
97
+ now: () => new Date(checkedAt),
98
+ env: { HOME: "/users/test" },
99
+ readFile,
100
+ fetch,
101
+ }),
102
+ ).rejects.toThrow(/Pi Anthropic/);
103
+ expect(fetch).not.toHaveBeenCalled();
104
+ });
105
+ });
@@ -0,0 +1,36 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { parseAnthropicCapacity } from "../../capacity/providers/anthropic.js";
3
+ import { resolvePiAnthropicCredential } from "../../harnesses/pi/credentials.js";
4
+
5
+ describe("capacity boundaries", () => {
6
+ it("keeps Pi credential extraction separate from Anthropic quota parsing", async () => {
7
+ const credential = await resolvePiAnthropicCredential({
8
+ env: { HOME: "/users/test" },
9
+ readFile: async () =>
10
+ JSON.stringify({
11
+ anthropic: {
12
+ type: "oauth",
13
+ access: "fake-boundary-token",
14
+ expires: Date.parse("2026-10-01T00:00:00.000Z"),
15
+ },
16
+ }),
17
+ });
18
+
19
+ const snapshot = parseAnthropicCapacity({ five_hour: { utilization: 25 } });
20
+
21
+ expect(credential).toEqual({
22
+ kind: "oauth",
23
+ access: "fake-boundary-token",
24
+ expiresMs: Date.parse("2026-10-01T00:00:00.000Z"),
25
+ });
26
+ expect(snapshot).toMatchObject({
27
+ authenticated: true,
28
+ available: "yes",
29
+ windows: [{ id: "session", usedPercent: 25 }],
30
+ creditsRemaining: null,
31
+ });
32
+ expect(snapshot).not.toHaveProperty("harness");
33
+ expect(snapshot).not.toHaveProperty("provider");
34
+ expect(snapshot).not.toHaveProperty("generatedAt");
35
+ });
36
+ });
@@ -2,7 +2,7 @@ import { readFile } from "node:fs/promises";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { describe, expect, it, vi } from "vitest";
4
4
  import * as capacity from "../../capacity/index.js";
5
- import { parseClaudeUsage } from "../../capacity/claude.js";
5
+ import { parseAnthropicCapacity } from "../../capacity/providers/anthropic.js";
6
6
  import * as agentManager from "../../index.js";
7
7
 
8
8
  const fixture = (name: string) =>
@@ -226,10 +226,7 @@ describe("Claude capacity", () => {
226
226
  });
227
227
 
228
228
  it("preserves valid windows in a partial payload", async () => {
229
- const report = parseClaudeUsage(
230
- JSON.parse(await fixture("claude-usage-partial.json")),
231
- "2026-09-25T18:00:00.000Z",
232
- );
229
+ const report = parseAnthropicCapacity(JSON.parse(await fixture("claude-usage-partial.json")));
233
230
  expect(report.available).toBe("yes");
234
231
  expect(report.windows).toEqual([
235
232
  {
@@ -251,9 +248,7 @@ describe("Claude capacity", () => {
251
248
 
252
249
  it("rejects a non-object usage payload", async () => {
253
250
  const raw = JSON.parse(await fixture("claude-usage-malformed.json"));
254
- expect(() => parseClaudeUsage(raw, "2026-09-25T18:00:00.000Z")).toThrow(
255
- "Claude usage response is malformed",
256
- );
251
+ expect(() => parseAnthropicCapacity(raw)).toThrow("Claude usage response is malformed");
257
252
  });
258
253
 
259
254
  it.each([
@@ -3,9 +3,9 @@ import {
3
3
  CODEX_APP_SERVER_ARGS,
4
4
  parseUsage,
5
5
  probeCodexCapacity,
6
- resolveCodexAuthPath,
7
- toRateWindow,
8
- } from "../../capacity/codex.js";
6
+ } from "../../capacity/sources/codex.js";
7
+ import { resolveCodexAuthPath } from "../../harnesses/codex/credentials.js";
8
+ import { toRateWindow } from "../../capacity/providers/openai-subscription.js";
9
9
 
10
10
  const checkedAt = "2026-08-20T10:00:00.000Z";
11
11
  const context = { installed: true, checkedAt };
@@ -52,9 +52,7 @@ describe("Codex auth resolution", () => {
52
52
  });
53
53
 
54
54
  it("falls back to ~/.codex/auth.json", () => {
55
- expect(resolveCodexAuthPath({ HOME: "/users/test" })).toBe(
56
- "/users/test/.codex/auth.json",
57
- );
55
+ expect(resolveCodexAuthPath({ HOME: "/users/test" })).toBe("/users/test/.codex/auth.json");
58
56
  });
59
57
  });
60
58
 
@@ -108,9 +106,7 @@ describe("tiered Codex probing", () => {
108
106
  status: 200,
109
107
  }),
110
108
  )
111
- .mockResolvedValueOnce(
112
- new Response(JSON.stringify(apiUsage()), { status: 200 }),
113
- );
109
+ .mockResolvedValueOnce(new Response(JSON.stringify(apiUsage()), { status: 200 }));
114
110
  const rpc = vi.fn();
115
111
  const result = await probeCodexCapacity({
116
112
  ...context,
@@ -129,9 +125,7 @@ describe("tiered Codex probing", () => {
129
125
  expect(fetch.mock.calls[0][0]).toBe(
130
126
  "https://auth.openai.com/api/accounts/v1/user-auth-credential/whoami",
131
127
  );
132
- expect(fetch.mock.calls[1][0]).toBe(
133
- "https://chatgpt.com/backend-api/wham/usage",
134
- );
128
+ expect(fetch.mock.calls[1][0]).toBe("https://chatgpt.com/backend-api/wham/usage");
135
129
  expect(fetch.mock.calls[1][1].headers).toMatchObject({
136
130
  Authorization: "Bearer pat-secret",
137
131
  "ChatGPT-Account-Id": "acct-1",
@@ -154,9 +148,7 @@ describe("tiered Codex probing", () => {
154
148
  it("selects a fresh OAuth token without calling whoami", async () => {
155
149
  const fetch = vi
156
150
  .fn()
157
- .mockResolvedValue(
158
- new Response(JSON.stringify(apiUsage()), { status: 200 }),
159
- );
151
+ .mockResolvedValue(new Response(JSON.stringify(apiUsage()), { status: 200 }));
160
152
  const result = await probeCodexCapacity({
161
153
  ...context,
162
154
  readFile: async () =>
@@ -210,9 +202,7 @@ describe("tiered Codex probing", () => {
210
202
  ])("falls back to the CLI for %s", async (name, readFile) => {
211
203
  const fetch = vi
212
204
  .fn()
213
- .mockResolvedValue(
214
- new Response("", { status: name === "OAuth 401" ? 401 : 200 }),
215
- );
205
+ .mockResolvedValue(new Response("", { status: name === "OAuth 401" ? 401 : 200 }));
216
206
  const rpc = vi.fn(async () => ({
217
207
  rateLimits: {
218
208
  rateLimits: {
@@ -239,8 +229,7 @@ describe("tiered Codex probing", () => {
239
229
  }));
240
230
  const result = await probeCodexCapacity({
241
231
  ...context,
242
- readFile: async () =>
243
- JSON.stringify({ personal_access_token: "pat-secret" }),
232
+ readFile: async () => JSON.stringify({ personal_access_token: "pat-secret" }),
244
233
  fetch: vi.fn().mockRejectedValue(new Error("network failure pat-secret")),
245
234
  rpc,
246
235
  });
@@ -252,9 +241,7 @@ describe("tiered Codex probing", () => {
252
241
  const fetch = vi
253
242
  .fn()
254
243
  .mockRejectedValueOnce(new Error("PAT failed"))
255
- .mockResolvedValueOnce(
256
- new Response(JSON.stringify(apiUsage()), { status: 200 }),
257
- );
244
+ .mockResolvedValueOnce(new Response(JSON.stringify(apiUsage()), { status: 200 }));
258
245
  const rpc = vi.fn();
259
246
  const result = await probeCodexCapacity({
260
247
  ...context,
@@ -294,13 +281,7 @@ describe("tiered Codex probing", () => {
294
281
  "account/read",
295
282
  ]);
296
283
  expect(JSON.stringify(messages)).not.toMatch(/prompt|turn\/start/);
297
- expect(CODEX_APP_SERVER_ARGS).toEqual([
298
- "-s",
299
- "read-only",
300
- "-a",
301
- "untrusted",
302
- "app-server",
303
- ]);
284
+ expect(CODEX_APP_SERVER_ARGS).toEqual(["-s", "read-only", "-a", "untrusted", "app-server"]);
304
285
  });
305
286
 
306
287
  it("uses account/read to distinguish logged-out CLI state", async () => {
@@ -316,11 +297,7 @@ describe("tiered Codex probing", () => {
316
297
  });
317
298
 
318
299
  it("never exposes tokens or raw auth content through failures", async () => {
319
- const secrets = [
320
- "pat-secret-value",
321
- "oauth-secret-value",
322
- "refresh-secret-value",
323
- ];
300
+ const secrets = ["pat-secret-value", "oauth-secret-value", "refresh-secret-value"];
324
301
  const result = await probeCodexCapacity({
325
302
  ...context,
326
303
  readFile: async () =>
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "openai-codex": {
3
3
  "type": "oauth",
4
- "access": "fixture-header.eyJleHAiOjE3OTMwMDAwMDB9.fixture-signature",
4
+ "access": "sample access value for tests",
5
5
  "refresh": "fixture-refresh-token",
6
6
  "expires": 1793000000000,
7
7
  "accountId": "fixture-chatgpt-account-id"
@@ -2,14 +2,17 @@ import { readFile } from "node:fs/promises";
2
2
  import { readFileSync } from "node:fs";
3
3
  import { fileURLToPath } from "node:url";
4
4
  import { describe, expect, it, vi } from "vitest";
5
+ import { probeOpenAiCapacity } from "../../capacity/sources/pi.js";
5
6
  import {
6
- buildOpenAiReport,
7
+ buildOpenAiPlatformCapacity,
7
8
  parseOpenAiUsage,
8
- probeOpenAiCapacity,
9
- resolveOpenAiCredential,
10
- } from "../../capacity/openai.js";
9
+ } from "../../capacity/providers/openai-platform.js";
10
+ import { resolvePiOpenAiCredential as resolveOpenAiCredential } from "../../harnesses/pi/credentials.js";
11
11
 
12
12
  const checkedAt = "2026-09-26T10:00:00.000Z";
13
+ const fixtureOauthAccess = "sample access value for tests";
14
+ const fakeJwt = (payload: string) =>
15
+ ["test", Buffer.from(payload).toString("base64url"), "test"].join(".");
13
16
  const fixture = (name: string) =>
14
17
  readFile(fileURLToPath(new URL(`./fixtures/${name}`, import.meta.url)), "utf8");
15
18
  const fixtureString = (name: string) =>
@@ -59,7 +62,7 @@ describe("OpenAI tiered credential resolution", () => {
59
62
  openai: { type: "api_key", key: "pi-openai-test-key" },
60
63
  "openai-codex": {
61
64
  type: "oauth",
62
- access: "fixture-header.eyJleHAiOjE3OTMwMDAwMDB9.fixture-signature",
65
+ access: fixtureOauthAccess,
63
66
  refresh: "fixture-refresh-token",
64
67
  expires: 1793000000000,
65
68
  accountId: "fixture-chatgpt-account-id",
@@ -83,7 +86,7 @@ describe("OpenAI tiered credential resolution", () => {
83
86
  }),
84
87
  ).resolves.toEqual({
85
88
  kind: "oauth",
86
- access: "fixture-header.eyJleHAiOjE3OTMwMDAwMDB9.fixture-signature",
89
+ access: fixtureOauthAccess,
87
90
  accountId: "fixture-chatgpt-account-id",
88
91
  expiresMs: 1793000000000,
89
92
  });
@@ -148,7 +151,7 @@ describe("OpenAI OAuth capacity probe", () => {
148
151
  expect.objectContaining({
149
152
  method: "GET",
150
153
  headers: {
151
- Authorization: "Bearer fixture-header.eyJleHAiOjE3OTMwMDAwMDB9.fixture-signature",
154
+ Authorization: `Bearer ${fixtureOauthAccess}`,
152
155
  "ChatGPT-Account-Id": "fixture-chatgpt-account-id",
153
156
  },
154
157
  }),
@@ -217,7 +220,7 @@ describe("OpenAI OAuth capacity probe", () => {
217
220
  const expiredJwtAuth = JSON.stringify({
218
221
  "openai-codex": {
219
222
  type: "oauth",
220
- access: "h.eyJleHAiOjE3ODkwMDAwMDB9.s",
223
+ access: fakeJwt(JSON.stringify({ exp: 1789000000 })),
221
224
  accountId: "fixture-chatgpt-account-id",
222
225
  },
223
226
  });
@@ -235,7 +238,7 @@ describe("OpenAI OAuth capacity probe", () => {
235
238
  const futureJwtAuth = JSON.stringify({
236
239
  "openai-codex": {
237
240
  type: "oauth",
238
- access: "h.eyJleHAiOjE3OTMwMDAwMDB9.s",
241
+ access: fakeJwt(JSON.stringify({ exp: 1793000000 })),
239
242
  accountId: "fixture-chatgpt-account-id",
240
243
  },
241
244
  });
@@ -255,7 +258,7 @@ describe("OpenAI OAuth capacity probe", () => {
255
258
  const auth = JSON.stringify({
256
259
  "openai-codex": {
257
260
  type: "oauth",
258
- access: "fixture-header.bm90LWpzb24.fixture-signature",
261
+ access: fakeJwt("not-json"),
259
262
  accountId: "fixture-chatgpt-account-id",
260
263
  },
261
264
  });
@@ -391,11 +394,8 @@ describe("OpenAI usage mapping", () => {
391
394
  ...parseOpenAiUsage(JSON.parse(await fixture("openai-usage-completions.json"))),
392
395
  ...parseOpenAiUsage(JSON.parse(await fixture("openai-usage-responses.json"))),
393
396
  ];
394
- const report = buildOpenAiReport(buckets, checkedAt);
397
+ const report = buildOpenAiPlatformCapacity(buckets, checkedAt);
395
398
  expect(report).toMatchObject({
396
- harness: "pi",
397
- provider: "openai",
398
- generatedAt: checkedAt,
399
399
  authenticated: true,
400
400
  available: "yes",
401
401
  creditsRemaining: null,
@@ -427,7 +427,7 @@ describe("OpenAI usage mapping", () => {
427
427
  });
428
428
 
429
429
  it("reports zero usage for days without buckets", () => {
430
- const report = buildOpenAiReport([], checkedAt);
430
+ const report = buildOpenAiPlatformCapacity([], checkedAt);
431
431
  expect(report.windows.map((window) => window.current)).toEqual([0, 0]);
432
432
  expect(report.windows[0].resetsAt).toBe("2026-09-27T00:00:00.000Z");
433
433
  });
@@ -1,7 +1,7 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import { describe, expect, it } from "vitest";
4
- import { parseWhamUsage, toRateWindow } from "../../capacity/wham.js";
4
+ import { parseWhamUsage, toRateWindow } from "../../capacity/providers/openai-subscription.js";
5
5
 
6
6
  const fixture = (name: string) =>
7
7
  readFile(fileURLToPath(new URL(`./fixtures/${name}`, import.meta.url)), "utf8");