@cluesmith/codev 3.2.3 → 3.3.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 (445) hide show
  1. package/dashboard-dist/assets/index-BZRTEMtH.css +32 -0
  2. package/dashboard-dist/assets/{index-BZ1x7x1X.js → index-JCbkpAcV.js} +33 -33
  3. package/dashboard-dist/assets/index-JCbkpAcV.js.map +1 -0
  4. package/dashboard-dist/index.html +2 -2
  5. package/dist/agent-farm/cli.d.ts.map +1 -1
  6. package/dist/agent-farm/cli.js +173 -0
  7. package/dist/agent-farm/cli.js.map +1 -1
  8. package/dist/agent-farm/commands/attach.js +2 -2
  9. package/dist/agent-farm/commands/attach.js.map +1 -1
  10. package/dist/agent-farm/commands/cleanup.d.ts.map +1 -1
  11. package/dist/agent-farm/commands/cleanup.js +16 -0
  12. package/dist/agent-farm/commands/cleanup.js.map +1 -1
  13. package/dist/agent-farm/commands/inbox.d.ts +42 -0
  14. package/dist/agent-farm/commands/inbox.d.ts.map +1 -0
  15. package/dist/agent-farm/commands/inbox.js +129 -0
  16. package/dist/agent-farm/commands/inbox.js.map +1 -0
  17. package/dist/agent-farm/commands/interrupt.d.ts +20 -0
  18. package/dist/agent-farm/commands/interrupt.d.ts.map +1 -0
  19. package/dist/agent-farm/commands/interrupt.js +65 -0
  20. package/dist/agent-farm/commands/interrupt.js.map +1 -0
  21. package/dist/agent-farm/commands/reset/constants.d.ts +52 -0
  22. package/dist/agent-farm/commands/reset/constants.d.ts.map +1 -0
  23. package/dist/agent-farm/commands/reset/constants.js +52 -0
  24. package/dist/agent-farm/commands/reset/constants.js.map +1 -0
  25. package/dist/agent-farm/commands/reset/context.d.ts +230 -0
  26. package/dist/agent-farm/commands/reset/context.d.ts.map +1 -0
  27. package/dist/agent-farm/commands/reset/context.js +428 -0
  28. package/dist/agent-farm/commands/reset/context.js.map +1 -0
  29. package/dist/agent-farm/commands/reset/index.d.ts +192 -0
  30. package/dist/agent-farm/commands/reset/index.d.ts.map +1 -0
  31. package/dist/agent-farm/commands/reset/index.js +488 -0
  32. package/dist/agent-farm/commands/reset/index.js.map +1 -0
  33. package/dist/agent-farm/commands/reset/receipt.d.ts +97 -0
  34. package/dist/agent-farm/commands/reset/receipt.d.ts.map +1 -0
  35. package/dist/agent-farm/commands/reset/receipt.js +149 -0
  36. package/dist/agent-farm/commands/reset/receipt.js.map +1 -0
  37. package/dist/agent-farm/commands/reset/reorient.d.ts +117 -0
  38. package/dist/agent-farm/commands/reset/reorient.d.ts.map +1 -0
  39. package/dist/agent-farm/commands/reset/reorient.js +313 -0
  40. package/dist/agent-farm/commands/reset/reorient.js.map +1 -0
  41. package/dist/agent-farm/commands/reset.d.ts +19 -0
  42. package/dist/agent-farm/commands/reset.d.ts.map +1 -0
  43. package/dist/agent-farm/commands/reset.js +292 -0
  44. package/dist/agent-farm/commands/reset.js.map +1 -0
  45. package/dist/agent-farm/commands/send.d.ts +2 -2
  46. package/dist/agent-farm/commands/send.d.ts.map +1 -1
  47. package/dist/agent-farm/commands/send.js +52 -11
  48. package/dist/agent-farm/commands/send.js.map +1 -1
  49. package/dist/agent-farm/commands/setup.d.ts +4 -1
  50. package/dist/agent-farm/commands/setup.d.ts.map +1 -1
  51. package/dist/agent-farm/commands/setup.js +8 -4
  52. package/dist/agent-farm/commands/setup.js.map +1 -1
  53. package/dist/agent-farm/commands/spawn-worktree.d.ts +116 -6
  54. package/dist/agent-farm/commands/spawn-worktree.d.ts.map +1 -1
  55. package/dist/agent-farm/commands/spawn-worktree.js +345 -74
  56. package/dist/agent-farm/commands/spawn-worktree.js.map +1 -1
  57. package/dist/agent-farm/commands/spawn.d.ts.map +1 -1
  58. package/dist/agent-farm/commands/spawn.js +13 -1
  59. package/dist/agent-farm/commands/spawn.js.map +1 -1
  60. package/dist/agent-farm/commands/status.d.ts.map +1 -1
  61. package/dist/agent-farm/commands/status.js +105 -9
  62. package/dist/agent-farm/commands/status.js.map +1 -1
  63. package/dist/agent-farm/commands/tower-sweep-husks.d.ts +6 -0
  64. package/dist/agent-farm/commands/tower-sweep-husks.d.ts.map +1 -0
  65. package/dist/agent-farm/commands/tower-sweep-husks.js +77 -0
  66. package/dist/agent-farm/commands/tower-sweep-husks.js.map +1 -0
  67. package/dist/agent-farm/commands/tower.d.ts.map +1 -1
  68. package/dist/agent-farm/commands/tower.js +34 -0
  69. package/dist/agent-farm/commands/tower.js.map +1 -1
  70. package/dist/agent-farm/commands/whoami.d.ts +65 -0
  71. package/dist/agent-farm/commands/whoami.d.ts.map +1 -0
  72. package/dist/agent-farm/commands/whoami.js +196 -0
  73. package/dist/agent-farm/commands/whoami.js.map +1 -0
  74. package/dist/agent-farm/db/index.d.ts +1 -1
  75. package/dist/agent-farm/db/index.d.ts.map +1 -1
  76. package/dist/agent-farm/db/index.js +80 -1
  77. package/dist/agent-farm/db/index.js.map +1 -1
  78. package/dist/agent-farm/db/mailbox.d.ts +222 -0
  79. package/dist/agent-farm/db/mailbox.d.ts.map +1 -0
  80. package/dist/agent-farm/db/mailbox.js +304 -0
  81. package/dist/agent-farm/db/mailbox.js.map +1 -0
  82. package/dist/agent-farm/db/schema.d.ts +1 -1
  83. package/dist/agent-farm/db/schema.d.ts.map +1 -1
  84. package/dist/agent-farm/db/schema.js +35 -0
  85. package/dist/agent-farm/db/schema.js.map +1 -1
  86. package/dist/agent-farm/db/types.d.ts +45 -0
  87. package/dist/agent-farm/db/types.d.ts.map +1 -1
  88. package/dist/agent-farm/db/types.js.map +1 -1
  89. package/dist/agent-farm/lib/builder-lookup.d.ts +1 -1
  90. package/dist/agent-farm/lib/builder-lookup.js +2 -2
  91. package/dist/agent-farm/lib/builder-lookup.js.map +1 -1
  92. package/dist/agent-farm/lib/reconnect-backoff.d.ts +54 -0
  93. package/dist/agent-farm/lib/reconnect-backoff.d.ts.map +1 -0
  94. package/dist/agent-farm/lib/reconnect-backoff.js +47 -0
  95. package/dist/agent-farm/lib/reconnect-backoff.js.map +1 -0
  96. package/dist/agent-farm/lib/tower-client.d.ts +16 -6
  97. package/dist/agent-farm/lib/tower-client.d.ts.map +1 -1
  98. package/dist/agent-farm/lib/tower-client.js +37 -6
  99. package/dist/agent-farm/lib/tower-client.js.map +1 -1
  100. package/dist/agent-farm/lib/tunnel-client.d.ts +69 -1
  101. package/dist/agent-farm/lib/tunnel-client.d.ts.map +1 -1
  102. package/dist/agent-farm/lib/tunnel-client.js +218 -27
  103. package/dist/agent-farm/lib/tunnel-client.js.map +1 -1
  104. package/dist/agent-farm/servers/architect-session-holder.d.ts +133 -0
  105. package/dist/agent-farm/servers/architect-session-holder.d.ts.map +1 -0
  106. package/dist/agent-farm/servers/architect-session-holder.js +268 -0
  107. package/dist/agent-farm/servers/architect-session-holder.js.map +1 -0
  108. package/dist/agent-farm/servers/cron-delivery.d.ts +72 -0
  109. package/dist/agent-farm/servers/cron-delivery.d.ts.map +1 -0
  110. package/dist/agent-farm/servers/cron-delivery.js +80 -0
  111. package/dist/agent-farm/servers/cron-delivery.js.map +1 -0
  112. package/dist/agent-farm/servers/delayed-send.d.ts +85 -0
  113. package/dist/agent-farm/servers/delayed-send.d.ts.map +1 -0
  114. package/dist/agent-farm/servers/delayed-send.js +165 -0
  115. package/dist/agent-farm/servers/delayed-send.js.map +1 -0
  116. package/dist/agent-farm/servers/gate-profiles.d.ts +67 -0
  117. package/dist/agent-farm/servers/gate-profiles.d.ts.map +1 -0
  118. package/dist/agent-farm/servers/gate-profiles.js +113 -0
  119. package/dist/agent-farm/servers/gate-profiles.js.map +1 -0
  120. package/dist/agent-farm/servers/mailbox-delivery.d.ts +370 -0
  121. package/dist/agent-farm/servers/mailbox-delivery.d.ts.map +1 -0
  122. package/dist/agent-farm/servers/mailbox-delivery.js +617 -0
  123. package/dist/agent-farm/servers/mailbox-delivery.js.map +1 -0
  124. package/dist/agent-farm/servers/mailbox-wiring.d.ts +125 -0
  125. package/dist/agent-farm/servers/mailbox-wiring.d.ts.map +1 -0
  126. package/dist/agent-farm/servers/mailbox-wiring.js +425 -0
  127. package/dist/agent-farm/servers/mailbox-wiring.js.map +1 -0
  128. package/dist/agent-farm/servers/message-write.d.ts +53 -1
  129. package/dist/agent-farm/servers/message-write.d.ts.map +1 -1
  130. package/dist/agent-farm/servers/message-write.js +63 -0
  131. package/dist/agent-farm/servers/message-write.js.map +1 -1
  132. package/dist/agent-farm/servers/overview.d.ts.map +1 -1
  133. package/dist/agent-farm/servers/overview.js +23 -4
  134. package/dist/agent-farm/servers/overview.js.map +1 -1
  135. package/dist/agent-farm/servers/process-census.d.ts +41 -0
  136. package/dist/agent-farm/servers/process-census.d.ts.map +1 -0
  137. package/dist/agent-farm/servers/process-census.js +62 -0
  138. package/dist/agent-farm/servers/process-census.js.map +1 -0
  139. package/dist/agent-farm/servers/render-gate.d.ts +141 -0
  140. package/dist/agent-farm/servers/render-gate.d.ts.map +1 -0
  141. package/dist/agent-farm/servers/render-gate.js +251 -0
  142. package/dist/agent-farm/servers/render-gate.js.map +1 -0
  143. package/dist/agent-farm/servers/session-log-sweep.d.ts +93 -0
  144. package/dist/agent-farm/servers/session-log-sweep.d.ts.map +1 -0
  145. package/dist/agent-farm/servers/session-log-sweep.js +165 -0
  146. package/dist/agent-farm/servers/session-log-sweep.js.map +1 -0
  147. package/dist/agent-farm/servers/session-submit.d.ts +90 -0
  148. package/dist/agent-farm/servers/session-submit.d.ts.map +1 -0
  149. package/dist/agent-farm/servers/session-submit.js +127 -0
  150. package/dist/agent-farm/servers/session-submit.js.map +1 -0
  151. package/dist/agent-farm/servers/shellper-husk-sweep.d.ts +87 -0
  152. package/dist/agent-farm/servers/shellper-husk-sweep.d.ts.map +1 -0
  153. package/dist/agent-farm/servers/shellper-husk-sweep.js +129 -0
  154. package/dist/agent-farm/servers/shellper-husk-sweep.js.map +1 -0
  155. package/dist/agent-farm/servers/tower-cron.d.ts +10 -7
  156. package/dist/agent-farm/servers/tower-cron.d.ts.map +1 -1
  157. package/dist/agent-farm/servers/tower-cron.js +58 -53
  158. package/dist/agent-farm/servers/tower-cron.js.map +1 -1
  159. package/dist/agent-farm/servers/tower-instances.d.ts +14 -2
  160. package/dist/agent-farm/servers/tower-instances.d.ts.map +1 -1
  161. package/dist/agent-farm/servers/tower-instances.js +128 -19
  162. package/dist/agent-farm/servers/tower-instances.js.map +1 -1
  163. package/dist/agent-farm/servers/tower-messages.d.ts +38 -1
  164. package/dist/agent-farm/servers/tower-messages.d.ts.map +1 -1
  165. package/dist/agent-farm/servers/tower-messages.js +106 -1
  166. package/dist/agent-farm/servers/tower-messages.js.map +1 -1
  167. package/dist/agent-farm/servers/tower-routes.d.ts +16 -4
  168. package/dist/agent-farm/servers/tower-routes.d.ts.map +1 -1
  169. package/dist/agent-farm/servers/tower-routes.js +807 -99
  170. package/dist/agent-farm/servers/tower-routes.js.map +1 -1
  171. package/dist/agent-farm/servers/tower-server.js +241 -15
  172. package/dist/agent-farm/servers/tower-server.js.map +1 -1
  173. package/dist/agent-farm/servers/tower-terminals.d.ts +17 -2
  174. package/dist/agent-farm/servers/tower-terminals.d.ts.map +1 -1
  175. package/dist/agent-farm/servers/tower-terminals.js +139 -124
  176. package/dist/agent-farm/servers/tower-terminals.js.map +1 -1
  177. package/dist/agent-farm/servers/tower-tunnel.d.ts.map +1 -1
  178. package/dist/agent-farm/servers/tower-tunnel.js +63 -4
  179. package/dist/agent-farm/servers/tower-tunnel.js.map +1 -1
  180. package/dist/agent-farm/servers/tower-types.d.ts +2 -1
  181. package/dist/agent-farm/servers/tower-types.d.ts.map +1 -1
  182. package/dist/agent-farm/servers/tower-utils.d.ts +99 -1
  183. package/dist/agent-farm/servers/tower-utils.d.ts.map +1 -1
  184. package/dist/agent-farm/servers/tower-utils.js +242 -6
  185. package/dist/agent-farm/servers/tower-utils.js.map +1 -1
  186. package/dist/agent-farm/servers/tower-websocket.d.ts.map +1 -1
  187. package/dist/agent-farm/servers/tower-websocket.js +6 -21
  188. package/dist/agent-farm/servers/tower-websocket.js.map +1 -1
  189. package/dist/agent-farm/servers/write-queue.d.ts +35 -0
  190. package/dist/agent-farm/servers/write-queue.d.ts.map +1 -0
  191. package/dist/agent-farm/servers/write-queue.js +56 -0
  192. package/dist/agent-farm/servers/write-queue.js.map +1 -0
  193. package/dist/agent-farm/state.d.ts +6 -1
  194. package/dist/agent-farm/state.d.ts.map +1 -1
  195. package/dist/agent-farm/state.js +5 -2
  196. package/dist/agent-farm/state.js.map +1 -1
  197. package/dist/agent-farm/types.d.ts +39 -0
  198. package/dist/agent-farm/types.d.ts.map +1 -1
  199. package/dist/agent-farm/utils/agent-names.d.ts +2 -2
  200. package/dist/agent-farm/utils/agent-names.d.ts.map +1 -1
  201. package/dist/agent-farm/utils/agent-names.js +3 -3
  202. package/dist/agent-farm/utils/agent-names.js.map +1 -1
  203. package/dist/agent-farm/utils/architect-name.d.ts +2 -2
  204. package/dist/agent-farm/utils/architect-name.d.ts.map +1 -1
  205. package/dist/agent-farm/utils/architect-name.js +3 -3
  206. package/dist/agent-farm/utils/architect-name.js.map +1 -1
  207. package/dist/agent-farm/utils/config.d.ts +13 -0
  208. package/dist/agent-farm/utils/config.d.ts.map +1 -1
  209. package/dist/agent-farm/utils/config.js +33 -1
  210. package/dist/agent-farm/utils/config.js.map +1 -1
  211. package/dist/agent-farm/utils/harness.d.ts +103 -7
  212. package/dist/agent-farm/utils/harness.d.ts.map +1 -1
  213. package/dist/agent-farm/utils/harness.js +126 -19
  214. package/dist/agent-farm/utils/harness.js.map +1 -1
  215. package/dist/cli.d.ts.map +1 -1
  216. package/dist/cli.js +6 -32
  217. package/dist/cli.js.map +1 -1
  218. package/dist/commands/adopt.d.ts +3 -2
  219. package/dist/commands/adopt.d.ts.map +1 -1
  220. package/dist/commands/adopt.js +7 -6
  221. package/dist/commands/adopt.js.map +1 -1
  222. package/dist/commands/consult/agy-auth-cache.d.ts +97 -0
  223. package/dist/commands/consult/agy-auth-cache.d.ts.map +1 -0
  224. package/dist/commands/consult/agy-auth-cache.js +320 -0
  225. package/dist/commands/consult/agy-auth-cache.js.map +1 -0
  226. package/dist/commands/consult/cli-options.d.ts +34 -0
  227. package/dist/commands/consult/cli-options.d.ts.map +1 -0
  228. package/dist/commands/consult/cli-options.js +67 -0
  229. package/dist/commands/consult/cli-options.js.map +1 -0
  230. package/dist/commands/consult/index.d.ts +56 -2
  231. package/dist/commands/consult/index.d.ts.map +1 -1
  232. package/dist/commands/consult/index.js +330 -27
  233. package/dist/commands/consult/index.js.map +1 -1
  234. package/dist/commands/consult/metrics.d.ts +42 -0
  235. package/dist/commands/consult/metrics.d.ts.map +1 -1
  236. package/dist/commands/consult/metrics.js +129 -9
  237. package/dist/commands/consult/metrics.js.map +1 -1
  238. package/dist/commands/doctor.d.ts +28 -0
  239. package/dist/commands/doctor.d.ts.map +1 -1
  240. package/dist/commands/doctor.js +258 -22
  241. package/dist/commands/doctor.js.map +1 -1
  242. package/dist/commands/init.d.ts +3 -2
  243. package/dist/commands/init.d.ts.map +1 -1
  244. package/dist/commands/init.js +7 -6
  245. package/dist/commands/init.js.map +1 -1
  246. package/dist/commands/porch/checks.d.ts +23 -0
  247. package/dist/commands/porch/checks.d.ts.map +1 -1
  248. package/dist/commands/porch/checks.js +60 -0
  249. package/dist/commands/porch/checks.js.map +1 -1
  250. package/dist/commands/porch/config.d.ts +20 -0
  251. package/dist/commands/porch/config.d.ts.map +1 -1
  252. package/dist/commands/porch/config.js +20 -0
  253. package/dist/commands/porch/config.js.map +1 -1
  254. package/dist/commands/porch/index.d.ts.map +1 -1
  255. package/dist/commands/porch/index.js +32 -36
  256. package/dist/commands/porch/index.js.map +1 -1
  257. package/dist/commands/porch/next.d.ts.map +1 -1
  258. package/dist/commands/porch/next.js +2 -41
  259. package/dist/commands/porch/next.js.map +1 -1
  260. package/dist/commands/update.d.ts +4 -4
  261. package/dist/commands/update.d.ts.map +1 -1
  262. package/dist/commands/update.js +9 -11
  263. package/dist/commands/update.js.map +1 -1
  264. package/dist/lib/config.d.ts +70 -3
  265. package/dist/lib/config.d.ts.map +1 -1
  266. package/dist/lib/config.js +67 -3
  267. package/dist/lib/config.js.map +1 -1
  268. package/dist/lib/consult-lanes.d.ts +149 -0
  269. package/dist/lib/consult-lanes.d.ts.map +1 -0
  270. package/dist/lib/consult-lanes.js +360 -0
  271. package/dist/lib/consult-lanes.js.map +1 -0
  272. package/dist/lib/default-branch.d.ts +1 -1
  273. package/dist/lib/default-branch.js +1 -1
  274. package/dist/lib/github.js +1 -1
  275. package/dist/lib/github.js.map +1 -1
  276. package/dist/lib/gitignore.d.ts +25 -2
  277. package/dist/lib/gitignore.d.ts.map +1 -1
  278. package/dist/lib/gitignore.js +70 -0
  279. package/dist/lib/gitignore.js.map +1 -1
  280. package/dist/lib/migration-backup-audit.d.ts +64 -0
  281. package/dist/lib/migration-backup-audit.d.ts.map +1 -0
  282. package/dist/lib/migration-backup-audit.js +145 -0
  283. package/dist/lib/migration-backup-audit.js.map +1 -0
  284. package/dist/lib/prompt-behavior-metrics.d.ts +67 -0
  285. package/dist/lib/prompt-behavior-metrics.d.ts.map +1 -0
  286. package/dist/lib/prompt-behavior-metrics.js +290 -0
  287. package/dist/lib/prompt-behavior-metrics.js.map +1 -0
  288. package/dist/lib/protocol-drift-audit.d.ts +118 -0
  289. package/dist/lib/protocol-drift-audit.d.ts.map +1 -0
  290. package/dist/lib/protocol-drift-audit.js +217 -0
  291. package/dist/lib/protocol-drift-audit.js.map +1 -0
  292. package/dist/lib/scaffold.d.ts +10 -3
  293. package/dist/lib/scaffold.d.ts.map +1 -1
  294. package/dist/lib/scaffold.js +29 -26
  295. package/dist/lib/scaffold.js.map +1 -1
  296. package/dist/lib/skeleton.d.ts +22 -1
  297. package/dist/lib/skeleton.d.ts.map +1 -1
  298. package/dist/lib/skeleton.js +120 -0
  299. package/dist/lib/skeleton.js.map +1 -1
  300. package/dist/lib/test-env.d.ts +74 -0
  301. package/dist/lib/test-env.d.ts.map +1 -0
  302. package/dist/lib/test-env.js +92 -0
  303. package/dist/lib/test-env.js.map +1 -0
  304. package/dist/terminal/pty-manager.d.ts +9 -0
  305. package/dist/terminal/pty-manager.d.ts.map +1 -1
  306. package/dist/terminal/pty-manager.js +32 -6
  307. package/dist/terminal/pty-manager.js.map +1 -1
  308. package/dist/terminal/pty-session.d.ts +156 -4
  309. package/dist/terminal/pty-session.d.ts.map +1 -1
  310. package/dist/terminal/pty-session.js +307 -42
  311. package/dist/terminal/pty-session.js.map +1 -1
  312. package/dist/terminal/ring-buffer.d.ts +39 -6
  313. package/dist/terminal/ring-buffer.d.ts.map +1 -1
  314. package/dist/terminal/ring-buffer.js +74 -6
  315. package/dist/terminal/ring-buffer.js.map +1 -1
  316. package/dist/terminal/session-manager.d.ts +129 -0
  317. package/dist/terminal/session-manager.d.ts.map +1 -1
  318. package/dist/terminal/session-manager.js +337 -44
  319. package/dist/terminal/session-manager.js.map +1 -1
  320. package/dist/terminal/session-screen.d.ts +91 -0
  321. package/dist/terminal/session-screen.d.ts.map +1 -0
  322. package/dist/terminal/session-screen.js +135 -0
  323. package/dist/terminal/session-screen.js.map +1 -0
  324. package/dist/terminal/shellper-client.d.ts +19 -4
  325. package/dist/terminal/shellper-client.d.ts.map +1 -1
  326. package/dist/terminal/shellper-client.js +70 -8
  327. package/dist/terminal/shellper-client.js.map +1 -1
  328. package/dist/terminal/shellper-main.js +2 -1
  329. package/dist/terminal/shellper-main.js.map +1 -1
  330. package/dist/terminal/shellper-process.d.ts +1 -1
  331. package/dist/terminal/shellper-process.d.ts.map +1 -1
  332. package/dist/terminal/shellper-process.js +33 -9
  333. package/dist/terminal/shellper-process.js.map +1 -1
  334. package/dist/terminal/shellper-protocol.d.ts +40 -2
  335. package/dist/terminal/shellper-protocol.d.ts.map +1 -1
  336. package/dist/terminal/shellper-protocol.js +72 -5
  337. package/dist/terminal/shellper-protocol.js.map +1 -1
  338. package/dist/terminal/shellper-replay-buffer.d.ts +69 -7
  339. package/dist/terminal/shellper-replay-buffer.d.ts.map +1 -1
  340. package/dist/terminal/shellper-replay-buffer.js +167 -31
  341. package/dist/terminal/shellper-replay-buffer.js.map +1 -1
  342. package/package.json +11 -9
  343. package/skeleton/.claude/skills/afx/SKILL.md +27 -1
  344. package/skeleton/.claude/skills/arch-init/SKILL.md +135 -0
  345. package/skeleton/.claude/skills/arch-save/SKILL.md +183 -0
  346. package/skeleton/.claude/skills/codev/SKILL.md +33 -2
  347. package/skeleton/.claude/skills/runnable-worktrees/SKILL.md +119 -0
  348. package/skeleton/.codex/skills/afx/SKILL.md +170 -0
  349. package/skeleton/.codex/skills/arch-init/SKILL.md +135 -0
  350. package/skeleton/.codex/skills/arch-save/SKILL.md +183 -0
  351. package/skeleton/.codex/skills/codev/SKILL.md +90 -0
  352. package/skeleton/.codex/skills/consult/SKILL.md +96 -0
  353. package/skeleton/.codex/skills/generate-image/SKILL.md +45 -0
  354. package/skeleton/.codex/skills/porch/SKILL.md +60 -0
  355. package/skeleton/.codex/skills/runnable-worktrees/SKILL.md +119 -0
  356. package/skeleton/.codex/skills/update-arch-docs/SKILL.md +132 -0
  357. package/skeleton/protocols/air/builder-prompt.md +28 -47
  358. package/skeleton/protocols/air/consult-types/impl-review.md +17 -34
  359. package/skeleton/protocols/air/consult-types/pr-review.md +14 -37
  360. package/skeleton/protocols/air/prompts/implement.md +20 -73
  361. package/skeleton/protocols/air/prompts/pr.md +22 -54
  362. package/skeleton/protocols/air/protocol.md +40 -71
  363. package/skeleton/protocols/aspir/builder-prompt.md +29 -66
  364. package/skeleton/protocols/aspir/consult-types/impl-review.md +17 -44
  365. package/skeleton/protocols/aspir/consult-types/phase-review.md +17 -44
  366. package/skeleton/protocols/aspir/consult-types/plan-review.md +14 -38
  367. package/skeleton/protocols/aspir/consult-types/pr-review.md +13 -39
  368. package/skeleton/protocols/aspir/consult-types/spec-review.md +13 -33
  369. package/skeleton/protocols/aspir/prompts/implement.md +27 -196
  370. package/skeleton/protocols/aspir/prompts/plan.md +21 -85
  371. package/skeleton/protocols/aspir/prompts/review.md +29 -228
  372. package/skeleton/protocols/aspir/prompts/specify.md +25 -111
  373. package/skeleton/protocols/aspir/protocol.json +10 -0
  374. package/skeleton/protocols/aspir/protocol.md +36 -82
  375. package/skeleton/protocols/bugfix/builder-prompt.md +23 -45
  376. package/skeleton/protocols/bugfix/consult-types/impl-review.md +21 -45
  377. package/skeleton/protocols/bugfix/consult-types/pr-review.md +18 -52
  378. package/skeleton/protocols/bugfix/prompts/fix.md +16 -61
  379. package/skeleton/protocols/bugfix/prompts/investigate.md +18 -62
  380. package/skeleton/protocols/bugfix/prompts/pr.md +22 -57
  381. package/skeleton/protocols/bugfix/protocol.md +44 -50
  382. package/skeleton/protocols/experiment/builder-prompt.md +25 -58
  383. package/skeleton/protocols/experiment/protocol.md +22 -188
  384. package/skeleton/protocols/experiment/templates/notes.md +11 -71
  385. package/skeleton/protocols/maintain/builder-prompt.md +24 -45
  386. package/skeleton/protocols/maintain/consult-types/impl-review.md +17 -44
  387. package/skeleton/protocols/maintain/consult-types/pr-review.md +13 -39
  388. package/skeleton/protocols/maintain/prompts/maintain.md +1 -1
  389. package/skeleton/protocols/maintain/protocol.md +32 -266
  390. package/skeleton/protocols/maintain/templates/maintenance-run.md +11 -0
  391. package/skeleton/protocols/pir/builder-prompt.md +30 -64
  392. package/skeleton/protocols/pir/consult-types/impl-review.md +17 -50
  393. package/skeleton/protocols/pir/consult-types/pr-review.md +11 -40
  394. package/skeleton/protocols/pir/prompts/implement.md +1 -2
  395. package/skeleton/protocols/pir/prompts/plan.md +0 -3
  396. package/skeleton/protocols/pir/prompts/review.md +1 -1
  397. package/skeleton/protocols/pir/protocol.md +54 -177
  398. package/skeleton/protocols/research/builder-prompt.md +27 -58
  399. package/skeleton/protocols/research/protocol.md +28 -153
  400. package/skeleton/protocols/spike/builder-prompt.md +30 -48
  401. package/skeleton/protocols/spike/protocol.md +24 -107
  402. package/skeleton/protocols/spike/templates/findings.md +9 -39
  403. package/skeleton/protocols/spir/builder-prompt.md +33 -62
  404. package/skeleton/protocols/spir/consult-types/impl-review.md +17 -44
  405. package/skeleton/protocols/spir/consult-types/phase-review.md +17 -44
  406. package/skeleton/protocols/spir/consult-types/plan-review.md +14 -38
  407. package/skeleton/protocols/spir/consult-types/pr-review.md +13 -39
  408. package/skeleton/protocols/spir/consult-types/spec-review.md +13 -33
  409. package/skeleton/protocols/spir/prompts/implement.md +26 -195
  410. package/skeleton/protocols/spir/prompts/plan.md +20 -84
  411. package/skeleton/protocols/spir/prompts/review.md +28 -227
  412. package/skeleton/protocols/spir/prompts/specify.md +24 -110
  413. package/skeleton/protocols/spir/protocol.json +10 -0
  414. package/skeleton/protocols/spir/protocol.md +84 -633
  415. package/skeleton/protocols/spir/templates/plan.md +28 -147
  416. package/skeleton/protocols/spir/templates/review.md +21 -80
  417. package/skeleton/protocols/spir/templates/spec.md +24 -118
  418. package/skeleton/resources/commands/agent-farm.md +348 -0
  419. package/skeleton/resources/commands/codev.md +5 -0
  420. package/skeleton/resources/commands/consult.md +262 -8
  421. package/skeleton/resources/commands/overview.md +1 -0
  422. package/skeleton/roles/architect.md +67 -306
  423. package/skeleton/roles/builder.md +77 -191
  424. package/skeleton/templates/AGENTS.md +2 -0
  425. package/skeleton/templates/CLAUDE.md +2 -0
  426. package/templates/tower.html +39 -2
  427. package/dashboard-dist/assets/index-BZ1x7x1X.js.map +0 -1
  428. package/dashboard-dist/assets/index-DB1KBSNE.css +0 -32
  429. package/dist/agent-farm/servers/send-buffer.d.ts +0 -59
  430. package/dist/agent-farm/servers/send-buffer.d.ts.map +0 -1
  431. package/dist/agent-farm/servers/send-buffer.js +0 -103
  432. package/dist/agent-farm/servers/send-buffer.js.map +0 -1
  433. package/skeleton/porch/prompts/defend.md +0 -108
  434. package/skeleton/porch/prompts/diagnose.md +0 -70
  435. package/skeleton/porch/prompts/evaluate.md +0 -132
  436. package/skeleton/porch/prompts/fix.md +0 -59
  437. package/skeleton/porch/prompts/implement.md +0 -88
  438. package/skeleton/porch/prompts/plan.md +0 -74
  439. package/skeleton/porch/prompts/pr.md +0 -84
  440. package/skeleton/porch/prompts/review.md +0 -249
  441. package/skeleton/porch/prompts/specify.md +0 -53
  442. package/skeleton/porch/prompts/test.md +0 -63
  443. package/skeleton/protocols/aspir/templates/plan.md +0 -184
  444. package/skeleton/protocols/aspir/templates/review.md +0 -120
  445. package/skeleton/protocols/aspir/templates/spec.md +0 -154
