@quickchatai/quickreview 1.1.0 → 1.3.1

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 (471) hide show
  1. package/README.md +137 -57
  2. package/bin/qcr.js +1 -1
  3. package/config.example.toml +60 -14
  4. package/deploy/ansible/README.md +20 -12
  5. package/deploy/ansible/inventory.example.ini +1 -1
  6. package/deploy/collect-secrets.sh +1 -1
  7. package/deploy/qcr-update.sh +1 -1
  8. package/dist/extensions/cli/home.js +14 -0
  9. package/dist/extensions/cli/home.js.map +1 -0
  10. package/dist/extensions/cli/login-route.js +5 -0
  11. package/dist/extensions/cli/login-route.js.map +1 -0
  12. package/dist/extensions/cli/selfimprove.js +18 -0
  13. package/dist/extensions/cli/selfimprove.js.map +1 -0
  14. package/dist/extensions/home.js +350 -0
  15. package/dist/extensions/home.js.map +1 -0
  16. package/dist/extensions/login-route.js +180 -0
  17. package/dist/extensions/login-route.js.map +1 -0
  18. package/dist/extensions/onboarding.js +35 -11
  19. package/dist/extensions/onboarding.js.map +1 -1
  20. package/dist/extensions/selfimprove.js +49 -0
  21. package/dist/extensions/selfimprove.js.map +1 -0
  22. package/dist/extensions/setup-scan.js +23 -592
  23. package/dist/extensions/setup-scan.js.map +1 -1
  24. package/dist/extensions/setup.js +2 -47
  25. package/dist/extensions/setup.js.map +1 -1
  26. package/dist/src/archive/restore.js +18 -15
  27. package/dist/src/archive/restore.js.map +1 -1
  28. package/dist/src/botworker.js +13 -2
  29. package/dist/src/botworker.js.map +1 -1
  30. package/dist/src/channelfs.js +3 -26
  31. package/dist/src/channelfs.js.map +1 -1
  32. package/dist/src/ciretry.js +2 -1
  33. package/dist/src/ciretry.js.map +1 -1
  34. package/dist/src/cli.js +54 -61
  35. package/dist/src/cli.js.map +1 -1
  36. package/dist/src/cliops.js +3 -2
  37. package/dist/src/cliops.js.map +1 -1
  38. package/dist/src/commands.js +25 -2
  39. package/dist/src/commands.js.map +1 -1
  40. package/dist/src/config.js +32 -2
  41. package/dist/src/config.js.map +1 -1
  42. package/dist/src/daemon.js +50 -8
  43. package/dist/src/daemon.js.map +1 -1
  44. package/dist/src/debuground.js +1 -0
  45. package/dist/src/debuground.js.map +1 -1
  46. package/dist/src/debugroundpi.js +6 -1
  47. package/dist/src/debugroundpi.js.map +1 -1
  48. package/dist/src/dispatch.js +5 -4
  49. package/dist/src/dispatch.js.map +1 -1
  50. package/dist/src/engine/caserunner-sdk.js +15 -251
  51. package/dist/src/engine/caserunner-sdk.js.map +1 -1
  52. package/dist/src/engine/debugpi.js +17 -5
  53. package/dist/src/engine/debugpi.js.map +1 -1
  54. package/dist/src/execboundary.js +10 -3
  55. package/dist/src/execboundary.js.map +1 -1
  56. package/dist/src/github.js +51 -16
  57. package/dist/src/github.js.map +1 -1
  58. package/dist/src/githubtoken.js +346 -0
  59. package/dist/src/githubtoken.js.map +1 -0
  60. package/dist/src/home/adapters.js +349 -0
  61. package/dist/src/home/adapters.js.map +1 -0
  62. package/dist/src/home/advanced.js +318 -0
  63. package/dist/src/home/advanced.js.map +1 -0
  64. package/dist/src/home/advancedtools.js +57 -0
  65. package/dist/src/home/advancedtools.js.map +1 -0
  66. package/dist/src/home/autoreview.js +325 -0
  67. package/dist/src/home/autoreview.js.map +1 -0
  68. package/dist/src/home/daemonstatus.js +32 -0
  69. package/dist/src/home/daemonstatus.js.map +1 -0
  70. package/dist/src/home/devicelogin.js +61 -0
  71. package/dist/src/home/devicelogin.js.map +1 -0
  72. package/dist/src/home/launch.js +202 -0
  73. package/dist/src/home/launch.js.map +1 -0
  74. package/dist/src/home/loginowner.js +205 -0
  75. package/dist/src/home/loginowner.js.map +1 -0
  76. package/dist/src/home/memory.js +122 -0
  77. package/dist/src/home/memory.js.map +1 -0
  78. package/dist/src/home/model.js +811 -0
  79. package/dist/src/home/model.js.map +1 -0
  80. package/dist/src/home/panel.js +520 -0
  81. package/dist/src/home/panel.js.map +1 -0
  82. package/dist/src/home/ports.js +19 -0
  83. package/dist/src/home/ports.js.map +1 -0
  84. package/dist/src/home/review.js +163 -0
  85. package/dist/src/home/review.js.map +1 -0
  86. package/dist/src/home/reviews.js +478 -0
  87. package/dist/src/home/reviews.js.map +1 -0
  88. package/dist/src/home/setup.js +507 -0
  89. package/dist/src/home/setup.js.map +1 -0
  90. package/dist/src/home/setupadapters.js +214 -0
  91. package/dist/src/home/setupadapters.js.map +1 -0
  92. package/dist/src/home/setupcard.js +199 -0
  93. package/dist/src/home/setupcard.js.map +1 -0
  94. package/dist/src/home/setuptools.js +45 -0
  95. package/dist/src/home/setuptools.js.map +1 -0
  96. package/dist/src/home/spend.js +160 -0
  97. package/dist/src/home/spend.js.map +1 -0
  98. package/dist/src/home/switches.js +209 -0
  99. package/dist/src/home/switches.js.map +1 -0
  100. package/dist/src/home/switchon.js +439 -0
  101. package/dist/src/home/switchon.js.map +1 -0
  102. package/dist/src/home/toolnames.js +41 -0
  103. package/dist/src/home/toolnames.js.map +1 -0
  104. package/dist/src/home/tune.js +341 -0
  105. package/dist/src/home/tune.js.map +1 -0
  106. package/dist/src/home/words.js +7 -0
  107. package/dist/src/home/words.js.map +1 -0
  108. package/dist/src/homes.js +14 -0
  109. package/dist/src/homes.js.map +1 -1
  110. package/dist/src/install.js +1 -1
  111. package/dist/src/install.js.map +1 -1
  112. package/dist/src/ipc/workerspec.js +8 -0
  113. package/dist/src/ipc/workerspec.js.map +1 -1
  114. package/dist/src/live/publish.js +1 -1
  115. package/dist/src/live/publish.js.map +1 -1
  116. package/dist/src/livecli.js +1 -1
  117. package/dist/src/livecli.js.map +1 -1
  118. package/dist/src/loginhint.js +8 -3
  119. package/dist/src/loginhint.js.map +1 -1
  120. package/dist/src/loginrefresh/handover.js +121 -0
  121. package/dist/src/loginrefresh/handover.js.map +1 -0
  122. package/dist/src/loginrefresh/lock.js +54 -3
  123. package/dist/src/loginrefresh/lock.js.map +1 -1
  124. package/dist/src/loginrefresh/pirefresh.js +3 -0
  125. package/dist/src/loginrefresh/pirefresh.js.map +1 -1
  126. package/dist/src/loginrefresh/refresher.js +33 -5
  127. package/dist/src/loginrefresh/refresher.js.map +1 -1
  128. package/dist/src/loginrefresh/supervisor.js +18 -1
  129. package/dist/src/loginrefresh/supervisor.js.map +1 -1
  130. package/dist/src/npmverbs.js +46 -14
  131. package/dist/src/npmverbs.js.map +1 -1
  132. package/dist/src/onboard/choices.js +35 -4
  133. package/dist/src/onboard/choices.js.map +1 -1
  134. package/dist/src/onboard/engine.js +32 -306
  135. package/dist/src/onboard/engine.js.map +1 -1
  136. package/dist/src/onboard/pipin.js +22 -0
  137. package/dist/src/onboard/pipin.js.map +1 -0
  138. package/dist/src/onboard/stepforward.js +41 -0
  139. package/dist/src/onboard/stepforward.js.map +1 -0
  140. package/dist/src/onboard/steps/appslug.js +4 -1
  141. package/dist/src/onboard/steps/appslug.js.map +1 -1
  142. package/dist/src/onboard/steps/bringup.js +3 -2
  143. package/dist/src/onboard/steps/bringup.js.map +1 -1
  144. package/dist/src/onboard/steps/compose.js +29 -0
  145. package/dist/src/onboard/steps/compose.js.map +1 -1
  146. package/dist/src/onboard/steps/configfill.js +120 -39
  147. package/dist/src/onboard/steps/configfill.js.map +1 -1
  148. package/dist/src/onboard/steps/githubaccount.js +595 -0
  149. package/dist/src/onboard/steps/githubaccount.js.map +1 -0
  150. package/dist/src/onboard/steps/githubapp.js +65 -7
  151. package/dist/src/onboard/steps/githubapp.js.map +1 -1
  152. package/dist/src/onboard/steps/golive.js +2 -0
  153. package/dist/src/onboard/steps/golive.js.map +1 -1
  154. package/dist/src/onboard/steps/index.js +11 -0
  155. package/dist/src/onboard/steps/index.js.map +1 -1
  156. package/dist/src/onboard/steps/ingress.js +80 -25
  157. package/dist/src/onboard/steps/ingress.js.map +1 -1
  158. package/dist/src/onboard/steps/llmauth.js +103 -30
  159. package/dist/src/onboard/steps/llmauth.js.map +1 -1
  160. package/dist/src/onboard/steps/loginshare.js +213 -0
  161. package/dist/src/onboard/steps/loginshare.js.map +1 -0
  162. package/dist/src/onboard/steps/memory.js +67 -3
  163. package/dist/src/onboard/steps/memory.js.map +1 -1
  164. package/dist/src/onboard/steps/proof.js +366 -56
  165. package/dist/src/onboard/steps/proof.js.map +1 -1
  166. package/dist/src/onboard/steps/repoconfig.js +72 -0
  167. package/dist/src/onboard/steps/repoconfig.js.map +1 -0
  168. package/dist/src/onboard/steps/topology.js +6 -6
  169. package/dist/src/onboard/steps/topology.js.map +1 -1
  170. package/dist/src/onboard/steps/transport.js +9 -1
  171. package/dist/src/onboard/steps/transport.js.map +1 -1
  172. package/dist/src/onboard/steps/tuiroute.js +102 -0
  173. package/dist/src/onboard/steps/tuiroute.js.map +1 -0
  174. package/dist/src/onboard/steps/verifyup.js +17 -1
  175. package/dist/src/onboard/steps/verifyup.js.map +1 -1
  176. package/dist/src/onboard/toolnames.js +30 -0
  177. package/dist/src/onboard/toolnames.js.map +1 -0
  178. package/dist/src/operatorwrite.js +23 -0
  179. package/dist/src/operatorwrite.js.map +1 -0
  180. package/dist/src/outcomes.js +4 -1
  181. package/dist/src/outcomes.js.map +1 -1
  182. package/dist/src/piagentdir.js +13 -0
  183. package/dist/src/piagentdir.js.map +1 -0
  184. package/dist/src/pilogin.js +31 -16
  185. package/dist/src/pilogin.js.map +1 -1
  186. package/dist/src/posting-journal.js +2 -1
  187. package/dist/src/posting-journal.js.map +1 -1
  188. package/dist/src/posting-outbox.js +6 -2
  189. package/dist/src/posting-outbox.js.map +1 -1
  190. package/dist/src/posting-receipt.js +14 -1
  191. package/dist/src/posting-receipt.js.map +1 -1
  192. package/dist/src/qcrbin.js +16 -11
  193. package/dist/src/qcrbin.js.map +1 -1
  194. package/dist/src/recheck.js +2 -1
  195. package/dist/src/recheck.js.map +1 -1
  196. package/dist/src/reconcile.js +22 -2
  197. package/dist/src/reconcile.js.map +1 -1
  198. package/dist/src/reviewargs.js +122 -0
  199. package/dist/src/reviewargs.js.map +1 -0
  200. package/dist/src/reviewcli.js +7 -161
  201. package/dist/src/reviewcli.js.map +1 -1
  202. package/dist/src/reviewdetach.js +76 -11
  203. package/dist/src/reviewdetach.js.map +1 -1
  204. package/dist/src/reviewhistory.js +32 -6
  205. package/dist/src/reviewhistory.js.map +1 -1
  206. package/dist/src/reviewnow.js +45 -0
  207. package/dist/src/reviewnow.js.map +1 -0
  208. package/dist/src/scancli.js +39 -691
  209. package/dist/src/scancli.js.map +1 -1
  210. package/dist/src/selfimprove.js +66 -0
  211. package/dist/src/selfimprove.js.map +1 -0
  212. package/dist/src/setup/adapters.js +5 -223
  213. package/dist/src/setup/adapters.js.map +1 -1
  214. package/dist/src/setup/caserunner.js +15 -249
  215. package/dist/src/setup/caserunner.js.map +1 -1
  216. package/dist/src/setup/engine.js +32 -341
  217. package/dist/src/setup/engine.js.map +1 -1
  218. package/dist/src/setup/facts.js +3 -592
  219. package/dist/src/setup/facts.js.map +1 -1
  220. package/dist/src/setup/findings.js +3 -947
  221. package/dist/src/setup/findings.js.map +1 -1
  222. package/dist/src/setup/presentation.js +3 -8
  223. package/dist/src/setup/presentation.js.map +1 -1
  224. package/dist/src/setup/scan.js +3 -724
  225. package/dist/src/setup/scan.js.map +1 -1
  226. package/dist/src/setup/scanbuild.js +9 -92
  227. package/dist/src/setup/scanbuild.js.map +1 -1
  228. package/dist/src/setup/scanlease.js +3 -250
  229. package/dist/src/setup/scanlease.js.map +1 -1
  230. package/dist/src/setup/scanreport.js +3 -288
  231. package/dist/src/setup/scanreport.js.map +1 -1
  232. package/dist/src/setup/scanrun.js +3 -1732
  233. package/dist/src/setup/scanrun.js.map +1 -1
  234. package/dist/src/setup/scantree.js +3 -256
  235. package/dist/src/setup/scantree.js.map +1 -1
  236. package/dist/src/setup/stage2request.js +3 -258
  237. package/dist/src/setup/stage2request.js.map +1 -1
  238. package/dist/src/setup/unitcut.js +3 -436
  239. package/dist/src/setup/unitcut.js.map +1 -1
  240. package/dist/src/state.js +2 -0
  241. package/dist/src/state.js.map +1 -1
  242. package/dist/src/statemachine.js +4 -1
  243. package/dist/src/statemachine.js.map +1 -1
  244. package/dist/src/takeover.js +1 -0
  245. package/dist/src/takeover.js.map +1 -1
  246. package/dist/src/takeoverpi.js +15 -1
  247. package/dist/src/takeoverpi.js.map +1 -1
  248. package/dist/src/tui/app.js +49 -0
  249. package/dist/src/tui/app.js.map +1 -1
  250. package/dist/src/tui/board.js +88 -12
  251. package/dist/src/tui/board.js.map +1 -1
  252. package/dist/src/tui/cmdbar.js +4 -1
  253. package/dist/src/tui/cmdbar.js.map +1 -1
  254. package/dist/src/tui/companion.js +76 -11
  255. package/dist/src/tui/companion.js.map +1 -1
  256. package/dist/src/tui/connect.js +7 -1
  257. package/dist/src/tui/connect.js.map +1 -1
  258. package/dist/src/tui/deploytab.js +2 -2
  259. package/dist/src/tui/deploytab.js.map +1 -1
  260. package/dist/src/tui/filters.js +5 -2
  261. package/dist/src/tui/filters.js.map +1 -1
  262. package/dist/src/tui/ghread.js +61 -5
  263. package/dist/src/tui/ghread.js.map +1 -1
  264. package/dist/src/tui/index.js +109 -20
  265. package/dist/src/tui/index.js.map +1 -1
  266. package/dist/src/tui/memforward.js +39 -2
  267. package/dist/src/tui/memforward.js.map +1 -1
  268. package/dist/src/tui/memory.js +3 -16
  269. package/dist/src/tui/memory.js.map +1 -1
  270. package/dist/src/tui/memoryurl.js +22 -0
  271. package/dist/src/tui/memoryurl.js.map +1 -0
  272. package/dist/src/tui/model.js +25 -0
  273. package/dist/src/tui/model.js.map +1 -1
  274. package/dist/src/tui/prpause.js +7 -0
  275. package/dist/src/tui/prpause.js.map +1 -1
  276. package/dist/src/tui/remote.js +7 -1
  277. package/dist/src/tui/remote.js.map +1 -1
  278. package/dist/src/tui/route.js +143 -0
  279. package/dist/src/tui/route.js.map +1 -0
  280. package/dist/src/tui/setupstate.js +32 -0
  281. package/dist/src/tui/setupstate.js.map +1 -0
  282. package/dist/src/tui/sshtriage.js +102 -5
  283. package/dist/src/tui/sshtriage.js.map +1 -1
  284. package/dist/src/tui/theme.js +2 -1
  285. package/dist/src/tui/theme.js.map +1 -1
  286. package/dist/src/tui/unwedge.js +2 -12
  287. package/dist/src/tui/unwedge.js.map +1 -1
  288. package/dist/src/verdicts.js +17 -5
  289. package/dist/src/verdicts.js.map +1 -1
  290. package/dist/src/version.js +1 -1
  291. package/dist/src/version.js.map +1 -1
  292. package/dist/src/webhook.js +12 -1
  293. package/dist/src/webhook.js.map +1 -1
  294. package/dist/src/wire.js +34 -8
  295. package/dist/src/wire.js.map +1 -1
  296. package/dist/src/workerworld.js +11 -1
  297. package/dist/src/workerworld.js.map +1 -1
  298. package/docker/README.md +1 -1
  299. package/docker/companion/entrypoint.sh +20 -1
  300. package/docker/entrypoint.sh +57 -6
  301. package/docker-compose.yml +16 -1
  302. package/extensions/cli/home.ts +14 -0
  303. package/extensions/cli/login-route.ts +10 -0
  304. package/extensions/cli/selfimprove.ts +18 -0
  305. package/extensions/home.ts +484 -0
  306. package/extensions/login-route.ts +220 -0
  307. package/extensions/onboarding.ts +41 -11
  308. package/extensions/selfimprove.ts +65 -0
  309. package/extensions/setup-scan.ts +79 -766
  310. package/extensions/setup.ts +14 -70
  311. package/package.json +6 -4
  312. package/prompts/onboarding.md +58 -18
  313. package/schemas/review-result.v1.json +60 -0
  314. package/scripts/deploy.sh +8 -6
  315. package/scripts/mint-token.sh +25 -4
  316. package/scripts/qcr-host.sh +28 -5
  317. package/scripts/vm-ghcr-login.sh +7 -4
  318. package/src/archive/restore.ts +18 -21
  319. package/src/botworker.ts +13 -2
  320. package/src/channelfs.ts +3 -26
  321. package/src/ciretry.ts +2 -1
  322. package/src/cli.ts +59 -67
  323. package/src/cliops.ts +3 -2
  324. package/src/commands.ts +28 -2
  325. package/src/config.ts +62 -4
  326. package/src/daemon.ts +58 -7
  327. package/src/debuground.ts +3 -0
  328. package/src/debugroundpi.ts +12 -1
  329. package/src/dispatch.ts +5 -4
  330. package/src/engine/caserunner-sdk.ts +29 -314
  331. package/src/engine/debugpi.ts +17 -5
  332. package/src/execboundary.ts +16 -4
  333. package/src/github.ts +57 -15
  334. package/src/githubtoken.ts +435 -0
  335. package/src/home/adapters.ts +418 -0
  336. package/src/home/advanced.ts +421 -0
  337. package/src/home/advancedtools.ts +96 -0
  338. package/src/home/autoreview.ts +385 -0
  339. package/src/home/daemonstatus.ts +55 -0
  340. package/src/home/devicelogin.ts +107 -0
  341. package/src/home/launch.ts +257 -0
  342. package/src/home/loginowner.ts +248 -0
  343. package/src/home/memory.ts +176 -0
  344. package/src/home/model.ts +965 -0
  345. package/src/home/panel.ts +666 -0
  346. package/src/home/ports.ts +338 -0
  347. package/src/home/review.ts +206 -0
  348. package/src/home/reviews.ts +565 -0
  349. package/src/home/setup.ts +689 -0
  350. package/src/home/setupadapters.ts +217 -0
  351. package/src/home/setupcard.ts +288 -0
  352. package/src/home/setuptools.ts +57 -0
  353. package/src/home/spend.ts +194 -0
  354. package/src/home/switches.ts +293 -0
  355. package/src/home/switchon.ts +554 -0
  356. package/src/home/toolnames.ts +46 -0
  357. package/src/home/tune.ts +405 -0
  358. package/src/home/words.ts +8 -0
  359. package/src/homes.ts +17 -0
  360. package/src/install.ts +1 -1
  361. package/src/ipc/workerspec.ts +15 -0
  362. package/src/live/publish.ts +4 -1
  363. package/src/livecli.ts +6 -1
  364. package/src/loginhint.ts +8 -3
  365. package/src/loginrefresh/handover.ts +138 -0
  366. package/src/loginrefresh/lock.ts +72 -3
  367. package/src/loginrefresh/pirefresh.ts +3 -0
  368. package/src/loginrefresh/refresher.ts +34 -5
  369. package/src/loginrefresh/supervisor.ts +25 -2
  370. package/src/npmverbs.ts +55 -13
  371. package/src/onboard/choices.ts +40 -4
  372. package/src/onboard/engine.ts +70 -421
  373. package/src/onboard/pipin.ts +31 -0
  374. package/src/onboard/stepforward.ts +61 -0
  375. package/src/onboard/steps/appslug.ts +4 -1
  376. package/src/onboard/steps/bringup.ts +3 -2
  377. package/src/onboard/steps/compose.ts +31 -0
  378. package/src/onboard/steps/configfill.ts +144 -40
  379. package/src/onboard/steps/githubaccount.ts +716 -0
  380. package/src/onboard/steps/githubapp.ts +92 -8
  381. package/src/onboard/steps/golive.ts +2 -0
  382. package/src/onboard/steps/index.ts +11 -0
  383. package/src/onboard/steps/ingress.ts +98 -31
  384. package/src/onboard/steps/llmauth.ts +114 -29
  385. package/src/onboard/steps/loginshare.ts +258 -0
  386. package/src/onboard/steps/memory.ts +76 -3
  387. package/src/onboard/steps/proof.ts +473 -55
  388. package/src/onboard/steps/repoconfig.ts +85 -0
  389. package/src/onboard/steps/topology.ts +7 -6
  390. package/src/onboard/steps/transport.ts +15 -0
  391. package/src/onboard/steps/tuiroute.ts +119 -0
  392. package/src/onboard/steps/verifyup.ts +20 -1
  393. package/src/onboard/toolnames.ts +34 -0
  394. package/src/operatorwrite.ts +25 -0
  395. package/src/outcomes.ts +8 -1
  396. package/src/piagentdir.ts +12 -0
  397. package/src/pilogin.ts +32 -16
  398. package/src/posting-journal.ts +6 -1
  399. package/src/posting-outbox.ts +10 -2
  400. package/src/posting-receipt.ts +17 -0
  401. package/src/qcrbin.ts +16 -11
  402. package/src/recheck.ts +2 -1
  403. package/src/reconcile.ts +29 -3
  404. package/src/reviewargs.ts +119 -0
  405. package/src/reviewcli.ts +6 -167
  406. package/src/reviewdetach.ts +107 -13
  407. package/src/reviewhistory.ts +20 -6
  408. package/src/reviewnow.ts +76 -0
  409. package/src/scancli.ts +75 -826
  410. package/src/selfimprove.ts +90 -0
  411. package/src/setup/adapters.ts +13 -221
  412. package/src/setup/caserunner.ts +34 -273
  413. package/src/setup/engine.ts +62 -496
  414. package/src/setup/facts.ts +3 -627
  415. package/src/setup/findings.ts +3 -1264
  416. package/src/setup/presentation.ts +3 -18
  417. package/src/setup/scan.ts +3 -1033
  418. package/src/setup/scanbuild.ts +20 -114
  419. package/src/setup/scanlease.ts +3 -297
  420. package/src/setup/scanreport.ts +3 -373
  421. package/src/setup/scanrun.ts +3 -2268
  422. package/src/setup/scantree.ts +3 -259
  423. package/src/setup/stage2request.ts +3 -347
  424. package/src/setup/unitcut.ts +3 -523
  425. package/src/state.ts +43 -0
  426. package/src/statemachine.ts +4 -1
  427. package/src/takeover.ts +5 -0
  428. package/src/takeoverpi.ts +16 -2
  429. package/src/tui/app.ts +47 -0
  430. package/src/tui/board.ts +111 -14
  431. package/src/tui/cmdbar.ts +4 -1
  432. package/src/tui/companion.ts +80 -11
  433. package/src/tui/connect.ts +7 -1
  434. package/src/tui/deploytab.ts +2 -2
  435. package/src/tui/filters.ts +7 -3
  436. package/src/tui/ghread.ts +74 -7
  437. package/src/tui/index.ts +117 -23
  438. package/src/tui/memforward.ts +52 -3
  439. package/src/tui/memory.ts +3 -15
  440. package/src/tui/memoryurl.ts +22 -0
  441. package/src/tui/model.ts +36 -2
  442. package/src/tui/prpause.ts +11 -2
  443. package/src/tui/remote.ts +9 -1
  444. package/src/tui/route.ts +173 -0
  445. package/src/tui/setupstate.ts +56 -0
  446. package/src/tui/sshtriage.ts +127 -6
  447. package/src/tui/theme.ts +2 -1
  448. package/src/tui/unwedge.ts +2 -14
  449. package/src/verdicts.ts +23 -5
  450. package/src/version.ts +1 -1
  451. package/src/webhook.ts +10 -1
  452. package/src/wire.ts +42 -9
  453. package/src/workerworld.ts +16 -2
  454. package/dist/src/live/projection.js +0 -450
  455. package/dist/src/live/projection.js.map +0 -1
  456. package/dist/src/live/read.js +0 -60
  457. package/dist/src/live/read.js.map +0 -1
  458. package/dist/src/onboard/launch.js +0 -83
  459. package/dist/src/onboard/launch.js.map +0 -1
  460. package/dist/src/setup/ledger.js +0 -207
  461. package/dist/src/setup/ledger.js.map +0 -1
  462. package/dist/src/setup/stage2.js +0 -617
  463. package/dist/src/setup/stage2.js.map +0 -1
  464. package/dist/src/setup/textsniff.js +0 -89
  465. package/dist/src/setup/textsniff.js.map +0 -1
  466. package/src/live/projection.ts +0 -491
  467. package/src/live/read.ts +0 -86
  468. package/src/onboard/launch.ts +0 -115
  469. package/src/setup/ledger.ts +0 -324
  470. package/src/setup/stage2.ts +0 -926
  471. package/src/setup/textsniff.ts +0 -95
