@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
@@ -0,0 +1,230 @@
1
+ import * as fs from "fs";
2
+ import * as path from "path";
3
+ import Database from "better-sqlite3";
4
+ import type { ListSessionsOptions, SessionSummary } from "../../adapters/AgentAdapter.js";
5
+ import { DevinSessionParser } from "./DevinSessionParser.js";
6
+ import { homeDir } from "../shared.js";
7
+
8
+ const SESSION_REF_SEP = "::";
9
+ const LOCK_FILE_SUFFIX = ".lock";
10
+
11
+ export function encodeDevinSessionRef(dbPath: string, sessionId: string): string {
12
+ return `${dbPath}${SESSION_REF_SEP}${sessionId}`;
13
+ }
14
+
15
+ /** Inverse of {@link encodeDevinSessionRef}; null when the ref has no separator. */
16
+ export function decodeDevinSessionRef(ref: string): string | null {
17
+ const idx = ref.lastIndexOf(SESSION_REF_SEP);
18
+ return idx === -1 ? null : ref.slice(idx + SESSION_REF_SEP.length);
19
+ }
20
+
21
+ export interface DevinSession {
22
+ sessionId: string;
23
+ directory: string;
24
+ title: string | null;
25
+ /** Epoch milliseconds. */
26
+ timeCreated: number;
27
+ /** Epoch milliseconds. */
28
+ lastActivityAt: number;
29
+ }
30
+
31
+ /** A live `session_locks/<slug>.lock` entry: the session and its holder PID. */
32
+ export interface DevinSessionLock {
33
+ sessionId: string;
34
+ pid: number;
35
+ }
36
+
37
+ interface DevinSessionRow {
38
+ id: string;
39
+ working_directory: string;
40
+ title: string | null;
41
+ created_at: number;
42
+ last_activity_at: number;
43
+ }
44
+
45
+ function toDevinSession(row: DevinSessionRow): DevinSession {
46
+ return {
47
+ sessionId: row.id,
48
+ directory: row.working_directory,
49
+ title: row.title,
50
+ timeCreated: row.created_at * 1000,
51
+ lastActivityAt: row.last_activity_at * 1000,
52
+ };
53
+ }
54
+
55
+ export class DevinSessionLocator {
56
+ private readonly dbPath: string;
57
+ private readonly locksDir: string;
58
+ private db: Database.Database | null = null;
59
+
60
+ constructor(
61
+ dbPath = DevinSessionLocator.resolveDbPath(),
62
+ locksDir = DevinSessionLocator.defaultLocksDir(dbPath),
63
+ private readonly parser: DevinSessionParser = new DevinSessionParser(),
64
+ ) {
65
+ this.dbPath = dbPath;
66
+ this.locksDir = locksDir;
67
+ }
68
+
69
+ static resolveDbPath(): string {
70
+ const xdg = process.env.XDG_DATA_HOME;
71
+ const base = xdg || path.join(homeDir(), ".local", "share");
72
+ return path.join(base, "devin", "cli", "sessions.db");
73
+ }
74
+
75
+ static defaultLocksDir(dbPath: string): string {
76
+ return path.join(path.dirname(dbPath), "session_locks");
77
+ }
78
+
79
+ openDb(): Database.Database | null {
80
+ if (this.db) return this.db;
81
+ if (!fs.existsSync(this.dbPath)) return null;
82
+
83
+ try {
84
+ this.db = new Database(this.dbPath, { readonly: true });
85
+ return this.db;
86
+ } catch {
87
+ return null;
88
+ }
89
+ }
90
+
91
+ close(): void {
92
+ if (this.db) {
93
+ try {
94
+ this.db.close();
95
+ } catch {
96
+ /* ignore */
97
+ }
98
+ this.db = null;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * Read `session_locks/*.lock` entries. Each file name is a session slug and
104
+ * its body is the PID of the `devin acp` backend holding the session.
105
+ * Malformed files are skipped; callers validate liveness against the
106
+ * process snapshot.
107
+ */
108
+ listActiveLocks(): DevinSessionLock[] {
109
+ let entries: string[];
110
+ try {
111
+ entries = fs.readdirSync(this.locksDir);
112
+ } catch {
113
+ return [];
114
+ }
115
+
116
+ const locks: DevinSessionLock[] = [];
117
+ for (const entry of entries) {
118
+ if (!entry.endsWith(LOCK_FILE_SUFFIX)) continue;
119
+ const sessionId = entry.slice(0, -LOCK_FILE_SUFFIX.length);
120
+ if (!sessionId) continue;
121
+
122
+ try {
123
+ const raw = fs.readFileSync(path.join(this.locksDir, entry), "utf-8").trim();
124
+ const pid = parseInt(raw, 10);
125
+ if (Number.isFinite(pid) && pid > 0) locks.push({ sessionId, pid });
126
+ } catch {
127
+ /* skip unreadable lock */
128
+ }
129
+ }
130
+ return locks;
131
+ }
132
+
133
+ findSessionById(db: Database.Database, sessionId: string): DevinSession | null {
134
+ try {
135
+ const row = db
136
+ .prepare<[string], DevinSessionRow>(`
137
+ SELECT id, working_directory, title, created_at, last_activity_at
138
+ FROM sessions
139
+ WHERE id = ? AND hidden = 0
140
+ `)
141
+ .get(sessionId);
142
+ return row ? toDevinSession(row) : null;
143
+ } catch {
144
+ return null;
145
+ }
146
+ }
147
+
148
+ findSessionForDirectory(db: Database.Database, directory: string): DevinSession | null {
149
+ try {
150
+ const row = db
151
+ .prepare<[string], DevinSessionRow>(`
152
+ SELECT id, working_directory, title, created_at, last_activity_at
153
+ FROM sessions
154
+ WHERE working_directory = ? AND hidden = 0
155
+ ORDER BY last_activity_at DESC
156
+ LIMIT 1
157
+ `)
158
+ .get(directory);
159
+ return row ? toDevinSession(row) : null;
160
+ } catch {
161
+ return null;
162
+ }
163
+ }
164
+
165
+ listSessions(opts?: ListSessionsOptions): SessionSummary[] {
166
+ const db = this.openDb();
167
+ if (!db) return [];
168
+
169
+ try {
170
+ const rows = db
171
+ .prepare<[], DevinSessionRow>(`
172
+ SELECT id, working_directory, title, created_at, last_activity_at
173
+ FROM sessions
174
+ WHERE hidden = 0
175
+ ORDER BY last_activity_at DESC
176
+ `)
177
+ .all();
178
+
179
+ const summaries: SessionSummary[] = [];
180
+ for (const row of rows) {
181
+ if (opts?.cwd !== undefined && row.working_directory !== opts.cwd) continue;
182
+ summaries.push(this.toSessionSummary(db, row));
183
+ }
184
+ return summaries;
185
+ } catch {
186
+ this.close();
187
+ return [];
188
+ }
189
+ }
190
+
191
+ findSessionsById(sessionId: string): SessionSummary[] {
192
+ const db = this.openDb();
193
+ if (!db) return [];
194
+
195
+ try {
196
+ const row = db
197
+ .prepare<[string], DevinSessionRow>(`
198
+ SELECT id, working_directory, title, created_at, last_activity_at
199
+ FROM sessions
200
+ WHERE id = ? AND hidden = 0
201
+ `)
202
+ .get(sessionId);
203
+ return row ? [this.toSessionSummary(db, row)] : [];
204
+ } catch {
205
+ this.close();
206
+ return [];
207
+ }
208
+ }
209
+
210
+ private toSessionSummary(db: Database.Database, row: DevinSessionRow): SessionSummary {
211
+ const session = toDevinSession(row);
212
+ return {
213
+ type: "devin",
214
+ sessionId: session.sessionId,
215
+ cwd: session.directory,
216
+ firstUserMessage: this.parser.getFirstUserMessage(db, session.sessionId),
217
+ lastActive: new Date(session.lastActivityAt),
218
+ startedAt: new Date(session.timeCreated),
219
+ sessionFilePath: encodeDevinSessionRef(this.dbPath, session.sessionId),
220
+ };
221
+ }
222
+
223
+ get dbFilePath(): string {
224
+ return this.dbPath;
225
+ }
226
+
227
+ get sessionLocksDir(): string {
228
+ return this.locksDir;
229
+ }
230
+ }
@@ -0,0 +1,236 @@
1
+ import type { ConversationMessage, ConversationOptions } from "../../adapters/AgentAdapter.js";
2
+ import { sliceTail } from "../../utils/jsonlTail.js";
3
+ import { SUMMARY_MAX_LENGTH, truncate } from "../shared.js";
4
+ import type Database from "better-sqlite3";
5
+
6
+ export interface DevinSessionStats {
7
+ lastRole: string | null;
8
+ /** Epoch milliseconds of the newest message node. */
9
+ lastTimeUpdated: number;
10
+ summary: string;
11
+ }
12
+
13
+ interface DevinChatMessage {
14
+ message_id?: string;
15
+ role?: string;
16
+ content?: unknown;
17
+ tool_calls?: { name?: string; function?: { name?: string } }[];
18
+ thinking?: { thinking?: string } | string;
19
+ name?: string;
20
+ metadata?: { is_user_input?: boolean };
21
+ }
22
+
23
+ interface MessageNodeRow {
24
+ node_id: number;
25
+ chat_message: string;
26
+ }
27
+
28
+ const EMPTY_STATS: DevinSessionStats = {
29
+ lastRole: null,
30
+ lastTimeUpdated: 0,
31
+ summary: "",
32
+ };
33
+
34
+ /** Rows fetched per requested tail message; revisions/tools eat into the window. */
35
+ const TAIL_OVERFETCH = 8;
36
+ const TOOL_CONTENT_MAX = 500;
37
+
38
+ export class DevinSessionParser {
39
+ getConversation(
40
+ db: Database.Database,
41
+ sessionId: string,
42
+ options?: ConversationOptions,
43
+ ): ConversationMessage[] {
44
+ const verbose = options?.verbose ?? false;
45
+ const tail =
46
+ options?.tail && Number.isInteger(options.tail) && options.tail > 0
47
+ ? options.tail
48
+ : undefined;
49
+
50
+ try {
51
+ // LIMIT -1 = unbounded in SQLite; one query serves the full and tail paths.
52
+ const limit = tail ? tail * TAIL_OVERFETCH : -1;
53
+ const rows = db
54
+ .prepare<[string, number], MessageNodeRow>(`
55
+ SELECT node_id, chat_message
56
+ FROM message_nodes
57
+ WHERE session_id = ?
58
+ ORDER BY node_id DESC
59
+ LIMIT ?
60
+ `)
61
+ .all(sessionId, limit)
62
+ .reverse();
63
+
64
+ const messages: ConversationMessage[] = [];
65
+ const latestNodeByMessageId = new Map<string, number>();
66
+ const parsed: { nodeId: number; message: DevinChatMessage }[] = [];
67
+
68
+ for (const row of rows) {
69
+ const message = this.parseChatMessage(row.chat_message);
70
+ if (!message) continue;
71
+ parsed.push({ nodeId: row.node_id, message });
72
+ if (message.message_id) {
73
+ latestNodeByMessageId.set(
74
+ message.message_id,
75
+ Math.max(latestNodeByMessageId.get(message.message_id) ?? 0, row.node_id),
76
+ );
77
+ }
78
+ }
79
+
80
+ for (const { nodeId, message } of parsed) {
81
+ if (
82
+ message.message_id &&
83
+ latestNodeByMessageId.get(message.message_id) !== nodeId
84
+ ) {
85
+ continue;
86
+ }
87
+
88
+ const role = message.role;
89
+ if (role === "system") continue;
90
+
91
+ const content = typeof message.content === "string" ? message.content : "";
92
+
93
+ if (role === "user") {
94
+ if (message.metadata?.is_user_input === false || !content.trim()) continue;
95
+ messages.push({ role: "user", content });
96
+ continue;
97
+ }
98
+
99
+ if (role === "assistant") {
100
+ const thinking =
101
+ typeof message.thinking === "string"
102
+ ? message.thinking
103
+ : message.thinking?.thinking;
104
+ if (verbose && thinking) {
105
+ messages.push({ role: "assistant", content: `[thinking] ${thinking}` });
106
+ }
107
+ if (content.trim()) messages.push({ role: "assistant", content });
108
+ if (verbose) {
109
+ for (const call of message.tool_calls ?? []) {
110
+ const name = call.function?.name ?? call.name ?? "tool";
111
+ messages.push({ role: "assistant", content: `[tool: ${name}]` });
112
+ }
113
+ }
114
+ continue;
115
+ }
116
+
117
+ if (role === "tool" && verbose) {
118
+ const label = message.name ? `[tool: ${message.name}]` : "[tool]";
119
+ messages.push({
120
+ role: "assistant",
121
+ content: `${label} ${truncate(content.trim(), TOOL_CONTENT_MAX)}`.trim(),
122
+ });
123
+ }
124
+ }
125
+
126
+ return sliceTail(messages, tail);
127
+ } catch {
128
+ return [];
129
+ }
130
+ }
131
+
132
+ getSessionStats(db: Database.Database, sessionId: string): DevinSessionStats {
133
+ try {
134
+ const frontier = db
135
+ .prepare<[string], { chat_message: string }>(`
136
+ SELECT chat_message
137
+ FROM message_nodes
138
+ WHERE session_id = ?
139
+ ORDER BY node_id DESC
140
+ LIMIT 1
141
+ `)
142
+ .get(sessionId);
143
+
144
+ const heartbeat = db
145
+ .prepare<[string], { maxCreated: number | null }>(`
146
+ SELECT MAX(created_at) AS maxCreated
147
+ FROM message_nodes
148
+ WHERE session_id = ?
149
+ `)
150
+ .get(sessionId);
151
+
152
+ return {
153
+ lastRole: this.parseChatMessage(frontier?.chat_message ?? "")?.role ?? null,
154
+ lastTimeUpdated: (heartbeat?.maxCreated ?? 0) * 1000,
155
+ summary: this.getLastUserPrompt(db, sessionId),
156
+ };
157
+ } catch {
158
+ return EMPTY_STATS;
159
+ }
160
+ }
161
+
162
+ /**
163
+ * Most recent typed prompt for the "working on" summary, matching the
164
+ * last-user-message convention of the other adapters.
165
+ */
166
+ getLastUserPrompt(db: Database.Database, sessionId: string): string {
167
+ return this.userPrompt(db, sessionId, "DESC");
168
+ }
169
+
170
+ /**
171
+ * First typed prompt for session-list identity (`firstUserMessage`).
172
+ */
173
+ getFirstUserMessage(db: Database.Database, sessionId: string): string {
174
+ return this.userPrompt(db, sessionId, "ASC");
175
+ }
176
+
177
+ /**
178
+ * Typed prompt in the given direction. `prompt_history` is authoritative;
179
+ * shell `!` commands and `/` slash commands are skipped. Falls back to a
180
+ * bounded `is_user_input` node scan in the same direction.
181
+ */
182
+ private userPrompt(
183
+ db: Database.Database,
184
+ sessionId: string,
185
+ direction: "ASC" | "DESC",
186
+ ): string {
187
+ try {
188
+ const prompt = db
189
+ .prepare<[string], { content: string }>(`
190
+ SELECT content
191
+ FROM prompt_history
192
+ WHERE session_id = ? AND is_shell = 0 AND content NOT LIKE '/%'
193
+ ORDER BY timestamp ${direction}
194
+ LIMIT 1
195
+ `)
196
+ .get(sessionId);
197
+ if (prompt?.content?.trim()) return truncate(prompt.content.trim(), SUMMARY_MAX_LENGTH);
198
+ } catch {
199
+ /* fall through to message_nodes */
200
+ }
201
+
202
+ try {
203
+ const rows = db
204
+ .prepare<[string], { chat_message: string }>(`
205
+ SELECT chat_message
206
+ FROM message_nodes
207
+ WHERE session_id = ? AND json_extract(chat_message, '$.role') = 'user'
208
+ ORDER BY node_id ${direction}
209
+ LIMIT 8
210
+ `)
211
+ .all(sessionId);
212
+ for (const row of rows) {
213
+ const message = this.parseChatMessage(row.chat_message);
214
+ if (
215
+ message?.role === "user" &&
216
+ message.metadata?.is_user_input !== false &&
217
+ typeof message.content === "string" &&
218
+ message.content.trim()
219
+ ) {
220
+ return truncate(message.content.trim(), SUMMARY_MAX_LENGTH);
221
+ }
222
+ }
223
+ } catch {
224
+ /* ignore */
225
+ }
226
+ return "";
227
+ }
228
+
229
+ private parseChatMessage(raw: string): DevinChatMessage | null {
230
+ try {
231
+ return JSON.parse(raw) as DevinChatMessage;
232
+ } catch {
233
+ return null;
234
+ }
235
+ }
236
+ }
@@ -0,0 +1,36 @@
1
+ import type {
2
+ AuthReadinessCheck,
3
+ HarnessReadinessProfile,
4
+ ReadinessRuntime,
5
+ } from "../readiness/types.js";
6
+
7
+ const LOGGED_IN_PATTERN = /^Logged in/m;
8
+
9
+ async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
10
+ try {
11
+ const result = await runtime.runCommand("devin", ["auth", "status"]);
12
+ const authenticated = LOGGED_IN_PATTERN.test(result.stdout);
13
+ return {
14
+ state: authenticated ? "authenticated" : "unauthenticated",
15
+ source: "devin auth status",
16
+ provider: null,
17
+ availableProviders: authenticated ? ["devin"] : [],
18
+ status: authenticated ? "pass" : "fail",
19
+ errors: authenticated ? [] : ["Devin is not logged in"],
20
+ };
21
+ } catch {
22
+ return {
23
+ state: "unknown",
24
+ source: "devin auth status",
25
+ provider: null,
26
+ availableProviders: [],
27
+ status: "warn",
28
+ errors: ["Devin authentication probe failed"],
29
+ };
30
+ }
31
+ }
32
+
33
+ export const devinReadiness: HarnessReadinessProfile = {
34
+ configDir: ".config/devin",
35
+ auth: authCheck,
36
+ };
@@ -8,6 +8,7 @@ import { GrokCliAdapter } from "./grok/GrokCliAdapter.js";
8
8
  import { KiroAdapter } from "./kiro/KiroAdapter.js";
9
9
  import { OpenCodeAdapter } from "./opencode/OpenCodeAdapter.js";
10
10
  import { PiAdapter } from "./pi/PiAdapter.js";
11
+ import { DevinAdapter } from "./devin/DevinAdapter.js";
11
12
 
12
13
  /**
13
14
  * One instance of every built-in harness adapter, in registration order
@@ -24,5 +25,6 @@ export function createBuiltinAdapters(): AgentAdapter[] {
24
25
  new AntigravityCliAdapter(),
25
26
  new OpenCodeAdapter(),
26
27
  new PiAdapter(),
28
+ new DevinAdapter(),
27
29
  ];
28
30
  }
@@ -0,0 +1,48 @@
1
+ import type {
2
+ AuthReadinessCheck,
3
+ HarnessReadinessProfile,
4
+ ReadinessRuntime,
5
+ } from "../readiness/types.js";
6
+
7
+ const ANSI_ESCAPE_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-?]*[ -/]*[@-~]`, "g");
8
+
9
+ function authProviderNames(output: string): string[] {
10
+ const names = new Set<string>();
11
+ for (const line of output.replace(ANSI_ESCAPE_PATTERN, "").split(/\r?\n/)) {
12
+ const match = /^\s*[●*+-]\s+(.+?)\s*$/.exec(line);
13
+ if (!match) continue;
14
+ const name = match[1].replace(/\s+(?:api|oauth|[\w-]*token|[A-Z][A-Z0-9_]+)$/i, "").trim();
15
+ if (name && !/^\d+\s+(?:credentials?|environment variables?)$/i.test(name)) names.add(name);
16
+ }
17
+ return [...names].sort();
18
+ }
19
+
20
+ async function authCheck(runtime: ReadinessRuntime): Promise<AuthReadinessCheck> {
21
+ try {
22
+ const result = await runtime.runCommand("opencode", ["auth", "list"]);
23
+ const availableProviders = authProviderNames(result.stdout);
24
+ const authenticated = availableProviders.length > 0;
25
+ return {
26
+ state: authenticated ? "authenticated" : "unauthenticated",
27
+ source: "opencode auth list",
28
+ provider: null,
29
+ availableProviders,
30
+ status: authenticated ? "pass" : "fail",
31
+ errors: authenticated ? [] : ["OpenCode has no configured credentials"],
32
+ };
33
+ } catch {
34
+ return {
35
+ state: "unknown",
36
+ source: "opencode auth list",
37
+ provider: null,
38
+ availableProviders: [],
39
+ status: "warn",
40
+ errors: ["OpenCode authentication probe failed"],
41
+ };
42
+ }
43
+ }
44
+
45
+ export const openCodeReadiness: HarnessReadinessProfile = {
46
+ configDir: ".config/opencode",
47
+ auth: authCheck,
48
+ };
@@ -0,0 +1,129 @@
1
+ import { readFile } from "node:fs/promises";
2
+ import { homedir } from "node:os";
3
+ import { join } from "node:path";
4
+
5
+ type UnknownRecord = Record<string, unknown>;
6
+
7
+ export type PiCredentialOptions = {
8
+ env?: NodeJS.ProcessEnv;
9
+ readFile?: (path: string, encoding: BufferEncoding) => Promise<string>;
10
+ };
11
+
12
+ export type PiAnthropicCredential = {
13
+ kind: "oauth";
14
+ access: string;
15
+ expiresMs: number | null;
16
+ };
17
+
18
+ export type PiOpenAiCredential =
19
+ | { kind: "platform"; key: string }
20
+ | { kind: "oauth"; access: string; accountId: string; expiresMs: number | null };
21
+
22
+ const OPENAI_CREDENTIALS_MISSING =
23
+ "OpenAI credentials not found; set OPENAI_API_KEY, configure the Pi openai provider, or log in to OpenAI via Pi";
24
+ const ZAI_CREDENTIAL_MISSING =
25
+ "z.ai API key not found; set Z_AI_API_KEY or configure Pi provider zai";
26
+
27
+ function record(value: unknown): UnknownRecord | null {
28
+ return value !== null && typeof value === "object" && !Array.isArray(value)
29
+ ? (value as UnknownRecord)
30
+ : null;
31
+ }
32
+
33
+ function nonEmptyText(value: unknown): string | null {
34
+ return typeof value === "string" && value.trim() ? value.trim() : null;
35
+ }
36
+
37
+ function epochMilliseconds(value: unknown): number | null {
38
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) return null;
39
+ return value > 1e12 ? value : value * 1000;
40
+ }
41
+
42
+ async function readPiAuthRoot(options: PiCredentialOptions): Promise<UnknownRecord> {
43
+ const env = options.env ?? process.env;
44
+ const authPath = join(env.HOME || homedir(), ".pi", "agent", "auth.json");
45
+ const contents = await (options.readFile ?? readFile)(authPath, "utf8");
46
+ const parsed = record(JSON.parse(contents));
47
+ if (!parsed) throw new Error("Pi auth file is malformed");
48
+ return parsed;
49
+ }
50
+
51
+ export async function resolvePiAnthropicCredential(
52
+ options: PiCredentialOptions = {},
53
+ ): Promise<PiAnthropicCredential> {
54
+ let root: UnknownRecord;
55
+ try {
56
+ root = await readPiAuthRoot(options);
57
+ } catch (error) {
58
+ if (record(error)?.code === "ENOENT") {
59
+ throw new Error("Pi Anthropic OAuth credentials not found");
60
+ }
61
+ throw new Error("Pi Anthropic auth file is malformed");
62
+ }
63
+
64
+ const credential = record(root.anthropic);
65
+ if (!credential) throw new Error("Pi Anthropic OAuth credentials not found");
66
+ const access = credential.type === "oauth" ? nonEmptyText(credential.access) : null;
67
+ if (!access) throw new Error("Pi Anthropic OAuth credential is malformed");
68
+ return {
69
+ kind: "oauth",
70
+ access,
71
+ expiresMs: epochMilliseconds(credential.expires),
72
+ };
73
+ }
74
+
75
+ export async function resolvePiOpenAiCredential(
76
+ options: PiCredentialOptions = {},
77
+ ): Promise<PiOpenAiCredential> {
78
+ const env = options.env ?? process.env;
79
+ const environmentKey = nonEmptyText(env.OPENAI_API_KEY);
80
+ if (environmentKey) return { kind: "platform", key: environmentKey };
81
+
82
+ let root: UnknownRecord;
83
+ try {
84
+ root = await readPiAuthRoot(options);
85
+ } catch (error) {
86
+ if (record(error)?.code === "ENOENT") throw new Error(OPENAI_CREDENTIALS_MISSING);
87
+ throw new Error("OpenAI Pi auth file is malformed");
88
+ }
89
+
90
+ const platform = record(root.openai);
91
+ if (platform) {
92
+ const key = platform.type === "api_key" ? nonEmptyText(platform.key) : null;
93
+ if (!key) throw new Error("OpenAI Pi auth file has a malformed openai credential");
94
+ return { kind: "platform", key };
95
+ }
96
+
97
+ const oauth = record(root["openai-codex"]);
98
+ const access = oauth?.type === "oauth" ? nonEmptyText(oauth.access) : null;
99
+ const accountId = nonEmptyText(oauth?.accountId);
100
+ if (oauth && access && accountId) {
101
+ return {
102
+ kind: "oauth",
103
+ access,
104
+ accountId,
105
+ expiresMs: epochMilliseconds(oauth.expires),
106
+ };
107
+ }
108
+ throw new Error(OPENAI_CREDENTIALS_MISSING);
109
+ }
110
+
111
+ export async function resolvePiZaiApiKey(options: PiCredentialOptions = {}): Promise<string> {
112
+ const env = options.env ?? process.env;
113
+ const environmentKey = nonEmptyText(env.Z_AI_API_KEY);
114
+ if (environmentKey) return environmentKey;
115
+
116
+ let root: UnknownRecord;
117
+ try {
118
+ root = await readPiAuthRoot(options);
119
+ } catch (error) {
120
+ if (record(error)?.code === "ENOENT") throw new Error(ZAI_CREDENTIAL_MISSING);
121
+ throw new Error("z.ai Pi auth file is malformed");
122
+ }
123
+
124
+ const credential = record(root.zai);
125
+ if (!credential) throw new Error(ZAI_CREDENTIAL_MISSING);
126
+ const key = credential.type === "api_key" ? nonEmptyText(credential.key) : null;
127
+ if (!key) throw new Error("z.ai Pi auth file has a malformed zai credential");
128
+ return key;
129
+ }