@ai-devkit/agent-manager 0.37.0 → 0.38.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 (295) 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/pi/durable/PiCliProbe.test.js +5 -11
  31. package/dist/__tests__/harnesses/pi/durable/PiCliProbe.test.js.map +1 -1
  32. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js +9 -19
  33. package/dist/__tests__/harnesses/pi/durable/PiPrintAgentService.test.js.map +1 -1
  34. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js +3 -7
  35. package/dist/__tests__/harnesses/pi/durable/PiPrintRunner.test.js.map +1 -1
  36. package/dist/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.js +31 -10
  37. package/dist/__tests__/harnesses/readiness/AgentReadiness.test.js.map +1 -0
  38. package/dist/__tests__/harnesses/runtimeProfiles.test.js +89 -0
  39. package/dist/__tests__/harnesses/runtimeProfiles.test.js.map +1 -0
  40. package/dist/__tests__/print/DurableAgent.test.js +4 -6
  41. package/dist/__tests__/print/DurableAgent.test.js.map +1 -1
  42. package/dist/__tests__/print/DurableAgentRepository.test.js +1 -12
  43. package/dist/__tests__/print/DurableAgentRepository.test.js.map +1 -1
  44. package/dist/__tests__/runtime/ManagedAgentRuntime.test.js.map +1 -1
  45. package/dist/__tests__/terminal/TmuxManager.test.js +1 -1
  46. package/dist/__tests__/terminal/TmuxManager.test.js.map +1 -1
  47. package/dist/capacity/index.d.ts +9 -19
  48. package/dist/capacity/index.d.ts.map +1 -1
  49. package/dist/capacity/index.js +11 -4
  50. package/dist/capacity/index.js.map +1 -1
  51. package/dist/capacity/providers/anthropic.d.ts +9 -0
  52. package/dist/capacity/providers/anthropic.d.ts.map +1 -0
  53. package/dist/capacity/{claude.js → providers/anthropic.js} +10 -128
  54. package/dist/capacity/providers/anthropic.js.map +1 -0
  55. package/dist/capacity/providers/openai-platform.d.ts +14 -0
  56. package/dist/capacity/providers/openai-platform.d.ts.map +1 -0
  57. package/dist/capacity/providers/openai-platform.js +120 -0
  58. package/dist/capacity/providers/openai-platform.js.map +1 -0
  59. package/dist/capacity/providers/openai-subscription.d.ts +20 -0
  60. package/dist/capacity/providers/openai-subscription.d.ts.map +1 -0
  61. package/dist/capacity/providers/openai-subscription.js +129 -0
  62. package/dist/capacity/providers/openai-subscription.js.map +1 -0
  63. package/dist/capacity/providers/zai.d.ts +8 -0
  64. package/dist/capacity/providers/zai.d.ts.map +1 -0
  65. package/dist/capacity/{zai.js → providers/zai.js} +17 -62
  66. package/dist/capacity/providers/zai.js.map +1 -0
  67. package/dist/capacity/sources/claude.d.ts +9 -0
  68. package/dist/capacity/sources/claude.d.ts.map +1 -0
  69. package/dist/capacity/sources/claude.js +17 -0
  70. package/dist/capacity/sources/claude.js.map +1 -0
  71. package/dist/capacity/{codex.d.ts → sources/codex.d.ts} +5 -9
  72. package/dist/capacity/sources/codex.d.ts.map +1 -0
  73. package/dist/capacity/{codex.js → sources/codex.js} +31 -84
  74. package/dist/capacity/sources/codex.js.map +1 -0
  75. package/dist/capacity/sources/pi.d.ts +30 -0
  76. package/dist/capacity/sources/pi.d.ts.map +1 -0
  77. package/dist/capacity/sources/pi.js +81 -0
  78. package/dist/capacity/sources/pi.js.map +1 -0
  79. package/dist/capacity/types.d.ts +2 -0
  80. package/dist/capacity/types.d.ts.map +1 -1
  81. package/dist/capacity/types.js +1 -1
  82. package/dist/capacity/types.js.map +1 -1
  83. package/dist/harnesses/claude/credentials.d.ts +10 -0
  84. package/dist/harnesses/claude/credentials.d.ts.map +1 -0
  85. package/dist/harnesses/claude/credentials.js +112 -0
  86. package/dist/harnesses/claude/credentials.js.map +1 -0
  87. package/dist/harnesses/claude/readiness.d.ts +3 -0
  88. package/dist/harnesses/claude/readiness.d.ts.map +1 -0
  89. package/dist/harnesses/claude/readiness.js +63 -0
  90. package/dist/harnesses/claude/readiness.js.map +1 -0
  91. package/dist/harnesses/codex/credentials.d.ts +15 -0
  92. package/dist/harnesses/codex/credentials.d.ts.map +1 -0
  93. package/dist/harnesses/codex/credentials.js +58 -0
  94. package/dist/harnesses/codex/credentials.js.map +1 -0
  95. package/dist/harnesses/codex/readiness.d.ts +3 -0
  96. package/dist/harnesses/codex/readiness.d.ts.map +1 -0
  97. package/dist/harnesses/codex/readiness.js +60 -0
  98. package/dist/harnesses/codex/readiness.js.map +1 -0
  99. package/dist/harnesses/copilot/readiness.d.ts +3 -0
  100. package/dist/harnesses/copilot/readiness.d.ts.map +1 -0
  101. package/dist/harnesses/copilot/readiness.js +39 -0
  102. package/dist/harnesses/copilot/readiness.js.map +1 -0
  103. package/dist/harnesses/opencode/readiness.d.ts +3 -0
  104. package/dist/harnesses/opencode/readiness.d.ts.map +1 -0
  105. package/dist/harnesses/opencode/readiness.js +50 -0
  106. package/dist/harnesses/opencode/readiness.js.map +1 -0
  107. package/dist/harnesses/pi/credentials.d.ts +22 -0
  108. package/dist/harnesses/pi/credentials.d.ts.map +1 -0
  109. package/dist/harnesses/pi/credentials.js +98 -0
  110. package/dist/harnesses/pi/credentials.js.map +1 -0
  111. package/dist/harnesses/pi/readiness.d.ts +3 -0
  112. package/dist/harnesses/pi/readiness.d.ts.map +1 -0
  113. package/dist/harnesses/pi/readiness.js +96 -0
  114. package/dist/harnesses/pi/readiness.js.map +1 -0
  115. package/dist/harnesses/readiness/AgentReadiness.d.ts +6 -0
  116. package/dist/harnesses/readiness/AgentReadiness.d.ts.map +1 -0
  117. package/dist/harnesses/readiness/AgentReadiness.js +68 -0
  118. package/dist/harnesses/readiness/AgentReadiness.js.map +1 -0
  119. package/dist/harnesses/readiness/checks.d.ts +31 -0
  120. package/dist/harnesses/readiness/checks.d.ts.map +1 -0
  121. package/dist/harnesses/readiness/checks.js +270 -0
  122. package/dist/harnesses/readiness/checks.js.map +1 -0
  123. package/dist/{readiness/AgentReadiness.d.ts → harnesses/readiness/types.d.ts} +19 -10
  124. package/dist/harnesses/readiness/types.d.ts.map +1 -0
  125. package/dist/harnesses/readiness/types.js +4 -0
  126. package/dist/harnesses/readiness/types.js.map +1 -0
  127. package/dist/harnesses/runtimeProfiles.d.ts +11 -0
  128. package/dist/harnesses/runtimeProfiles.d.ts.map +1 -0
  129. package/dist/harnesses/runtimeProfiles.js +99 -0
  130. package/dist/harnesses/runtimeProfiles.js.map +1 -0
  131. package/dist/index.d.ts +16 -42
  132. package/dist/index.d.ts.map +1 -1
  133. package/dist/index.js +9 -23
  134. package/dist/index.js.map +1 -1
  135. package/dist/runtime/ManagedAgentRuntime.d.ts +3 -3
  136. package/dist/runtime/ManagedAgentRuntime.d.ts.map +1 -1
  137. package/dist/runtime/ManagedAgentRuntime.js +6 -20
  138. package/dist/runtime/ManagedAgentRuntime.js.map +1 -1
  139. package/dist/runtime/herdr/HerdrAgentDiscovery.d.ts.map +1 -1
  140. package/dist/runtime/herdr/HerdrAgentDiscovery.js +2 -43
  141. package/dist/runtime/herdr/HerdrAgentDiscovery.js.map +1 -1
  142. package/dist/runtime/tmux/PidPolling.d.ts +1 -1
  143. package/dist/runtime/tmux/PidPolling.d.ts.map +1 -1
  144. package/dist/runtime/tmux/PidPolling.js.map +1 -1
  145. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts +1 -1
  146. package/dist/runtime/tmux/TmuxAgentRuntime.d.ts.map +1 -1
  147. package/dist/runtime/tmux/TmuxAgentRuntime.js +1 -1
  148. package/dist/runtime/tmux/TmuxAgentRuntime.js.map +1 -1
  149. package/dist/runtime/tmux/TmuxManager.d.ts +12 -0
  150. package/dist/runtime/tmux/TmuxManager.d.ts.map +1 -0
  151. package/dist/{terminal → runtime/tmux}/TmuxManager.js +9 -28
  152. package/dist/runtime/tmux/TmuxManager.js.map +1 -0
  153. package/dist/runtime/types.d.ts +1 -1
  154. package/dist/runtime/types.d.ts.map +1 -1
  155. package/dist/runtime/types.js.map +1 -1
  156. package/dist/terminal/TerminalFocusManager.d.ts +4 -32
  157. package/dist/terminal/TerminalFocusManager.d.ts.map +1 -1
  158. package/dist/terminal/TerminalFocusManager.js +17 -267
  159. package/dist/terminal/TerminalFocusManager.js.map +1 -1
  160. package/dist/terminal/TtyWriter.d.ts +1 -44
  161. package/dist/terminal/TtyWriter.d.ts.map +1 -1
  162. package/dist/terminal/TtyWriter.js +11 -347
  163. package/dist/terminal/TtyWriter.js.map +1 -1
  164. package/dist/terminal/backends/index.d.ts +5 -0
  165. package/dist/terminal/backends/index.d.ts.map +1 -0
  166. package/dist/terminal/backends/index.js +19 -0
  167. package/dist/terminal/backends/index.js.map +1 -0
  168. package/dist/terminal/backends/iterm2.d.ts +3 -0
  169. package/dist/terminal/backends/iterm2.d.ts.map +1 -0
  170. package/dist/terminal/backends/iterm2.js +135 -0
  171. package/dist/terminal/backends/iterm2.js.map +1 -0
  172. package/dist/terminal/backends/shared.d.ts +7 -0
  173. package/dist/terminal/backends/shared.d.ts.map +1 -0
  174. package/dist/terminal/backends/shared.js +35 -0
  175. package/dist/terminal/backends/shared.js.map +1 -0
  176. package/dist/terminal/backends/terminal-app.d.ts +3 -0
  177. package/dist/terminal/backends/terminal-app.d.ts.map +1 -0
  178. package/dist/terminal/backends/terminal-app.js +129 -0
  179. package/dist/terminal/backends/terminal-app.js.map +1 -0
  180. package/dist/terminal/backends/tmux.d.ts +3 -0
  181. package/dist/terminal/backends/tmux.d.ts.map +1 -0
  182. package/dist/terminal/backends/tmux.js +76 -0
  183. package/dist/terminal/backends/tmux.js.map +1 -0
  184. package/dist/terminal/backends/wezterm.d.ts +3 -0
  185. package/dist/terminal/backends/wezterm.d.ts.map +1 -0
  186. package/dist/terminal/backends/wezterm.js +71 -0
  187. package/dist/terminal/backends/wezterm.js.map +1 -0
  188. package/dist/terminal/types.d.ts +21 -0
  189. package/dist/terminal/types.d.ts.map +1 -0
  190. package/dist/terminal/types.js +10 -0
  191. package/dist/terminal/types.js.map +1 -0
  192. package/package.json +1 -1
  193. package/src/AgentManager.ts +1 -1
  194. package/src/__tests__/capacity/anthropic.test.ts +105 -0
  195. package/src/__tests__/capacity/boundaries.test.ts +36 -0
  196. package/src/__tests__/capacity/claude.test.ts +3 -8
  197. package/src/__tests__/capacity/codex.test.ts +12 -35
  198. package/src/__tests__/capacity/fixtures/openai-codex-auth.json +1 -1
  199. package/src/__tests__/capacity/openai.test.ts +15 -15
  200. package/src/__tests__/capacity/wham.test.ts +1 -1
  201. package/src/__tests__/capacity/zai.test.ts +27 -62
  202. package/src/__tests__/harnesses/claude/durable/ClaudePrintAgent.integration.test.ts +2 -2
  203. package/src/__tests__/harnesses/claude/durable/ClaudePrintRunner.test.ts +1 -1
  204. package/src/__tests__/harnesses/codex/durable/CodexCliProbe.test.ts +14 -19
  205. package/src/__tests__/harnesses/codex/durable/CodexPrintAgent.integration.test.ts +3 -3
  206. package/src/__tests__/harnesses/codex/durable/CodexPrintAgentService.test.ts +24 -30
  207. package/src/__tests__/harnesses/codex/durable/CodexPrintRunner.test.ts +4 -8
  208. package/src/__tests__/harnesses/pi/durable/PiCliProbe.test.ts +7 -11
  209. package/src/__tests__/harnesses/pi/durable/PiPrintAgentService.test.ts +19 -22
  210. package/src/__tests__/harnesses/pi/durable/PiPrintRunner.test.ts +7 -8
  211. package/src/__tests__/{readiness → harnesses/readiness}/AgentReadiness.test.ts +36 -9
  212. package/src/__tests__/harnesses/runtimeProfiles.test.ts +110 -0
  213. package/src/__tests__/print/DurableAgent.test.ts +4 -13
  214. package/src/__tests__/print/DurableAgentRepository.test.ts +1 -13
  215. package/src/__tests__/runtime/ManagedAgentRuntime.test.ts +1 -1
  216. package/src/__tests__/terminal/TmuxManager.test.ts +1 -1
  217. package/src/capacity/index.ts +29 -23
  218. package/src/capacity/{claude.ts → providers/anthropic.ts} +16 -131
  219. package/src/capacity/providers/openai-platform.ts +150 -0
  220. package/src/capacity/{wham.ts → providers/openai-subscription.ts} +86 -8
  221. package/src/capacity/{zai.ts → providers/zai.ts} +29 -111
  222. package/src/capacity/sources/claude.ts +30 -0
  223. package/src/capacity/{codex.ts → sources/codex.ts} +54 -124
  224. package/src/capacity/sources/pi.ts +131 -0
  225. package/src/capacity/types.ts +3 -0
  226. package/src/harnesses/claude/credentials.ts +122 -0
  227. package/src/harnesses/claude/readiness.ts +69 -0
  228. package/src/harnesses/codex/credentials.ts +81 -0
  229. package/src/harnesses/codex/readiness.ts +72 -0
  230. package/src/harnesses/copilot/readiness.ts +33 -0
  231. package/src/harnesses/opencode/readiness.ts +48 -0
  232. package/src/harnesses/pi/credentials.ts +129 -0
  233. package/src/harnesses/pi/readiness.ts +104 -0
  234. package/src/harnesses/readiness/AgentReadiness.ts +87 -0
  235. package/src/harnesses/readiness/checks.ts +336 -0
  236. package/src/harnesses/readiness/types.ts +90 -0
  237. package/src/{utils/agents.ts → harnesses/runtimeProfiles.ts} +34 -38
  238. package/src/index.ts +15 -128
  239. package/src/runtime/ManagedAgentRuntime.ts +7 -22
  240. package/src/runtime/herdr/HerdrAgentDiscovery.ts +3 -19
  241. package/src/runtime/tmux/PidPolling.ts +1 -1
  242. package/src/runtime/tmux/TmuxAgentRuntime.ts +1 -1
  243. package/src/{terminal → runtime/tmux}/TmuxManager.ts +9 -30
  244. package/src/runtime/types.ts +1 -1
  245. package/src/terminal/TerminalFocusManager.ts +19 -292
  246. package/src/terminal/TtyWriter.ts +16 -336
  247. package/src/terminal/backends/index.ts +21 -0
  248. package/src/terminal/backends/iterm2.ts +126 -0
  249. package/src/terminal/backends/shared.ts +42 -0
  250. package/src/terminal/backends/terminal-app.ts +122 -0
  251. package/src/terminal/backends/tmux.ts +62 -0
  252. package/src/terminal/backends/wezterm.ts +65 -0
  253. package/src/terminal/types.ts +22 -0
  254. package/dist/__tests__/readiness/AgentReadiness.test.js.map +0 -1
  255. package/dist/__tests__/utils/agents.test.js +0 -93
  256. package/dist/__tests__/utils/agents.test.js.map +0 -1
  257. package/dist/capacity/claude.d.ts +0 -17
  258. package/dist/capacity/claude.d.ts.map +0 -1
  259. package/dist/capacity/claude.js.map +0 -1
  260. package/dist/capacity/codex.d.ts.map +0 -1
  261. package/dist/capacity/codex.js.map +0 -1
  262. package/dist/capacity/openai.d.ts +0 -47
  263. package/dist/capacity/openai.d.ts.map +0 -1
  264. package/dist/capacity/openai.js +0 -307
  265. package/dist/capacity/openai.js.map +0 -1
  266. package/dist/capacity/wham.d.ts +0 -16
  267. package/dist/capacity/wham.d.ts.map +0 -1
  268. package/dist/capacity/wham.js +0 -66
  269. package/dist/capacity/wham.js.map +0 -1
  270. package/dist/capacity/zai.d.ts +0 -14
  271. package/dist/capacity/zai.d.ts.map +0 -1
  272. package/dist/capacity/zai.js.map +0 -1
  273. package/dist/readiness/AgentReadiness.d.ts.map +0 -1
  274. package/dist/readiness/AgentReadiness.js +0 -614
  275. package/dist/readiness/AgentReadiness.js.map +0 -1
  276. package/dist/terminal/TmuxManager.d.ts +0 -28
  277. package/dist/terminal/TmuxManager.d.ts.map +0 -1
  278. package/dist/terminal/TmuxManager.js.map +0 -1
  279. package/dist/terminal/index.d.ts +0 -5
  280. package/dist/terminal/index.d.ts.map +0 -1
  281. package/dist/terminal/index.js +0 -5
  282. package/dist/terminal/index.js.map +0 -1
  283. package/dist/utils/agents.d.ts +0 -16
  284. package/dist/utils/agents.d.ts.map +0 -1
  285. package/dist/utils/agents.js +0 -117
  286. package/dist/utils/agents.js.map +0 -1
  287. package/dist/utils/index.d.ts +0 -8
  288. package/dist/utils/index.d.ts.map +0 -1
  289. package/dist/utils/index.js +0 -6
  290. package/dist/utils/index.js.map +0 -1
  291. package/src/__tests__/utils/agents.test.ts +0 -105
  292. package/src/capacity/openai.ts +0 -355
  293. package/src/readiness/AgentReadiness.ts +0 -730
  294. package/src/terminal/index.ts +0 -4
  295. package/src/utils/index.ts +0 -14