@@ -53,7 +53,14 @@ import { CONTEXT_PACK_FILE } from "@quickchatai/pi-quickreview/src/contextpack.j
53
53
  import { verifiedPayload } from "./session-command-payload.js";
54
54
  import { DEBUG_START_MESSAGE } from "./engine/debugpi.js";
55
55
  import { DEBUG_STATUS_NAME, debugSession, type DebugRoundSpec } from "./debuground.js";
56
- import { onTerminal, paneLaunch, prepareBorrowedAgent, takeoverEnv } from "./takeoverpi.js";
56
+ import {
57
+ onTerminal,
58
+ paneLaunch,
59
+ prepareBorrowedAgent,
60
+ readLogin,
61
+ takeoverEnv,
62
+ } from "./takeoverpi.js";
63
+ import { routeModelSpecForLogin } from "@quickchatai/pi-quickreview/src/modelroute.js";
57
64
 
58
65
  export { DEBUG_START_MESSAGE };
59
66
 
@@ -76,6 +83,7 @@ export function readDebugSpec(file: string): DebugRoundSpec {
76
83
  raw.session !== debugSession(raw.pr) ||
77
84
  typeof raw.command_id !== "string" ||
78
85
  typeof raw.agent_dir !== "string" ||
86
+ (raw.login_dir !== undefined && typeof raw.login_dir !== "string") ||
79
87
  typeof raw.borrow_dir !== "string" ||
80
88
  typeof raw.session_dir !== "string" ||
81
89
  typeof raw.model !== "string" ||
@@ -225,6 +233,9 @@ export async function runDebugRoundPane(
225
233
  return 3;
226
234
  }
227
235
  const env = debugRoundEnv(procEnv, spec);
236
+ // An OpenAI model runs on the OpenAI login the borrowed copy holds
237
+ // (packages/pi-plugin/src/modelroute.ts): the spec's own provider when it is there.
238
+ spec = { ...spec, model: routeModelSpecForLogin(spec.model, readLogin(spec.borrow_dir), env) };
228
239
  const pi = paneLaunch(debugRoundArgv(spec), deps.piBin ?? spec.pi_bin);
229
240
  const launch = toolIsolation(pi.bin, pi.argv, procEnv);
230
241
  if ("refused" in launch) {
package/src/dispatch.ts CHANGED
@@ -15,6 +15,7 @@
15
15
  // plan and the order are built by the review planner (packages/pi-plugin/src/planner.ts), and the
16
16
  // prompt renderer (packages/pi-plugin/src/templates.ts) is injected so this module has no content
17
17
  // dependency.
18
+ import { botAccountLogin } from "@quickchatai/pi-quickreview/src/identity.js";
18
19
  import { existsSync, mkdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
19
20
  import { join, resolve } from "node:path";
20
21
  import type { Config } from "./config.js";
@@ -1247,7 +1248,7 @@ async function dispatchUnlocked(deps: DispatchDeps): Promise<RoundOrder[]> {
1247
1248
  );
1248
1249
  }
1249
1250
  const restored = restoreStandingLifecycle(store, row.number, standing, {
1250
- botLogin: cfg.github.bot_login,
1251
+ botLogin: botAccountLogin(cfg.github),
1251
1252
  now,
1252
1253
  reason: standing ? "no-new-evidence" : "no-new-evidence:standing-unknown",
1253
1254
  });
@@ -1329,7 +1330,7 @@ async function dispatchUnlocked(deps: DispatchDeps): Promise<RoundOrder[]> {
1329
1330
  { type: "verdict", verdict: standing },
1330
1331
  {},
1331
1332
  {
1332
- botLogin: cfg.github.bot_login,
1333
+ botLogin: botAccountLogin(cfg.github),
1333
1334
  now,
1334
1335
  reason: `dispatch-short-circuit:${sc.reason}`,
1335
1336
  },
@@ -2086,7 +2087,7 @@ async function dispatchUnlocked(deps: DispatchDeps): Promise<RoundOrder[]> {
2086
2087
  row.number,
2087
2088
  { type: "dispatched" },
2088
2089
  {},
2089
- { botLogin: cfg.github.bot_login, now },
2090
+ { botLogin: botAccountLogin(cfg.github), now },
2090
2091
  );
2091
2092
  store.logEvent("round_dispatched", row.number, {
2092
2093
  round_id: roundId,
@@ -2182,7 +2183,7 @@ async function dispatchUnlocked(deps: DispatchDeps): Promise<RoundOrder[]> {
2182
2183
  row.number,
2183
2184
  { type: "dispatched" },
2184
2185
  {},
2185
- { botLogin: cfg.github.bot_login, now },
2186
+ { botLogin: botAccountLogin(cfg.github), now },
2186
2187
  );
2187
2188
  } catch (publishError) {
2188
2189
  log(
@@ -1,12 +1,6 @@
1
- // The scan's leaf runner on the SDK: SetupEngine's `CaseRunner` port
2
- // (src/setup/engine.ts) over the runtime port, the in-process twin of
3
- // `piCaseRunner` (src/setup/caserunner.ts). One case is one session on the
4
- // runtime, with the same pieces the child process gets: the agent file's model,
5
- // tools and body (appended to pi's own system prompt), bash-guard the one
6
- // extension, no skill and no context file, a cwd that is first-party ground and
7
- // never the reviewed checkout, and the allowlisted env. The outcome, the reads
8
- // and the kept transcript are read off the same event stream by the same
9
- // reader (`caseReadout`), so a case answers identically whichever host ran it.
1
+ // The scan driver's SDK leaves: the leaf runner on the SDK (scanleaves.ts
2
+ // `sdkCaseRunner`, the in-process twin of `piCaseRunner`) over a host this
3
+ // module builds per case, on the owner's borrowed login.
10
4
  //
11
5
  // WHERE IT RUNS. Only in scancli's detached driver, a disposable one-scan
12
6
  // process (the scan sandbox or the host process `detachDriver` starts), whose
@@ -20,27 +14,10 @@
20
14
  // login (engineCredentials): a token the companion refreshed mid-scan is
21
15
  // picked up at the next case, a token the case would not outlive is refused
22
16
  // before it starts, and nothing is ever refreshed or written here.
17
+ import { copyFileSync, existsSync, mkdirSync, rmSync } from "node:fs";
18
+ import { homedir } from "node:os";
19
+ import { isAbsolute, join } from "node:path";
23
20
  import {
24
- appendFileSync,
25
- copyFileSync,
26
- existsSync,
27
- mkdirSync,
28
- mkdtempSync,
29
- readFileSync,
30
- rmSync,
31
- writeFileSync,
32
- } from "node:fs";
33
- import { homedir, tmpdir } from "node:os";
34
- import { basename, dirname, isAbsolute, join } from "node:path";
35
- import type { ExtensionFactory } from "@earendil-works/pi-coding-agent";
36
- import {
37
- bashGuardConfigFromEnv,
38
- createBashGuard,
39
- } from "@quickchatai/pi-quickreview/extensions/bash-guard.js";
40
- import { CASE_STOP_GRACE_MS } from "@quickchatai/pi-quickreview/src/casebudget.js";
41
- import { splitModelSpec } from "../config.js";
42
- import {
43
- AuthError,
44
21
  AuthExpiring,
45
22
  SCAN_AUTH_SRC_ENV,
46
23
  SCAN_CASE_AUTH_BUDGET_MS,
@@ -48,293 +25,30 @@ import {
48
25
  scanAuthExpiring,
49
26
  scanAuthMissing,
50
27
  } from "@quickchatai/pi-quickreview/src/engine/auth.js";
51
- import {
52
- TRANSCRIPT_RUNS_KEPT,
53
- caseReadout,
54
- leafEnv,
55
- parseAgentFile,
56
- pruneTranscripts,
57
- stderrExcerpt,
58
- transcriptFile,
59
- type AgentFile,
60
- } from "../setup/caserunner.js";
61
- import type { CaseOutcome, CaseRunner, CaseSpec } from "../setup/engine.js";
62
- import { LENS_SESSIONS_DIR } from "@quickchatai/pi-quickreview/src/engine/openable.js";
63
28
  import { PiHost } from "@quickchatai/pi-quickreview/src/engine/pisdk.js";
64
- import type {
65
- Runtime,
66
- SessionSpec,
67
- TurnResult,
68
- } from "@quickchatai/pi-quickreview/src/engine/port.js";
69
- import {
70
- sdkRuntime,
71
- type SessionExtras,
72
- } from "@quickchatai/pi-quickreview/src/engine/sdkruntime.js";
29
+ import { sdkRuntime } from "@quickchatai/pi-quickreview/src/engine/sdkruntime.js";
73
30
  import { BUDGET_EXIT_CODE } from "@quickchatai/pi-quickreview/src/engine/budget.js";
74
-
75
- /** A leaf turn's hard wall clock. The login's own case budget
76
- * (`SCAN_CASE_AUTH_BUDGET_MS`) is the usual bound and aborts first; this one
77
- * only backs it where no login budget applies, and is kept past it so the
78
- * budget's last-resort step (it ends the process) never races the abort. */
79
- export const LEAF_TURN_TIMEOUT_MS = SCAN_CASE_AUTH_BUDGET_MS + 15 * 60_000;
80
-
81
- /** What the model's bash may draw from the leaf env, beyond pi's defaults:
82
- * certificates, proxies, XDG dirs and the temp dirs. Never a provider key. */
83
- const LEAF_BASH_ENV_ALLOW: readonly string[] = [
84
- "TMP",
85
- "TEMP",
86
- "XDG_*",
87
- "SSL_CERT_FILE",
88
- "SSL_CERT_DIR",
89
- "NODE_EXTRA_CA_CERTS",
90
- "HTTP_PROXY",
91
- "HTTPS_PROXY",
92
- "NO_PROXY",
93
- "ALL_PROXY",
94
- "http_proxy",
95
- "https_proxy",
96
- "no_proxy",
97
- "all_proxy",
98
- ];
99
-
100
- /** The per-session pieces the runner hands whoever builds the runtime: the
101
- * `extras` of `sdkRuntime` (packages/pi-plugin/src/engine/sdkruntime.ts). */
102
- export type LeafExtras = (spec: SessionSpec) => SessionExtras;
103
-
104
- export interface SdkCaseRunnerOptions {
105
- /** The scan agent build: `<agent>.md` files. */
106
- agentsDir: string;
107
- /** The leaf's working directory: first-party ground, never the reviewed
108
- * checkout (whose own pi config must not load). Also what a relative read is
109
- * resolved against. */
110
- cwd: string;
111
- /** Where each case's event stream is kept; absent, none is. */
112
- transcriptDir?: string;
113
- /** The env the leaf's env is filtered from. Omitted: process.env. */
114
- env?: NodeJS.ProcessEnv;
115
- /** The model of an agent that names none. */
116
- defaultModel?: string;
117
- /** The runtime this case runs on, built with the case's `extras`. Called once
118
- * per case: a route that must re-read its login per case builds its host
119
- * here. */
120
- runtime: (extras: LeafExtras) => Runtime | Promise<Runtime>;
121
- /** The guard extension. Omitted: bash-guard over the leaf env. */
122
- guard?: ExtensionFactory;
123
- /** Wall clock for a case's turn. Omitted: `LEAF_TURN_TIMEOUT_MS`. */
124
- timeoutMs?: number;
125
- }
126
-
127
- /** A transcript's path under `dir`, relative and absolute, with its run
128
- * directory made (and the old runs pruned when it is new); null when it cannot
129
- * be made. A transcript is evidence, never a reason to fail a case. */
130
- function openTranscript(dir: string, id: string): { rel: string; at: string } | null {
131
- try {
132
- const rel = transcriptFile(id);
133
- const at = join(dir, rel);
134
- const runDir = rel.includes("/") ? rel.slice(0, rel.indexOf("/")) : null;
135
- const fresh = runDir !== null && !existsSync(join(dir, runDir));
136
- mkdirSync(dirname(at), { recursive: true });
137
- if (fresh) pruneTranscripts(dir, TRANSCRIPT_RUNS_KEPT, runDir);
138
- writeFileSync(at, "", { mode: 0o600 });
139
- return { rel, at };
140
- } catch {
141
- return null;
142
- }
143
- }
144
-
145
- /** Where a case's session JSONL goes: `<run>/lens-sessions/<case>/` beside the
146
- * transcript `rel` names (`<run>/<case>.jsonl`). */
147
- export function caseSessionDir(transcriptDir: string, rel: string): string {
148
- return join(transcriptDir, dirname(rel), LENS_SESSIONS_DIR, basename(rel, ".jsonl"));
149
- }
150
-
151
- /**
152
- * Runs a case as one SDK session, the way `piCaseRunner` runs it as one child:
153
- * the agent's model (its `:thinking` suffix as the session's thinking level),
154
- * its tools, its body appended to pi's system prompt, the task as the one
155
- * message. The answer is the last assistant message's text; the cost is pi's
156
- * own per-message accounting summed (null when it reported none); `reads` is the
157
- * `read`/`grep` calls that finished without an error, resolved against `cwd`.
158
- * With `transcriptDir` the session's whole event stream is kept at
159
- * `transcriptFile(spec.id)` under it, the format `pi --mode json` prints, and
160
- * its session JSONL beside it (`caseSessionDir`). An
161
- * abort stops the session (abort, then dispose after the stop grace). Every
162
- * outcome resolves — a runtime that cannot be built or a session that cannot
163
- * start is a failed case, never a rejection.
164
- */
165
- export function sdkCaseRunner(opts: SdkCaseRunnerOptions): CaseRunner {
166
- return {
167
- run: async (
168
- spec: CaseSpec,
169
- signal: AbortSignal,
170
- onCost?: (spentSoFar: number) => void,
171
- ): Promise<CaseOutcome> => {
172
- if (!/^[a-z][a-z0-9-]*$/.test(spec.agent))
173
- return {
174
- ok: false,
175
- reason: `not an agent name: ${JSON.stringify(spec.agent)}`,
176
- cost_usd: 0,
177
- };
178
- let agent: AgentFile | null;
179
- try {
180
- agent = parseAgentFile(readFileSync(join(opts.agentsDir, `${spec.agent}.md`), "utf8"));
181
- } catch {
182
- return { ok: false, reason: `no agent ${spec.agent} in ${opts.agentsDir}`, cost_usd: 0 };
183
- }
184
- if (agent === null || agent.name !== spec.agent)
185
- return { ok: false, reason: `${spec.agent}.md is not an agent file`, cost_usd: 0 };
186
- const modelSpec = agent.model ?? opts.defaultModel;
187
- if (!modelSpec)
188
- return { ok: false, reason: `${spec.agent} names no model and none is set`, cost_usd: 0 };
189
- if (signal.aborted) return { ok: false, reason: "stopped before it started", cost_usd: 0 };
190
-
191
- let tmp: string;
192
- try {
193
- tmp = mkdtempSync(join(tmpdir(), "qcr-case-"));
194
- } catch (err) {
195
- return { ok: false, reason: `no temp directory for the case: ${String(err)}`, cost_usd: 0 };
196
- }
197
- let session: Awaited<ReturnType<Runtime["create"]>> | undefined;
198
- try {
199
- const env = leafEnv(opts.env);
200
- const { model, thinking } = splitModelSpec(modelSpec);
201
- const guard = opts.guard ?? createBashGuard(bashGuardConfigFromEnv(env, opts.cwd));
202
- const extras: LeafExtras = () => ({
203
- extensions: [guard],
204
- appendSystemPrompt: [agent.systemPrompt],
205
- bashEnv: { allow: LEAF_BASH_ENV_ALLOW },
206
- ...(thinking !== null
207
- ? { thinkingLevel: thinking as NonNullable<SessionExtras["thinkingLevel"]> }
208
- : {}),
209
- });
210
- // The transcript: opened before the session, appended event by event,
211
- // abandoned (and its name dropped from the outcome) at the first write
212
- // that fails.
213
- const opened =
214
- opts.transcriptDir !== undefined ? openTranscript(opts.transcriptDir, spec.id) : null;
215
- const sessionSpec: SessionSpec = {
216
- cwd: opts.cwd,
217
- env,
218
- model,
219
- ...(agent.tools.length > 0 ? { tools: agent.tools } : {}),
220
- name: spec.id,
221
- // The case's session JSONL beside its transcript, so the case
222
- // opens in pi: `qcr open <run dir> --lens <case>`.
223
- ...(opts.transcriptDir !== undefined && opened !== null
224
- ? { sessionDir: caseSessionDir(opts.transcriptDir, opened.rel) }
225
- : {}),
226
- eventsFile: join(tmp, "events.jsonl"),
227
- stderrFile: join(tmp, "stderr.txt"),
228
- limits: { timeoutMs: opts.timeoutMs ?? LEAF_TURN_TIMEOUT_MS },
229
- };
230
-
231
- let transcript: string | undefined = opened?.rel;
232
- let transcriptAt: string | null = opened?.at ?? null;
233
- const keep = (event: unknown): void => {
234
- if (transcriptAt === null) return;
235
- try {
236
- appendFileSync(transcriptAt, JSON.stringify(event) + "\n");
237
- } catch {
238
- transcriptAt = null;
239
- transcript = undefined;
240
- }
241
- };
242
- const readout = caseReadout(opts.cwd, onCost);
243
- const finish = <T extends CaseOutcome>(o: T): T =>
244
- transcript === undefined ? o : { ...o, transcript };
245
-
246
- try {
247
- const runtime = await opts.runtime(extras);
248
- session = await runtime.create(sessionSpec);
249
- } catch (err) {
250
- const reason = err instanceof Error ? err.message : String(err);
251
- // A refused login already says what to do; anything else is the host's.
252
- return {
253
- ok: false,
254
- reason: err instanceof AuthError ? reason : `could not start the session: ${reason}`,
255
- cost_usd: 0,
256
- };
257
- }
258
- session.subscribe((event) => {
259
- keep(event);
260
- readout.feed(event);
261
- });
262
-
263
- let stopped = false;
264
- let hardStop: ((o: CaseOutcome) => void) | undefined;
265
- const gone = new Promise<CaseOutcome>((resolve) => {
266
- hardStop = resolve;
267
- });
268
- const onAbort = (): void => {
269
- stopped = true;
270
- const live = session;
271
- void live?.abort().catch(() => {});
272
- // The stop grace, then the session is released; a turn still not
273
- // settled a grace later is given up on, so the case resolves.
274
- setTimeout(() => void live?.dispose().catch(() => {}), CASE_STOP_GRACE_MS).unref();
275
- setTimeout(
276
- () => hardStop?.({ ok: false, reason: "stopped", cost_usd: readout.spent() }),
277
- 2 * CASE_STOP_GRACE_MS,
278
- ).unref();
279
- };
280
- signal.addEventListener("abort", onAbort, { once: true });
281
- if (signal.aborted) {
282
- onAbort();
283
- // Cancelled while the session was being created: no turn starts.
284
- return finish({ ok: false, reason: "stopped", cost_usd: 0 });
285
- }
286
-
287
- const turn = (async (): Promise<CaseOutcome> => {
288
- let result: TurnResult;
289
- try {
290
- result = await session.prompt(`Task: ${spec.prompt}`);
291
- } catch (err) {
292
- return {
293
- ok: false,
294
- reason: `the session failed: ${err instanceof Error ? err.message : String(err)}`,
295
- cost_usd: readout.spent(),
296
- };
297
- }
298
- // What a case that ended without an answer had spent: a failure costs
299
- // money too, and a caller holding a budget must count it.
300
- const cost_usd = readout.spent();
301
- if (stopped) return { ok: false, reason: "stopped", cost_usd };
302
- if (result.timedOut)
303
- return { ok: false, reason: "the case ran past its wall clock", cost_usd };
304
- // The model's own reason leads: a provider error also ends the turn non-zero.
305
- if (readout.failure !== null) return { ok: false, reason: readout.failure, cost_usd };
306
- if (result.code !== 0) {
307
- let tail = "";
308
- try {
309
- tail = stderrExcerpt(readFileSync(sessionSpec.stderrFile, "utf8"));
310
- } catch {
311
- // No stderr to quote.
312
- }
313
- return {
314
- ok: false,
315
- reason: `the session ended without a result${tail ? `: ${tail}` : ""}`,
316
- cost_usd,
317
- };
318
- }
319
- if (readout.final.trim() === "")
320
- return { ok: false, reason: "the subagent gave no answer", cost_usd };
321
- return { ok: true, output: readout.final, cost_usd, reads: readout.reads };
322
- })();
323
- try {
324
- return finish(await Promise.race([turn, gone]));
325
- } finally {
326
- signal.removeEventListener("abort", onAbort);
327
- }
328
- } finally {
329
- try {
330
- await session?.dispose();
331
- } catch {
332
- // Already released.
333
- }
334
- rmSync(tmp, { recursive: true, force: true });
335
- }
336
- },
337
- };
31
+ import type { CaseRunner } from "@quickchatai/pi-quickreview/src/scan/scanports.js";
32
+ import {
33
+ LEAF_TURN_TIMEOUT_MS,
34
+ sdkCaseRunner as sdkCaseRunnerOver,
35
+ type SdkCaseRunnerOptions,
36
+ } from "@quickchatai/pi-quickreview/src/scan/scanleaves.js";
37
+
38
+ export {
39
+ LEAF_TURN_TIMEOUT_MS,
40
+ caseSessionDir,
41
+ type LeafExtras,
42
+ type SdkCaseRunnerOptions,
43
+ } from "@quickchatai/pi-quickreview/src/scan/scanleaves.js";
44
+
45
+ /** `sdkCaseRunner` (scanleaves.ts) in this process: the leaf env filtered
46
+ * from this process's env and a read's `~` taken as this process's home,
47
+ * unless told otherwise. */
48
+ export function sdkCaseRunner(
49
+ opts: Omit<SdkCaseRunnerOptions, "env" | "home"> & { env?: NodeJS.ProcessEnv; home?: string },
50
+ ): CaseRunner {
51
+ return sdkCaseRunnerOver({ ...opts, env: opts.env ?? process.env, home: opts.home ?? homedir() });
338
52
  }
339
53
 
340
54
  // ---- the scan driver's host ----------------------------------------------------
@@ -380,6 +94,7 @@ export function scanSdkCases(o: ScanSdkCasesOptions): CaseRunner {
380
94
  throw new Error(`the SDK host's agentDir must be absolute: ${o.agentDir}`);
381
95
  return sdkCaseRunner({
382
96
  agentsDir: o.agentsDir,
97
+ home: homedir(),
383
98
  cwd: o.cwd,
384
99
  ...(o.transcriptDir !== undefined ? { transcriptDir: o.transcriptDir } : {}),
385
100
  env: o.env,
@@ -25,6 +25,7 @@ import { readFileSync, rmSync, writeFileSync } from "node:fs";
25
25
  import { join } from "node:path";
26
26
  import { FINDING_CLUSTERS_ENTRY, extensionFile } from "../coreext.js";
27
27
  import { GITHUB_TOKEN_ENVS } from "@quickchatai/pi-quickreview/src/envstrip.js";
28
+ import { routeModelSpecForLogin } from "@quickchatai/pi-quickreview/src/modelroute.js";
28
29
  import { piCliPath } from "../picli.js";
29
30
  import {
30
31
  DEBUG_STATUS_FILE,
@@ -57,6 +58,15 @@ export const DEBUG_ROUND_EXTENSIONS = [
57
58
  FINDING_CLUSTERS_ENTRY,
58
59
  ] as const;
59
60
 
61
+ /** The login file in an agent dir, as text; null when there is none. */
62
+ function loginIn(agentDir: string): string | null {
63
+ try {
64
+ return readFileSync(join(agentDir, "auth.json"), "utf8");
65
+ } catch {
66
+ return null;
67
+ }
68
+ }
69
+
60
70
  /** pi's first message: the extension command that starts the round. */
61
71
  export const DEBUG_START_MESSAGE = "/qcr-round";
62
72
 
@@ -186,17 +196,19 @@ export async function runDebugRound(i: RoundSpec, deps: DebugRoundDeps = {}): Pr
186
196
  // an earlier attempt's progress record is not this round's
187
197
  rmSync(join(i.outDir, DEBUG_STATUS_FILE), { force: true });
188
198
  log(`review: ${label} - debug round in pi, lenses ${prep.prompts.plannedLenses.join(" ")}`);
199
+ const env = debugEnv(deps.env ?? process.env, prep, {
200
+ order: join(i.outDir, "round-order.md"),
201
+ tasksDir: join(i.outDir, "tasks"),
202
+ });
189
203
  const args = debugArgv({
190
204
  root: i.root,
191
- model: i.cfg.models.conversation,
205
+ // An OpenAI model runs on the OpenAI login the run's agent dir holds
206
+ // (packages/pi-plugin/src/modelroute.ts).
207
+ model: routeModelSpecForLogin(i.cfg.models.conversation, loginIn(i.agentDir), env),
192
208
  sysPath: prep.sysPath,
193
209
  sessionDir: prep.sessionDir,
194
210
  sessionName: prep.sessionName,
195
211
  });
196
- const env = debugEnv(deps.env ?? process.env, prep, {
197
- order: join(i.outDir, "round-order.md"),
198
- tasksDir: join(i.outDir, "tasks"),
199
- });
200
212
  // The whole pi runs as the tool uid, as the process route's did: its bash,
201
213
  // and the lens sessions of its extension, get no capability and cannot read
202
214
  // this process.
@@ -37,6 +37,7 @@
37
37
  // · It detects; it never blocks. A detector that could wedge a review would
38
38
  // be traded away the first time it misfired.
39
39
 
40
+ import { carriesOwnMarker } from "@quickchatai/pi-quickreview/src/identity.js";
40
41
  import { createHash } from "node:crypto";
41
42
  import { readFile, readdir } from "node:fs/promises";
42
43
  import { join, resolve } from "node:path";
@@ -554,11 +555,22 @@ function hasIssuedMarker(body: string, commitId: string, issued: ReadonlySet<str
554
555
  * every GitHub App posts as a `Bot`, and another installed review bot has no
555
556
  * posting trace here by construction, so a type check would report each of
556
557
  * its reviews (and raise the severe wake on its approvals) as ours. */
557
- function isBotAuthored(review: AuditedReview, botLogin: string, appSlug?: string): boolean {
558
+ function isBotAuthored(
559
+ review: AuditedReview,
560
+ botLogin: string,
561
+ appSlug?: string,
562
+ personMode = false,
563
+ ): boolean {
558
564
  const login = review.user.toLowerCase();
565
+ // Person mode: the login is the person's, who reviews by hand too. Only a
566
+ // review carrying the reviewer's marker or footer is the reviewer's — so a
567
+ // write outside the gate that carries neither is invisible here (recorded
568
+ // in docs/exec-boundary.md), and the person's own approval never alarms.
569
+ if (personMode)
570
+ return login === botLogin.toLowerCase() && botLogin !== "" && carriesOwnMarker(review.body);
559
571
  return (
560
572
  login === botLogin.toLowerCase() ||
561
- (appSlug !== undefined && login === `${appSlug.toLowerCase()}[bot]`)
573
+ (appSlug !== undefined && appSlug !== "" && login === `${appSlug.toLowerCase()}[bot]`)
562
574
  );
563
575
  }
564
576
 
@@ -573,7 +585,7 @@ export function auditUngatedReviews(
573
585
  reviews: readonly AuditedReview[],
574
586
  headSha: string,
575
587
  traces: PostingTraces,
576
- opts: { botLogin: string; appSlug?: string },
588
+ opts: { botLogin: string; appSlug?: string; personMode?: boolean },
577
589
  ): UngatedReview[] {
578
590
  const head = headSha.toLowerCase();
579
591
  const out: UngatedReview[] = [];
@@ -583,7 +595,7 @@ export function auditUngatedReviews(
583
595
  // recorded in docs/exec-boundary.md rather than implied away.
584
596
  let migrationExcusesLeft = traces.migrationPrecheck ? 1 : 0;
585
597
  for (const r of reviews) {
586
- if (!isBotAuthored(r, opts.botLogin, opts.appSlug)) continue;
598
+ if (!isBotAuthored(r, opts.botLogin, opts.appSlug, opts.personMode === true)) continue;
587
599
  const state = (r.state ?? "").toUpperCase();
588
600
  if (state === "PENDING" || state === "DISMISSED") continue;
589
601
  if ((r.commit_id ?? "").toLowerCase() !== head) continue;