@@ -15,11 +15,14 @@ import { query as claudeQuery } from '@anthropic-ai/claude-agent-sdk';
15
15
  import { Codex } from '@openai/codex-sdk';
16
16
  import { readCodevFile, findWorkspaceRoot } from '../../lib/skeleton.js';
17
17
  import { resolveDefaultBranch } from '../../lib/default-branch.js';
18
- import { loadConfig } from '../../lib/config.js';
18
+ import { loadConfig, findConfigSource } from '../../lib/config.js';
19
+ import { resolveLaneModel, resolveReasoningEffort, validateModelId, assertLaneAcceptsModelOverride, } from '../../lib/consult-lanes.js';
19
20
  import { getResolver, GitRefResolver } from '../porch/artifacts.js';
20
21
  import { MetricsDB } from './metrics.js';
21
22
  import { extractUsage, extractReviewText } from './usage-extractor.js';
22
23
  import { executeForgeCommandSync } from '../../lib/forge.js';
24
+ import { preflightAgyAuth, recordAgyAuthState } from './agy-auth-cache.js';
25
+ import { assertAgyLaneAllowedUnderTest } from '../../lib/test-env.js';
23
26
  const MODEL_CONFIGS = {
24
27
  // gemini dispatches to the Antigravity CLI (`agy`) via runAgyConsultation —
25
28
  // this entry exists only for model validation and the `pro` alias; its
@@ -49,6 +52,7 @@ function recordMetrics(ctx, extra) {
49
52
  db.record({
50
53
  timestamp: ctx.timestamp,
51
54
  model: ctx.model,
55
+ modelId: extra.modelId,
52
56
  reviewType: ctx.reviewType,
53
57
  subcommand: ctx.subcommand,
54
58
  protocol: ctx.protocol,
@@ -291,13 +295,147 @@ function commandExists(cmd) {
291
295
  return false;
292
296
  }
293
297
  }
294
- // Codex pricing for cost computation (matches values from old SUBPROCESS_MODEL_PRICING)
295
- const CODEX_PRICING = { inputPer1M: 2.00, cachedInputPer1M: 1.00, outputPer1M: 8.00 };
298
+ /**
299
+ * Shipped default model id for the codex consult lane (#1288).
300
+ *
301
+ * The `-sol` suffix is LOAD-BEARING. Both plain `gpt-5.6` and `gpt-5.6-codex`
302
+ * were live-probed on 2026-07-29 and rejected by Codex with a ChatGPT account
303
+ * ("The '<id>' model is not supported when using Codex with a ChatGPT
304
+ * account."). Do not "simplify" this id — `default-models.test.ts` guards it.
305
+ */
306
+ export const DEFAULT_CODEX_MODEL = 'gpt-5.6-sol';
307
+ /** Shipped default reasoning effort for the codex consult lane. */
308
+ export const DEFAULT_CODEX_REASONING_EFFORT = 'medium';
309
+ /** Shipped default model id for the claude consult lane (#1288). */
310
+ export const DEFAULT_CLAUDE_MODEL = 'claude-opus-5';
311
+ /**
312
+ * Per-1M-token codex rates, keyed by model id. Verified against
313
+ * https://developers.openai.com/api/docs/pricing on 2026-07-30.
314
+ *
315
+ * Only OpenAI's *standard* tier is modelled; the separate long-context tier
316
+ * (charged above the standard context threshold) is not, so cost is
317
+ * under-reported for unusually large consultations.
318
+ *
319
+ * A model id absent from this table yields `costUsd: null` rather than a cost
320
+ * computed from some other model's rates — a confidently wrong number is worse
321
+ * than none.
322
+ */
323
+ const CODEX_PRICING = {
324
+ 'gpt-5.6-sol': { inputPer1M: 5.00, cachedInputPer1M: 0.50, outputPer1M: 30.00 },
325
+ };
326
+ /**
327
+ * Compute codex consultation cost in USD, or null when the model's published
328
+ * rates are unknown.
329
+ */
330
+ export function computeCodexCost(model, inputTokens, cachedInputTokens, outputTokens, workspaceRoot) {
331
+ // `consult.pricing.codex` (spec 1286) outranks the shipped table, so a workspace running a model
332
+ // Codev has no rates for can still get real costs instead of nulls. Optional param: main's
333
+ // 4-arg callers and tests are unaffected.
334
+ const configured = workspaceRoot ? loadConfig(workspaceRoot).consult?.pricing?.codex : undefined;
335
+ const pricing = configured ?? CODEX_PRICING[model];
336
+ if (!pricing)
337
+ return null;
338
+ const uncached = inputTokens - cachedInputTokens;
339
+ return (uncached / 1_000_000) * pricing.inputPer1M
340
+ + (cachedInputTokens / 1_000_000) * pricing.cachedInputPer1M
341
+ + (outputTokens / 1_000_000) * pricing.outputPer1M;
342
+ }
343
+ /**
344
+ * Resolve which model id an SDK lane runs, and record where it came from.
345
+ *
346
+ * Precedence: `--model-id` > `consult.models.<lane>` > the shipped default constant.
347
+ *
348
+ * The provenance is not decoration: with five config layers, telling a user their
349
+ * `consult.models.codex` is wrong doesn't tell them which of five files to edit.
350
+ */
351
+ export function resolveLaneModelChoice(workspaceRoot, lane, defaultId, modelIdOverride) {
352
+ if (modelIdOverride !== undefined) {
353
+ validateModelId(modelIdOverride, '--model-id');
354
+ return { id: modelIdOverride, key: '--model-id', source: null, fromFlag: true };
355
+ }
356
+ const { id, key } = resolveLaneModel(loadConfig(workspaceRoot).consult, lane);
357
+ if (id === undefined || key === undefined) {
358
+ return { id: defaultId, key: null, source: null, fromFlag: false };
359
+ }
360
+ return { id, key, source: findConfigSource(workspaceRoot, ['consult', 'models', lane]), fromFlag: false };
361
+ }
362
+ /**
363
+ * Remove a stale review file before failing a consultation.
364
+ *
365
+ * "No review file" has to mean none *exists*, not merely that this run declined to write one.
366
+ * Porch keys off the file's presence, and consult writes to a deterministic per-iteration path — so
367
+ * a review left by an earlier run of the same iteration would be accepted as though the failed run
368
+ * had succeeded, and the phase would advance on a stale verdict. Found by codex reviewing the agy
369
+ * lane; applied to all three lanes because the exposure is identical wherever a runner throws after
370
+ * a previous run wrote output.
371
+ */
372
+ function discardStaleOutput(outputPath) {
373
+ if (!outputPath)
374
+ return;
375
+ try {
376
+ if (fs.existsSync(outputPath))
377
+ fs.unlinkSync(outputPath);
378
+ }
379
+ catch (err) {
380
+ // Best-effort — failing to unlink must not mask the underlying error. But it must not be
381
+ // silent either: this is precisely the state where porch could accept a stale review, so it
382
+ // has to be visible rather than undetectable.
383
+ console.error(`\n[warning] could not remove stale review at ${outputPath}: ` +
384
+ `${err instanceof Error ? err.message : String(err)}\n` +
385
+ `Delete it manually — porch may otherwise accept it as this iteration's review.`);
386
+ }
387
+ }
388
+ /**
389
+ * Resolve a model for a lane that has **no built-in default** — agy picks its own model when
390
+ * `--model` is absent, so there is nothing to fall back to.
391
+ *
392
+ * `null` means "omit the flag entirely", which is what preserves zero-config parity: an
393
+ * unconfigured gemini lane must produce byte-identical argv to before this spec.
394
+ */
395
+ export function resolveOptionalLaneModelChoice(workspaceRoot, lane, modelIdOverride) {
396
+ if (modelIdOverride !== undefined) {
397
+ validateModelId(modelIdOverride, '--model-id');
398
+ return { id: modelIdOverride, key: '--model-id', source: null, fromFlag: true };
399
+ }
400
+ const { id, key } = resolveLaneModel(loadConfig(workspaceRoot).consult, lane);
401
+ if (id === undefined || key === undefined)
402
+ return null;
403
+ return { id, key, source: findConfigSource(workspaceRoot, ['consult', 'models', lane]), fromFlag: false };
404
+ }
405
+ /**
406
+ * Attach model provenance to a provider rejection.
407
+ *
408
+ * Deliberately does NOT substitute a working id or otherwise recover — a bad model id must fail
409
+ * loudly. The provider's own text is preserved verbatim and merely annotated, because paraphrasing
410
+ * a provider error is how you lose the one detail that identifies the real problem.
411
+ */
412
+ function annotateModelError(err, lane, choice) {
413
+ // A shipped default can't be misconfigured by the user — nothing useful to add.
414
+ if (choice.key === null)
415
+ return err;
416
+ const providerText = err instanceof Error ? err.message : String(err);
417
+ const where = choice.fromFlag
418
+ ? 'passed via --model-id'
419
+ : `from \`${choice.key}\`${choice.source ? ` in ${choice.source}` : ''}`;
420
+ const annotated = new Error(`${providerText}\n\n` +
421
+ `The ${lane} lane requested model "${choice.id}" (${where}).\n` +
422
+ `If the provider rejected that id, correct it at the source above. ` +
423
+ `Codev does not fall back to a default model.`);
424
+ if (err instanceof Error && err.stack)
425
+ annotated.stack = err.stack;
426
+ return annotated;
427
+ }
296
428
  /**
297
429
  * Run Codex consultation via @openai/codex-sdk.
298
430
  * Mirrors runClaudeConsultation() — streams events, captures usage, records metrics.
299
431
  */
300
- export async function runCodexConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx) {
432
+ export async function runCodexConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx, modelChoice, reasoningEffort) {
433
+ // Absent an explicit choice (direct callers), resolve from config so behavior is identical
434
+ // whether the caller threads it through or not.
435
+ const choice = modelChoice ?? resolveLaneModelChoice(workspaceRoot, 'codex', DEFAULT_CODEX_MODEL);
436
+ const effort = reasoningEffort
437
+ ?? resolveReasoningEffort(loadConfig(workspaceRoot).consult)
438
+ ?? DEFAULT_CODEX_REASONING_EFFORT;
301
439
  const chunks = [];
302
440
  const startTime = Date.now();
303
441
  let usageData = null;
@@ -313,9 +451,9 @@ export async function runCodexConsultation(queryText, role, workspaceRoot, outpu
313
451
  },
314
452
  });
315
453
  const thread = codex.startThread({
316
- model: 'gpt-5.4',
454
+ model: choice.id,
317
455
  sandboxMode: 'read-only',
318
- modelReasoningEffort: 'medium',
456
+ modelReasoningEffort: effort,
319
457
  workingDirectory: workspaceRoot,
320
458
  });
321
459
  const { events } = await thread.runStreamed(queryText);
@@ -333,10 +471,7 @@ export async function runCodexConsultation(queryText, role, workspaceRoot, outpu
333
471
  // output_tokens already includes reasoning_output_tokens (OpenAI Responses-API
334
472
  // convention) — do NOT add the latter to cost or reasoning is double-billed.
335
473
  const output = event.usage.output_tokens;
336
- const uncached = input - cached;
337
- const cost = (uncached / 1_000_000) * CODEX_PRICING.inputPer1M
338
- + (cached / 1_000_000) * CODEX_PRICING.cachedInputPer1M
339
- + (output / 1_000_000) * CODEX_PRICING.outputPer1M;
474
+ const cost = computeCodexCost(choice.id, input, cached, output, workspaceRoot);
340
475
  usageData = { inputTokens: input, cachedInputTokens: cached, outputTokens: output, costUsd: cost };
341
476
  }
342
477
  if (event.type === 'turn.failed') {
@@ -364,7 +499,8 @@ export async function runCodexConsultation(queryText, role, workspaceRoot, outpu
364
499
  errorMessage = (err instanceof Error ? err.message : String(err)).substring(0, 500);
365
500
  exitCode = 1;
366
501
  }
367
- throw err;
502
+ discardStaleOutput(outputPath);
503
+ throw annotateModelError(err, 'codex', choice);
368
504
  }
369
505
  finally {
370
506
  // Clean up temp file
@@ -374,6 +510,7 @@ export async function runCodexConsultation(queryText, role, workspaceRoot, outpu
374
510
  if (metricsCtx) {
375
511
  const duration = (Date.now() - startTime) / 1000;
376
512
  recordMetrics(metricsCtx, {
513
+ modelId: choice.id,
377
514
  durationSeconds: duration,
378
515
  inputTokens: usageData?.inputTokens ?? null,
379
516
  cachedInputTokens: usageData?.cachedInputTokens ?? null,
@@ -419,7 +556,10 @@ export function buildClaudeConsultEnv(processEnv) {
419
556
  * Uses the SDK's query() function instead of CLI subprocess.
420
557
  * This avoids the CLAUDECODE nesting guard and enables tool use during reviews.
421
558
  */
422
- async function runClaudeConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx) {
559
+ export async function runClaudeConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx, modelChoice) {
560
+ // Absent an explicit choice (direct callers), resolve from config so behavior is identical
561
+ // whether the caller threads it through or not.
562
+ const choice = modelChoice ?? resolveLaneModelChoice(workspaceRoot, 'claude', DEFAULT_CLAUDE_MODEL);
423
563
  const chunks = [];
424
564
  const startTime = Date.now();
425
565
  let sdkResult;
@@ -439,7 +579,7 @@ async function runClaudeConsultation(queryText, role, workspaceRoot, outputPath,
439
579
  allowedTools: ['Read', 'Glob', 'Grep'],
440
580
  permissionMode: 'bypassPermissions',
441
581
  allowDangerouslySkipPermissions: true,
442
- model: 'claude-opus-4-6',
582
+ model: choice.id,
443
583
  maxTurns: CLAUDE_MAX_TURNS,
444
584
  maxBudgetUsd: 25,
445
585
  cwd: workspaceRoot,
@@ -480,7 +620,8 @@ async function runClaudeConsultation(queryText, role, workspaceRoot, outputPath,
480
620
  errorMessage = (err instanceof Error ? err.message : String(err)).substring(0, 500);
481
621
  exitCode = 1;
482
622
  }
483
- throw err;
623
+ discardStaleOutput(outputPath);
624
+ throw annotateModelError(err, 'claude', choice);
484
625
  }
485
626
  finally {
486
627
  if (savedClaudeCode !== undefined) {
@@ -491,6 +632,7 @@ async function runClaudeConsultation(queryText, role, workspaceRoot, outputPath,
491
632
  const duration = (Date.now() - startTime) / 1000;
492
633
  const usage = sdkResult ? extractUsage('claude', '', sdkResult) : null;
493
634
  recordMetrics(metricsCtx, {
635
+ modelId: choice.id,
494
636
  durationSeconds: duration,
495
637
  inputTokens: usage?.inputTokens ?? null,
496
638
  cachedInputTokens: usage?.cachedInputTokens ?? null,
@@ -519,6 +661,18 @@ const AGY_PRINT_TIMEOUT = '5m'; // passed to `agy --print-timeout`
519
661
  const AGY_TIMEOUT_MS = 6 * 60 * 1000; // Codev-owned hard cap (> agy's own timeout)
520
662
  // OAuth banner appears before any review text; only scan the early stream.
521
663
  const AGY_MARKER_SCAN_LIMIT = 8192;
664
+ // Bounded tail of agy's own output retained for a configured-lane hard failure. agy's rejection
665
+ // text is the only thing that explains WHY a model id was refused, but it lands in an error
666
+ // message, so it is capped rather than accumulated.
667
+ const AGY_FAILURE_TAIL_MAX_CHARS = 2000;
668
+ /**
669
+ * How long a prober waits, marker-free, before publishing `auth` to the shared
670
+ * cache (#1077). The OAuth banner is the very first thing an unauthenticated agy
671
+ * emits, so silence this far in is good evidence we are signed in — and it lets
672
+ * the processes waiting on our verdict get moving instead of stalling behind a
673
+ * review that may run for minutes.
674
+ */
675
+ const AGY_AUTH_GRACE_MS = 3000;
522
676
  // agy's own print-timeout message: on an agentic task that outruns --print-timeout,
523
677
  // it returns this (often with a "monitoring the task" note) instead of a review.
524
678
  // Treat it as a non-response → non-blocking skip rather than a garbage "review".
@@ -572,6 +726,12 @@ export function resolveAgyBin() {
572
726
  const override = process.env.CODEV_AGY_BIN;
573
727
  if (override)
574
728
  return isRealAgyCli(override) ? override : null;
729
+ // Past this point we go looking for the developer's real install, so this is
730
+ // the true chokepoint for the test-isolation guard (#1323) — not the spawn
731
+ // sites. Resolution is not passive: the PATH branch below runs
732
+ // `agyRespondsToVersion`, which *executes* the candidate binary. Guarding only
733
+ // the spawn would still let a suite run the real agy.
734
+ assertAgyLaneAllowedUnderTest();
575
735
  // Canonical install path — trusted location; realpath-reject the IDE only.
576
736
  const preferred = path.join(homedir(), '.local', 'bin', 'agy');
577
737
  if (isRealAgyCli(preferred))
@@ -629,10 +789,15 @@ function writeConsultOutput(outputPath, content) {
629
789
  fs.writeFileSync(outputPath, content);
630
790
  console.error(`\nOutput written to: ${outputPath}`);
631
791
  }
632
- function recordAgyMetrics(metricsCtx, startTime, exitCode, errorMessage) {
792
+ function recordAgyMetrics(metricsCtx, startTime, exitCode, errorMessage,
793
+ // No default: every caller states the id or states null. A default would quietly reintroduce
794
+ // the silent-NULL path that making modelId required on MetricsRecord exists to prevent.
795
+ modelId) {
633
796
  if (!metricsCtx)
634
797
  return;
635
798
  recordMetrics(metricsCtx, {
799
+ // Null on a skip with no model configured — "no model was chosen", not "we forgot".
800
+ modelId,
636
801
  durationSeconds: (Date.now() - startTime) / 1000,
637
802
  // agy --print emits plain text, no token usage → cost rows degrade gracefully (null).
638
803
  inputTokens: null,
@@ -649,18 +814,42 @@ function recordAgyMetrics(metricsCtx, startTime, exitCode, errorMessage) {
649
814
  * prompt, and NEVER blocks the run: a missing/unauthed/invalid CLI or a
650
815
  * timeout/error produces a non-blocking COMMENT skip instead of throwing.
651
816
  */
652
- async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx) {
817
+ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, metricsCtx, modelChoice) {
653
818
  const startTime = Date.now();
819
+ // `undefined` means "resolve it yourself" (direct callers); an explicit `null` means "no model
820
+ // configured", which must stay distinguishable from "not yet resolved".
821
+ const choice = modelChoice === undefined
822
+ ? resolveOptionalLaneModelChoice(workspaceRoot, 'gemini')
823
+ : modelChoice;
824
+ // The test-isolation guard (#1323) lives inside resolveAgyBin, at the point
825
+ // where an unpinned lookup would reach the real install. It throws rather than
826
+ // falling through to the non-blocking skip below — deliberately: a misconfigured
827
+ // test must fail loudly instead of passing on a machine that happens not to have
828
+ // agy installed while spawning the real CLI on one that does.
654
829
  const bin = resolveAgyBin();
655
830
  if (!bin) {
656
831
  const reason = 'agy CLI not found (install: https://antigravity.google/cli/install.sh)';
657
832
  const content = agySkipContent(reason);
658
833
  process.stdout.write(content);
659
834
  writeConsultOutput(outputPath, content);
660
- recordAgyMetrics(metricsCtx, startTime, 0, reason);
835
+ recordAgyMetrics(metricsCtx, startTime, 0, reason, choice?.id ?? null);
661
836
  console.error(`\n[gemini (agy) skipped: ${reason}]`);
662
837
  return;
663
838
  }
839
+ // Pre-flight the shared auth cache BEFORE spawning (#1077). An unauthenticated
840
+ // agy opens a browser tab before it prints the OAuth URL we detect below, so
841
+ // post-spawn detection cannot prevent the tab — only not spawning can. Across a
842
+ // CMAP burst this collapses N stranded tabs into at most one per TTL window.
843
+ const preflight = await preflightAgyAuth(bin);
844
+ if (preflight.action === 'skip') {
845
+ const reason = preflight.reason ?? 'agy unauthenticated (cached)';
846
+ const content = agySkipContent(reason);
847
+ process.stdout.write(content);
848
+ writeConsultOutput(outputPath, content);
849
+ recordAgyMetrics(metricsCtx, startTime, 0, reason, choice?.id ?? null);
850
+ console.error(`\n[gemini (agy) skipped without spawning: ${reason}]`);
851
+ return;
852
+ }
664
853
  // agy has no system-prompt flag — fold the role into the prompt (hermes precedent).
665
854
  // Prepend strict constraint directive to force agy to remain on-task, prevent exploratory actions
666
855
  // that lead to wandering off-task, and avoid unrelated local skills/directories (Issue #1032).
@@ -693,6 +882,11 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
693
882
  const args = ['--sandbox', '--print-timeout', AGY_PRINT_TIMEOUT];
694
883
  for (const d of addDirs)
695
884
  args.push('--add-dir', d);
885
+ // Omitted entirely when unconfigured, so an unconfigured lane's argv is byte-identical to
886
+ // pre-1286 and agy keeps choosing its own model. Must precede --print: agy parses --print as a
887
+ // string-valued option, so its value has to be the immediately following argument.
888
+ if (choice)
889
+ args.push('--model', choice.id);
696
890
  // agy 1.0.10 defines --print as a string-valued option, so its prompt must
697
891
  // immediately follow the flag rather than another option such as --sandbox.
698
892
  args.push('--print', promptArg);
@@ -704,7 +898,7 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
704
898
  catch { /* best-effort */ }
705
899
  }
706
900
  };
707
- return new Promise((resolve) => {
901
+ return new Promise((resolve, reject) => {
708
902
  const proc = spawn(bin, args, {
709
903
  cwd: workspaceRoot,
710
904
  stdio: ['ignore', 'pipe', 'pipe'],
@@ -712,6 +906,25 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
712
906
  const outChunks = [];
713
907
  let scanBuf = '';
714
908
  let settled = false;
909
+ // stderr is watched for auth markers but otherwise discarded today, so a hard failure would
910
+ // have nothing but an exit code to report. Retain a bounded tail of BOTH streams: agy's own
911
+ // text is the only thing that explains *why* a model was rejected, and this lands in an error
912
+ // message, so it must not be unbounded.
913
+ let outputTail = '';
914
+ // When we hold the probe lock, other consult processes are polling the cache
915
+ // for our verdict — publish it as soon as it is knowable, and always release
916
+ // the lock, whatever this run turns into.
917
+ const graceTimer = preflight.isProber
918
+ ? setTimeout(() => preflight.publish('auth'), AGY_AUTH_GRACE_MS)
919
+ : null;
920
+ const publishAuth = (state) => {
921
+ if (graceTimer)
922
+ clearTimeout(graceTimer);
923
+ if (state)
924
+ preflight.publish(state);
925
+ else
926
+ preflight.release();
927
+ };
715
928
  const settleSkip = (reason, exitCode = 0) => {
716
929
  if (settled)
717
930
  return;
@@ -722,10 +935,14 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
722
935
  }
723
936
  catch { /* already gone */ }
724
937
  cleanup();
938
+ // No-op if a verdict was already published (e.g. the OAuth-marker path);
939
+ // otherwise this just releases the lock — a timeout or a spawn failure is
940
+ // not evidence either way about authentication.
941
+ publishAuth();
725
942
  const content = agySkipContent(reason);
726
943
  process.stdout.write(content);
727
944
  writeConsultOutput(outputPath, content);
728
- recordAgyMetrics(metricsCtx, startTime, exitCode, reason);
945
+ recordAgyMetrics(metricsCtx, startTime, exitCode, reason, choice?.id ?? null);
729
946
  console.error(`\n[gemini (agy) skipped: ${reason}]`);
730
947
  resolve();
731
948
  };
@@ -733,9 +950,23 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
733
950
  const watch = (buf, isStdout) => {
734
951
  if (isStdout)
735
952
  outChunks.push(buf);
953
+ outputTail = (outputTail + buf.toString('utf-8')).slice(-AGY_FAILURE_TAIL_MAX_CHARS);
736
954
  if (scanBuf.length < AGY_MARKER_SCAN_LIMIT) {
737
955
  scanBuf += buf.toString('utf-8');
738
956
  if (AGY_OAUTH_MARKERS.some((m) => scanBuf.includes(m))) {
957
+ // The one place we have positive proof of being signed out, so it is
958
+ // written unconditionally rather than through the once-only publish:
959
+ // - If our own grace timer already guessed `auth` (agy took longer
960
+ // than AGY_AUTH_GRACE_MS to emit its banner), that guess would
961
+ // otherwise stand for the full auth TTL and every burst in that
962
+ // window would spawn tabs again.
963
+ // - A non-prober that failed open and spawned holds real evidence
964
+ // too; discarding it would leave a misfired guess uncorrected
965
+ // until the TTL lapsed, instead of on the very next spawn.
966
+ // recordAgyAuthState carries its own disabled-guard, so this is safe
967
+ // on every path.
968
+ recordAgyAuthState('unauth', bin);
969
+ publishAuth('unauth');
739
970
  settleSkip('agy not authenticated — run `agy` once to sign in (OAuth)', 1);
740
971
  }
741
972
  }
@@ -752,7 +983,48 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
752
983
  clearTimeout(timer);
753
984
  cleanup();
754
985
  const raw = Buffer.concat(outChunks).toString('utf-8').trim();
755
- if (code !== 0 || raw.length === 0 || raw.includes(AGY_NONRESPONSE_MARKER)) {
986
+ // THE PHASE 3 INVARIANT: a skip may only be reached for an ENVIRONMENT cause.
987
+ //
988
+ // Configuring `consult.models.gemini` is opting out of "quietly proceed without this lane" —
989
+ // a non-zero exit then means the model was probably rejected, and swallowing that as a
990
+ // COMMENT skip would let a typo'd model id silently reduce every review to two lanes.
991
+ //
992
+ // Deliberately narrow: ONLY a non-zero exit hard-fails. Auth, timeout, non-response and
993
+ // empty output stay skips even when configured, because those are environment causes and the
994
+ // degraded-agy lane (#1032/#1033) must keep its non-blocking property. Widening this to
995
+ // "any failure" would wedge phases for workspaces whose agy is merely unauthenticated.
996
+ // Environment causes are classified FIRST. agy can emit its non-response marker *and* exit
997
+ // non-zero, and checking the exit code before the marker would misfile that timeout as a
998
+ // configuration failure — breaking the "timeout stays a skip in both cases" half of the
999
+ // invariant for exactly the degraded lane it exists to protect. (Found by codex at review.)
1000
+ // ONLY the non-response marker, deliberately — NOT empty stdout. A rejected model id writes
1001
+ // its error to stderr and exits non-zero with empty stdout, so treating "no stdout" as an
1002
+ // environment cause would make the hard failure unreachable for the exact case it exists to
1003
+ // catch. (My own stale-review test caught that overcorrection.) Empty stdout still means
1004
+ // "no review" on the zero-exit path below.
1005
+ // Checked against BOTH streams: agy may print its timeout notice to stderr, and matching
1006
+ // stdout alone would hard-fail a configured lane on a plain timeout — the same invariant hole
1007
+ // twice over. `outputTail` is capped, but a timeout notice is by nature near the end of the
1008
+ // stream, so the tail is where it lands. (Found by claude at review.)
1009
+ const timedOutProducing = raw.includes(AGY_NONRESPONSE_MARKER) || outputTail.includes(AGY_NONRESPONSE_MARKER);
1010
+ // `code === null` means agy was killed by a signal (OOM, external kill) — an environment
1011
+ // cause, not a rejected model, so it must not hard-fail either. `code !== 0` alone is true
1012
+ // for null and would misfile it. (Found by claude at review.)
1013
+ if (code !== null && code !== 0 && choice && !timedOutProducing) {
1014
+ publishAuth();
1015
+ recordAgyMetrics(metricsCtx, startTime, code, `agy exited with code ${code}`, choice?.id ?? null);
1016
+ console.error(`\n[gemini (agy) FAILED: configured model "${choice.id}" — see error]`);
1017
+ // "No review file" must mean none EXISTS, not merely that this run wrote none.
1018
+ discardStaleOutput(outputPath);
1019
+ const providerError = new Error(`agy exited with code ${code}.` +
1020
+ (outputTail.trim() ? `\n\nagy output (last ${AGY_FAILURE_TAIL_MAX_CHARS} chars):\n${outputTail.trim()}` : ''));
1021
+ reject(annotateModelError(providerError, 'gemini', choice));
1022
+ return;
1023
+ }
1024
+ if (code !== 0 || raw.length === 0 || timedOutProducing) {
1025
+ // A broken run tells us nothing about auth — release without a verdict
1026
+ // and let the next call re-probe.
1027
+ publishAuth();
756
1028
  const reason = code !== 0
757
1029
  ? `agy exited with code ${code}`
758
1030
  : raw.includes(AGY_NONRESPONSE_MARKER)
@@ -761,28 +1033,50 @@ async function runAgyConsultation(queryText, role, workspaceRoot, outputPath, me
761
1033
  const content = agySkipContent(reason);
762
1034
  process.stdout.write(content);
763
1035
  writeConsultOutput(outputPath, content);
764
- recordAgyMetrics(metricsCtx, startTime, code ?? 1, reason);
1036
+ recordAgyMetrics(metricsCtx, startTime, code ?? 1, reason, choice?.id ?? null);
765
1037
  console.error(`\n[gemini (agy) skipped: ${reason}]`);
766
1038
  resolve();
767
1039
  return;
768
1040
  }
1041
+ // A real review is conclusive proof agy is signed in.
1042
+ publishAuth('auth');
769
1043
  // Plain-text stdout IS the review.
770
1044
  process.stdout.write(raw);
771
1045
  writeConsultOutput(outputPath, raw);
772
- recordAgyMetrics(metricsCtx, startTime, 0, null);
1046
+ recordAgyMetrics(metricsCtx, startTime, 0, null, choice?.id ?? null);
773
1047
  console.error(`\n[gemini (agy) completed in ${((Date.now() - startTime) / 1000).toFixed(1)}s]`);
774
1048
  resolve();
775
1049
  });
776
1050
  });
777
1051
  }
1052
+ /**
1053
+ * Record the model a lane actually ran, so a transcript answers "what did this use?".
1054
+ *
1055
+ * Logged from the dispatch branch that owns the resolved `choice`, NOT re-derived for display: a
1056
+ * second resolution path is exactly how `--model-id` came to be documented, parsed, and inert.
1057
+ * Naming the source too means a surprising id points at the file to edit.
1058
+ */
1059
+ function logResolvedModel(lane, id, key, effort) {
1060
+ const from = key ? ` (from ${key})` : '';
1061
+ const at = effort ? ` at ${effort} reasoning effort` : '';
1062
+ console.error(`[${lane.toUpperCase()}] model: ${id}${at}${from}`);
1063
+ }
778
1064
  /**
779
1065
  * Run the consultation — dispatches to the correct model runner.
780
1066
  */
781
- async function runConsultation(model, query, workspaceRoot, role, outputPath, metricsCtx, generalMode) {
1067
+ async function runConsultation(model, query, workspaceRoot, role, outputPath, metricsCtx, generalMode, modelIdOverride) {
1068
+ // Fail before dispatch if the selected lane cannot honour the override. Checked here rather than
1069
+ // per-branch so a lane that never reads it can't silently ignore it (codex caught exactly that for
1070
+ // hermes). Syntax is validated per-lane in resolveLaneModelChoice.
1071
+ if (modelIdOverride !== undefined) {
1072
+ assertLaneAcceptsModelOverride(model);
1073
+ }
782
1074
  // SDK-based models
783
1075
  if (model === 'claude') {
784
1076
  const startTime = Date.now();
785
- await runClaudeConsultation(query, role, workspaceRoot, outputPath, metricsCtx);
1077
+ const choice = resolveLaneModelChoice(workspaceRoot, 'claude', DEFAULT_CLAUDE_MODEL, modelIdOverride);
1078
+ logResolvedModel(model, choice.id, choice.key);
1079
+ await runClaudeConsultation(query, role, workspaceRoot, outputPath, metricsCtx, choice);
786
1080
  const duration = (Date.now() - startTime) / 1000;
787
1081
  logQuery(workspaceRoot, model, query, duration);
788
1082
  console.error(`\n[${model} completed in ${duration.toFixed(1)}s]`);
@@ -790,7 +1084,10 @@ async function runConsultation(model, query, workspaceRoot, role, outputPath, me
790
1084
  }
791
1085
  if (model === 'codex') {
792
1086
  const startTime = Date.now();
793
- await runCodexConsultation(query, role, workspaceRoot, outputPath, metricsCtx);
1087
+ const choice = resolveLaneModelChoice(workspaceRoot, 'codex', DEFAULT_CODEX_MODEL, modelIdOverride);
1088
+ const effort = resolveReasoningEffort(loadConfig(workspaceRoot).consult) ?? DEFAULT_CODEX_REASONING_EFFORT;
1089
+ logResolvedModel(model, choice.id, choice.key, effort);
1090
+ await runCodexConsultation(query, role, workspaceRoot, outputPath, metricsCtx, choice, effort);
794
1091
  const duration = (Date.now() - startTime) / 1000;
795
1092
  logQuery(workspaceRoot, model, query, duration);
796
1093
  console.error(`\n[${model} completed in ${duration.toFixed(1)}s]`);
@@ -800,7 +1097,10 @@ async function runConsultation(model, query, workspaceRoot, role, outputPath, me
800
1097
  // and non-blocking skip (see runAgyConsultation).
801
1098
  if (model === 'gemini') {
802
1099
  const startTime = Date.now();
803
- await runAgyConsultation(query, role, workspaceRoot, outputPath, metricsCtx);
1100
+ const choice = resolveOptionalLaneModelChoice(workspaceRoot, 'gemini', modelIdOverride);
1101
+ // No configured id means agy chooses; say so rather than printing a value we did not set.
1102
+ logResolvedModel(model, choice?.id ?? "agy's own default", choice?.key ?? null);
1103
+ await runAgyConsultation(query, role, workspaceRoot, outputPath, metricsCtx, choice);
804
1104
  logQuery(workspaceRoot, model, query, (Date.now() - startTime) / 1000);
805
1105
  return;
806
1106
  }
@@ -876,6 +1176,8 @@ async function runConsultation(model, query, workspaceRoot, role, outputPath, me
876
1176
  if (metricsCtx) {
877
1177
  const usage = extractUsage(model, rawOutput);
878
1178
  recordMetrics(metricsCtx, {
1179
+ // Subprocess lanes (hermes) expose no model selector — see MODEL_CONFIGURABLE_LANES.
1180
+ modelId: null,
879
1181
  durationSeconds: duration,
880
1182
  inputTokens: usage?.inputTokens ?? null,
881
1183
  cachedInputTokens: usage?.cachedInputTokens ?? null,
@@ -901,6 +1203,7 @@ async function runConsultation(model, query, workspaceRoot, role, outputPath, me
901
1203
  if (metricsCtx) {
902
1204
  const duration = (Date.now() - startTime) / 1000;
903
1205
  recordMetrics(metricsCtx, {
1206
+ modelId: null,
904
1207
  durationSeconds: duration,
905
1208
  inputTokens: null,
906
1209
  cachedInputTokens: null,
@@ -1774,7 +2077,7 @@ export async function consult(options) {
1774
2077
  }
1775
2078
  }
1776
2079
  const isGeneralMode = !hasType;
1777
- await runConsultation(model, query, workspaceRoot, role, outputPath, metricsCtx, isGeneralMode);
2080
+ await runConsultation(model, query, workspaceRoot, role, outputPath, metricsCtx, isGeneralMode, options.modelId);
1778
2081
  }
1779
2082
  // Exported for testing
1780
2083
  export { getDiffStat as _getDiffStat, buildSpecQuery as _buildSpecQuery, buildPlanQuery as _buildPlanQuery, buildPRQuery as _buildPRQuery, composePRQueryText as _composePRQueryText, computeLocalPRDiff as _computeLocalPRDiff, resolveIntegrationBase as _resolveIntegrationBase, resolveArchitectQuery as _resolveArchitectQuery, computePersistentOutputPath as _computePersistentOutputPath, MODEL_CONFIGS as _MODEL_CONFIGS, MODEL_ALIASES as _MODEL_ALIASES, runAgyConsultation as _runAgyConsultation, agySkipContent as _agySkipContent, };