@@ -0,0 +1,87 @@
1
+ import { claudeReadiness } from "../claude/readiness.js";
2
+ import { codexReadiness } from "../codex/readiness.js";
3
+ import { copilotReadiness } from "../copilot/readiness.js";
4
+ import { openCodeReadiness } from "../opencode/readiness.js";
5
+ import { piReadiness } from "../pi/readiness.js";
6
+ import {
7
+ builtInSkillsCheck,
8
+ createReadinessRuntime,
9
+ directoryCheck,
10
+ executableCheck,
11
+ worstReadinessStatus,
12
+ } from "./checks.js";
13
+ import {
14
+ READINESS_AGENT_TYPES,
15
+ type AgentReadinessOptions,
16
+ type AgentReadinessReport,
17
+ type HarnessReadinessProfile,
18
+ type ReadinessAgentType,
19
+ type ReadinessRuntime,
20
+ } from "./types.js";
21
+
22
+ const READINESS_PROFILES: Record<ReadinessAgentType, HarnessReadinessProfile> = {
23
+ claude: claudeReadiness,
24
+ codex: codexReadiness,
25
+ gemini_cli: { configDir: ".gemini" },
26
+ grok_cli: { configDir: ".grok" },
27
+ kiro: { configDir: ".kiro" },
28
+ antigravity_cli: { configDir: ".gemini/antigravity-cli" },
29
+ opencode: openCodeReadiness,
30
+ copilot: copilotReadiness,
31
+ pi: piReadiness,
32
+ };
33
+
34
+ export async function getAgentReadinessReport(
35
+ agent: ReadinessAgentType,
36
+ options: AgentReadinessOptions = {},
37
+ ): Promise<AgentReadinessReport> {
38
+ return agentReadiness(agent, createReadinessRuntime(options));
39
+ }
40
+
41
+ async function agentReadiness(
42
+ agent: ReadinessAgentType,
43
+ runtime: ReadinessRuntime,
44
+ ): Promise<AgentReadinessReport> {
45
+ const profile = READINESS_PROFILES[agent];
46
+ const [executable, globalConfig, builtInSkills, auth, integration] = await Promise.all([
47
+ executableCheck(agent, runtime),
48
+ directoryCheck(profile.configDir, runtime),
49
+ builtInSkillsCheck(agent, runtime),
50
+ profile.auth?.(runtime),
51
+ profile.integration?.(runtime),
52
+ ]);
53
+ return {
54
+ type: agent,
55
+ executable,
56
+ globalConfig,
57
+ builtInSkills,
58
+ auth,
59
+ integration,
60
+ status: worstReadinessStatus([
61
+ executable.status,
62
+ globalConfig.status,
63
+ ...(auth ? [auth.status] : []),
64
+ ...(integration ? [integration.status] : []),
65
+ ]),
66
+ };
67
+ }
68
+
69
+ export async function getAgentReadinessReports(
70
+ options: AgentReadinessOptions = {},
71
+ ): Promise<Record<ReadinessAgentType, AgentReadinessReport>> {
72
+ const runtime = createReadinessRuntime(options);
73
+ const entries = await Promise.all(
74
+ READINESS_AGENT_TYPES.map(
75
+ async (agent) => [agent, await agentReadiness(agent, runtime)] as const,
76
+ ),
77
+ );
78
+ return Object.fromEntries(entries) as Record<ReadinessAgentType, AgentReadinessReport>;
79
+ }
80
+
81
+ export { worstReadinessStatus } from "./checks.js";
82
+ export type {
83
+ AgentReadinessOptions,
84
+ AgentReadinessReport,
85
+ ReadinessAgentType,
86
+ ReadinessStatus,
87
+ } from "./types.js";
@@ -0,0 +1,336 @@
1
+ import { execFile } from "node:child_process";
2
+ import { constants } from "node:fs";
3
+ import { access as fsAccess, readFile as fsReadFile } from "node:fs/promises";
4
+ import { delimiter, join } from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { getCodexCapacityReport } from "../../capacity/index.js";
7
+ import { HARNESS_RUNTIME_PROFILES } from "../runtimeProfiles.js";
8
+ import type {
9
+ AgentReadinessOptions,
10
+ BuiltInSkillsReadinessCheck,
11
+ DirectoryReadinessCheck,
12
+ ExecutableReadinessCheck,
13
+ ReadinessAgentType,
14
+ ReadinessCheck,
15
+ ReadinessRuntime,
16
+ ReadinessStatus,
17
+ } from "./types.js";
18
+
19
+ const execFileAsync = promisify(execFile);
20
+
21
+ export function createReadinessRuntime(options: AgentReadinessOptions): ReadinessRuntime {
22
+ return {
23
+ homeDir: options.homeDir ?? process.env.HOME ?? "",
24
+ path: options.path ?? process.env.PATH ?? "",
25
+ assetRoot: options.assetRoot ?? null,
26
+ builtInSkillNames: options.builtInSkillNames ?? [],
27
+ skillRoots: options.skillRoots ?? {},
28
+ readFile: options.readFile ?? ((target) => fsReadFile(target, "utf8")),
29
+ access: options.access ?? ((target, mode = constants.R_OK) => fsAccess(target, mode)),
30
+ runCommand: options.runCommand ?? defaultRunCommand,
31
+ codexAuth: options.codexAuth ?? (async () => (await getCodexCapacityReport()).authenticated),
32
+ };
33
+ }
34
+
35
+ async function defaultRunCommand(command: string, args: string[]) {
36
+ const result = await execFileAsync(command, args, {
37
+ encoding: "utf8",
38
+ timeout: 5000,
39
+ maxBuffer: 1024 * 1024,
40
+ });
41
+ return { stdout: result.stdout, stderr: result.stderr };
42
+ }
43
+
44
+ function statusRank(status: ReadinessStatus): number {
45
+ return status === "fail" ? 2 : status === "warn" ? 1 : 0;
46
+ }
47
+
48
+ export function worstReadinessStatus(statuses: ReadinessStatus[]): ReadinessStatus {
49
+ return statuses.reduce<ReadinessStatus>(
50
+ (worst, current) => (statusRank(current) > statusRank(worst) ? current : worst),
51
+ "pass",
52
+ );
53
+ }
54
+
55
+ export function displayHome(target: string, homeDir: string): string {
56
+ return target === homeDir
57
+ ? "~"
58
+ : target.startsWith(`${homeDir}/`)
59
+ ? `~${target.slice(homeDir.length)}`
60
+ : target;
61
+ }
62
+
63
+ export function record(value: unknown): Record<string, unknown> | null {
64
+ return value !== null && typeof value === "object" && !Array.isArray(value)
65
+ ? (value as Record<string, unknown>)
66
+ : null;
67
+ }
68
+
69
+ export function nonEmpty(value: unknown): boolean {
70
+ return typeof value === "string" && value.trim().length > 0;
71
+ }
72
+
73
+ export async function accessible(
74
+ target: string,
75
+ runtime: ReadinessRuntime,
76
+ mode = constants.R_OK,
77
+ ): Promise<boolean> {
78
+ try {
79
+ await runtime.access(target, mode);
80
+ return true;
81
+ } catch {
82
+ return false;
83
+ }
84
+ }
85
+
86
+ async function resolveExecutable(
87
+ command: string,
88
+ runtime: ReadinessRuntime,
89
+ ): Promise<string | null> {
90
+ const candidates = runtime.path
91
+ .split(delimiter)
92
+ .filter(Boolean)
93
+ .map((directory) => join(directory, command));
94
+ const checks = await Promise.all(
95
+ candidates.map(async (target) => ({
96
+ target,
97
+ available: await accessible(target, runtime, constants.X_OK),
98
+ })),
99
+ );
100
+ return checks.find((check) => check.available)?.target ?? null;
101
+ }
102
+
103
+ export async function executableCheck(
104
+ agent: ReadinessAgentType,
105
+ runtime: ReadinessRuntime,
106
+ ): Promise<ExecutableReadinessCheck> {
107
+ const command = HARNESS_RUNTIME_PROFILES[agent].command;
108
+ const resolvedPath = await resolveExecutable(command, runtime);
109
+ return {
110
+ command,
111
+ path: resolvedPath,
112
+ status: resolvedPath ? "pass" : "fail",
113
+ errors: resolvedPath ? [] : [`${command} was not found on PATH`],
114
+ };
115
+ }
116
+
117
+ export async function directoryCheck(
118
+ configDir: string,
119
+ runtime: ReadinessRuntime,
120
+ ): Promise<DirectoryReadinessCheck> {
121
+ const target = join(runtime.homeDir, configDir);
122
+ const readable = await accessible(target, runtime);
123
+ return {
124
+ path: displayHome(target, runtime.homeDir),
125
+ present: readable,
126
+ readable,
127
+ status: readable ? "pass" : "fail",
128
+ errors: readable ? [] : ["global configuration directory is unavailable"],
129
+ };
130
+ }
131
+
132
+ export async function builtInSkillsCheck(
133
+ agent: ReadinessAgentType,
134
+ runtime: ReadinessRuntime,
135
+ ): Promise<BuiltInSkillsReadinessCheck> {
136
+ const relativeRoot = runtime.skillRoots[agent];
137
+ if (!relativeRoot) {
138
+ return {
139
+ path: null,
140
+ required: runtime.builtInSkillNames.length,
141
+ present: 0,
142
+ missing: [...runtime.builtInSkillNames],
143
+ status: "info",
144
+ errors: [],
145
+ };
146
+ }
147
+ const root = join(runtime.homeDir, relativeRoot);
148
+ const checks = await Promise.all(
149
+ runtime.builtInSkillNames.map(async (name) => ({
150
+ name,
151
+ present: await accessible(join(root, name, "SKILL.md"), runtime),
152
+ })),
153
+ );
154
+ const present = checks.filter((check) => check.present).map((check) => check.name);
155
+ const missing = runtime.builtInSkillNames.filter((name) => !present.includes(name));
156
+ return {
157
+ path: displayHome(root, runtime.homeDir),
158
+ required: runtime.builtInSkillNames.length,
159
+ present: present.length,
160
+ missing: [...missing],
161
+ status: "info",
162
+ errors: [],
163
+ };
164
+ }
165
+
166
+ export async function scriptCheck(
167
+ installed: string,
168
+ bundled: string,
169
+ runtime: ReadinessRuntime,
170
+ ): Promise<
171
+ ReadinessCheck & {
172
+ path: string;
173
+ present: boolean;
174
+ readable: boolean;
175
+ matchesBundledAsset: boolean;
176
+ }
177
+ > {
178
+ let installedText: string;
179
+ try {
180
+ installedText = await runtime.readFile(installed);
181
+ } catch {
182
+ return {
183
+ path: displayHome(installed, runtime.homeDir),
184
+ present: false,
185
+ readable: false,
186
+ matchesBundledAsset: false,
187
+ status: "fail",
188
+ errors: ["hook script is unavailable"],
189
+ };
190
+ }
191
+ try {
192
+ const bundledText = await runtime.readFile(bundled);
193
+ const matches = installedText === bundledText;
194
+ return {
195
+ path: displayHome(installed, runtime.homeDir),
196
+ present: true,
197
+ readable: true,
198
+ matchesBundledAsset: matches,
199
+ status: matches ? "pass" : "fail",
200
+ errors: matches ? [] : ["hook script differs from the bundled AI DevKit asset"],
201
+ };
202
+ } catch {
203
+ return {
204
+ path: displayHome(installed, runtime.homeDir),
205
+ present: true,
206
+ readable: true,
207
+ matchesBundledAsset: false,
208
+ status: "fail",
209
+ errors: ["bundled hook asset is unavailable"],
210
+ };
211
+ }
212
+ }
213
+
214
+ function containsHook(root: unknown, event: string, command: string): boolean {
215
+ const hooks = record(record(root)?.hooks);
216
+ const entries = hooks?.[event];
217
+ if (!Array.isArray(entries)) return false;
218
+ return entries.some((entry) => {
219
+ const commands = record(entry)?.hooks;
220
+ return (
221
+ Array.isArray(commands) &&
222
+ commands.some((hook) => {
223
+ const item = record(hook);
224
+ return item?.type === "command" && item.command === command;
225
+ })
226
+ );
227
+ });
228
+ }
229
+
230
+ export async function registrationCheck(
231
+ target: string,
232
+ event: string,
233
+ command: string,
234
+ runtime: ReadinessRuntime,
235
+ ): Promise<
236
+ ReadinessCheck & {
237
+ path: string;
238
+ event: string;
239
+ command: string;
240
+ present: boolean;
241
+ valid: boolean;
242
+ }
243
+ > {
244
+ try {
245
+ const parsed = JSON.parse(await runtime.readFile(target));
246
+ const valid = containsHook(parsed, event, command);
247
+ return {
248
+ path: displayHome(target, runtime.homeDir),
249
+ event,
250
+ command,
251
+ present: true,
252
+ valid,
253
+ status: valid ? "pass" : "fail",
254
+ errors: valid ? [] : ["required hook registration is missing"],
255
+ };
256
+ } catch {
257
+ return {
258
+ path: displayHome(target, runtime.homeDir),
259
+ event,
260
+ command,
261
+ present: false,
262
+ valid: false,
263
+ status: "fail",
264
+ errors: ["hook configuration is missing or invalid"],
265
+ };
266
+ }
267
+ }
268
+
269
+ export async function mappingCheck(
270
+ target: string,
271
+ runtime: ReadinessRuntime,
272
+ ): Promise<
273
+ ReadinessCheck & {
274
+ path: string;
275
+ present: boolean;
276
+ valid: boolean;
277
+ invalidEntries: number;
278
+ staleEntries: number;
279
+ }
280
+ > {
281
+ let text: string;
282
+ try {
283
+ text = await runtime.readFile(target);
284
+ } catch {
285
+ return {
286
+ path: displayHome(target, runtime.homeDir),
287
+ present: false,
288
+ valid: false,
289
+ invalidEntries: 0,
290
+ staleEntries: 0,
291
+ status: "warn",
292
+ errors: ["session mapping has not been created"],
293
+ };
294
+ }
295
+ let parsed: Record<string, unknown> | null = null;
296
+ try {
297
+ parsed = record(JSON.parse(text));
298
+ } catch {
299
+ // Fixed safe error below.
300
+ }
301
+ if (!parsed) {
302
+ return {
303
+ path: displayHome(target, runtime.homeDir),
304
+ present: true,
305
+ valid: false,
306
+ invalidEntries: 0,
307
+ staleEntries: 0,
308
+ status: "fail",
309
+ errors: ["session mapping is invalid"],
310
+ };
311
+ }
312
+ const entries = await Promise.all(
313
+ Object.entries(parsed).map(async ([pid, sessionPath]) => {
314
+ if (!/^\d+$/.test(pid) || typeof sessionPath !== "string" || !sessionPath) {
315
+ return { valid: false, stale: false };
316
+ }
317
+ return { valid: true, stale: !(await accessible(sessionPath, runtime)) };
318
+ }),
319
+ );
320
+ const invalidEntries = entries.filter((entry) => !entry.valid).length;
321
+ const staleEntries = entries.filter((entry) => entry.stale).length;
322
+ const valid = invalidEntries === 0;
323
+ return {
324
+ path: displayHome(target, runtime.homeDir),
325
+ present: true,
326
+ valid,
327
+ invalidEntries,
328
+ staleEntries,
329
+ status: !valid ? "fail" : staleEntries ? "warn" : "pass",
330
+ errors: !valid
331
+ ? ["session mapping contains invalid entries"]
332
+ : staleEntries
333
+ ? ["session mapping contains stale entries"]
334
+ : [],
335
+ };
336
+ }
@@ -0,0 +1,90 @@
1
+ import { AGENT_TYPES } from "../../adapters/AgentAdapter.js";
2
+
3
+ export const READINESS_AGENT_TYPES = AGENT_TYPES;
4
+
5
+ export type ReadinessStatus = "pass" | "warn" | "fail";
6
+ export type ReadinessAuthState = "authenticated" | "unauthenticated" | "unknown";
7
+ export type ReadinessAgentType = (typeof READINESS_AGENT_TYPES)[number];
8
+ type ReadinessInfoStatus = ReadinessStatus | "info";
9
+
10
+ export type CommandResult = { stdout: string; stderr: string };
11
+ export type ReadFile = (target: string) => Promise<string>;
12
+ export type Access = (target: string, mode?: number) => Promise<void>;
13
+ export type RunCommand = (command: string, args: string[]) => Promise<CommandResult>;
14
+
15
+ export interface ReadinessCheck<S extends ReadinessInfoStatus = ReadinessStatus> {
16
+ status: S;
17
+ errors: string[];
18
+ }
19
+
20
+ export interface ExecutableReadinessCheck extends ReadinessCheck {
21
+ command: string;
22
+ path: string | null;
23
+ }
24
+
25
+ export interface DirectoryReadinessCheck extends ReadinessCheck {
26
+ path: string;
27
+ present: boolean;
28
+ readable: boolean;
29
+ }
30
+
31
+ export interface BuiltInSkillsReadinessCheck extends ReadinessCheck<"info"> {
32
+ path: string | null;
33
+ required: number;
34
+ present: number;
35
+ missing: string[];
36
+ }
37
+
38
+ export interface AuthReadinessCheck extends ReadinessCheck {
39
+ state: ReadinessAuthState;
40
+ source: string;
41
+ provider: string | null;
42
+ availableProviders: string[];
43
+ }
44
+
45
+ export interface IntegrationReadinessCheck extends ReadinessCheck {
46
+ label: string;
47
+ installed: boolean;
48
+ details?: Record<string, unknown>;
49
+ }
50
+
51
+ export interface AgentReadinessReport {
52
+ type: ReadinessAgentType;
53
+ executable: ExecutableReadinessCheck;
54
+ globalConfig: DirectoryReadinessCheck;
55
+ builtInSkills: BuiltInSkillsReadinessCheck;
56
+ auth?: AuthReadinessCheck;
57
+ integration?: IntegrationReadinessCheck;
58
+ status: ReadinessStatus;
59
+ }
60
+
61
+ export interface AgentReadinessOptions {
62
+ homeDir?: string;
63
+ path?: string;
64
+ assetRoot?: string;
65
+ builtInSkillNames?: readonly string[];
66
+ skillRoots?: Partial<Record<ReadinessAgentType, string>>;
67
+ readFile?: ReadFile;
68
+ access?: Access;
69
+ runCommand?: RunCommand;
70
+ codexAuth?: () => Promise<boolean | null>;
71
+ }
72
+
73
+ export type ReadinessRuntime = Required<
74
+ Omit<AgentReadinessOptions, "assetRoot" | "builtInSkillNames" | "skillRoots">
75
+ > & {
76
+ assetRoot: string | null;
77
+ builtInSkillNames: readonly string[];
78
+ skillRoots: Partial<Record<ReadinessAgentType, string>>;
79
+ };
80
+
81
+ export type AuthReadinessProbe = (runtime: ReadinessRuntime) => Promise<AuthReadinessCheck>;
82
+ export type IntegrationReadinessProbe = (
83
+ runtime: ReadinessRuntime,
84
+ ) => Promise<IntegrationReadinessCheck>;
85
+
86
+ export interface HarnessReadinessProfile {
87
+ configDir: string;
88
+ auth?: AuthReadinessProbe;
89
+ integration?: IntegrationReadinessProbe;
90
+ }
@@ -1,44 +1,46 @@
1
- import path from "path";
2
- import type { AGENT_TYPES } from "../adapters/AgentAdapter.js";
3
- import { executablePath } from "./process.js";
1
+ import path from "node:path";
2
+ import { AGENT_TYPES, type AgentType } from "../adapters/AgentAdapter.js";
3
+ import { executablePath } from "../utils/process.js";
4
4
 
5
- /** Every built-in harness can be started, so each needs an {@link AGENTS} entry. */
6
5
  export type StartableAgentType = (typeof AGENT_TYPES)[number];
7
6
 
8
- export interface AgentConfig {
9
- /** Shell command to launch the agent (sent to tmux via `send-keys`). */
7
+ export interface HarnessRuntimeProfile {
10
8
  command: string;
11
- /** Returns true if the given `ps` command line belongs to this agent. */
9
+ runtimeKind: string;
10
+ discoveryAliases: readonly string[];
12
11
  matches: (psCommand: string) => boolean;
13
12
  }
14
13
 
15
- /**
16
- * Per-agent configuration: launch command plus a matcher that recognizes the
17
- * agent's process in `ps` output. Each matcher knows that agent's distribution
18
- * quirks (e.g. gemini ships as a Node script so its real binary is in argv[1..]).
19
- */
20
- export const AGENTS: Record<StartableAgentType, AgentConfig> = {
21
- claude: { command: "claude", matches: matchArgv0("claude") },
22
- codex: { command: "codex", matches: matchArgv0("codex") },
23
- copilot: { command: "copilot", matches: matchArgv0Name("copilot-cli") },
24
- gemini_cli: { command: "gemini", matches: matchAnyToken("gemini") },
25
- grok_cli: { command: "grok", matches: matchArgv0("grok") },
26
- antigravity_cli: { command: "agy", matches: matchArgv0("agy") },
27
- opencode: { command: "opencode", matches: matchArgv0("opencode") },
28
- pi: { command: "pi", matches: matchAnyBasename(["pi"]) },
29
- kiro: { command: "kiro-cli", matches: matchAnyBasename(["kiro-cli", "kiro"]) },
14
+ export const HARNESS_RUNTIME_PROFILES: Record<StartableAgentType, HarnessRuntimeProfile> = {
15
+ claude: profile("claude", matchArgv0("claude")),
16
+ codex: profile("codex", matchArgv0("codex")),
17
+ copilot: profile("copilot", matchArgv0Name("copilot-cli")),
18
+ gemini_cli: profile("gemini", matchAnyBasename(["gemini"]), ["gemini_cli"]),
19
+ grok_cli: profile("grok", matchArgv0("grok"), ["grok_cli"]),
20
+ antigravity_cli: profile("agy", matchArgv0("agy"), ["antigravity", "antigravity_cli"]),
21
+ opencode: profile("opencode", matchArgv0("opencode")),
22
+ pi: profile("pi", matchAnyBasename(["pi"])),
23
+ kiro: profile("kiro", matchAnyBasename(["kiro-cli", "kiro"]), ["kiro_cli", "kiro-cli"]),
30
24
  };
31
25
 
32
- /**
33
- * Matchers read a `ps` command line, where argv is joined with spaces. argv[0]
34
- * is resolved with {@link executablePath} so executables installed under
35
- * directories containing spaces are recognised, while arguments are never
36
- * folded into the executable path unless that combined path is a real file.
37
- *
38
- * The filesystem is consulted only when a token after the first could be part
39
- * of a matching argv[0]; commands whose first token already decides the match,
40
- * or that contain no candidate token at all, are handled with string checks.
41
- */
26
+ export function runtimeAgentMatchesHarness(runtimeAgent: string, type: AgentType): boolean {
27
+ if (type === "other") return runtimeAgent.toLowerCase() === "other";
28
+ return HARNESS_RUNTIME_PROFILES[type].discoveryAliases.includes(runtimeAgent.toLowerCase());
29
+ }
30
+
31
+ function profile(
32
+ runtimeKind: string,
33
+ matches: (psCommand: string) => boolean,
34
+ extraAliases: readonly string[] = [],
35
+ ): HarnessRuntimeProfile {
36
+ return {
37
+ command: runtimeKind === "kiro" ? "kiro-cli" : runtimeKind,
38
+ runtimeKind,
39
+ discoveryAliases: [runtimeKind, ...extraAliases],
40
+ matches,
41
+ };
42
+ }
43
+
42
44
  const ABSOLUTE_PATH = /^(?:\/|[a-zA-Z]:[\\/])/;
43
45
  const PATH_SEPARATOR = /[\\/]/;
44
46
 
@@ -50,14 +52,12 @@ function tokensOf(psCommand: string): string[] {
50
52
  return psCommand.trim().split(/\s+/).filter(Boolean);
51
53
  }
52
54
 
53
- /** Split a command line into its resolved argv[0] and the remaining argument tokens. */
54
55
  function splitArgv0(psCommand: string): { argv0: string; args: string[] } {
55
56
  const command = psCommand.trim();
56
57
  const argv0 = executablePath(command);
57
58
  return { argv0, args: tokensOf(command.slice(argv0.length)) };
58
59
  }
59
60
 
60
- /** Whether argv[0] may span several tokens, i.e. a later path-like token satisfies `test`. */
61
61
  function mayContinueArgv0(tokens: string[], test: (token: string) => boolean): boolean {
62
62
  return (
63
63
  ABSOLUTE_PATH.test(tokens[0]) &&
@@ -87,10 +87,6 @@ function matchArgv0Name(name: string): (psCommand: string) => boolean {
87
87
  };
88
88
  }
89
89
 
90
- function matchAnyToken(name: string): (psCommand: string) => boolean {
91
- return matchAnyBasename([name]);
92
- }
93
-
94
90
  function matchAnyBasename(names: string[]): (psCommand: string) => boolean {
95
91
  const lowers = new Set(names.map((name) => name.toLowerCase()));
96
92
  const test = (token: string) => lowers.has(basenameOf(token));