@quickchatai/quickreview 0.0.0-stage → 1.1.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 (1443) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +385 -2
  3. package/THIRD-PARTY-NOTICES.md +134 -0
  4. package/agents/pass-abstraction.md +64 -0
  5. package/agents/pass-ask-human.md +134 -0
  6. package/agents/pass-backcompat.md +67 -0
  7. package/agents/pass-baseline.md +61 -0
  8. package/agents/pass-behaviour-delta.md +64 -0
  9. package/agents/pass-bounds.md +36 -0
  10. package/agents/pass-claims.md +31 -0
  11. package/agents/pass-concurrency.md +168 -0
  12. package/agents/pass-consistency.md +57 -0
  13. package/agents/pass-contracts.md +30 -0
  14. package/agents/pass-data-safety.md +54 -0
  15. package/agents/pass-deferred.md +35 -0
  16. package/agents/pass-delivery.md +37 -0
  17. package/agents/pass-duplication.md +60 -0
  18. package/agents/pass-intent.md +63 -0
  19. package/agents/pass-lint-conformance.md +46 -0
  20. package/agents/pass-maintainability.md +67 -0
  21. package/agents/pass-placement.md +68 -0
  22. package/agents/pass-prod-risk.md +24 -0
  23. package/agents/pass-rollout.md +32 -0
  24. package/agents/pass-roundtrip.md +46 -0
  25. package/agents/pass-security.md +31 -0
  26. package/agents/pass-simplicity.md +68 -0
  27. package/agents/pass-statelessness.md +27 -0
  28. package/agents/pass-style.md +37 -0
  29. package/agents/pass-sweep.md +57 -0
  30. package/agents/pass-tests.md +58 -0
  31. package/agents/pass-ui.md +23 -0
  32. package/agents/pass-validate.md +50 -0
  33. package/agents/pass-widened.md +46 -0
  34. package/agents/scan/pass-scan.md +86 -0
  35. package/bin/qcr.js +11 -0
  36. package/config.example.toml +1067 -0
  37. package/deploy/ansible/README.md +356 -0
  38. package/deploy/ansible/group_vars/all.yml +212 -0
  39. package/deploy/ansible/inventory.example.ini +25 -0
  40. package/deploy/ansible/playbook.yml +360 -0
  41. package/deploy/collect-secrets.sh +289 -0
  42. package/deploy/collect-slack-secrets.mjs +1190 -0
  43. package/deploy/github-app-manifest.json +31 -0
  44. package/deploy/manifest.schema.json +75 -0
  45. package/deploy/qcr-update.sh +803 -0
  46. package/deploy/reviewer/AGENTS.override.md +12 -0
  47. package/dist/extensions/cli/bash-guard-ambient.js +42 -0
  48. package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
  49. package/dist/extensions/cli/bash-guard.js +5 -0
  50. package/dist/extensions/cli/bash-guard.js.map +1 -0
  51. package/dist/extensions/cli/companion-guard.js +5 -0
  52. package/dist/extensions/cli/companion-guard.js.map +1 -0
  53. package/dist/extensions/cli/debug-round.js +8 -0
  54. package/dist/extensions/cli/debug-round.js.map +1 -0
  55. package/dist/extensions/cli/inject-bridge.js +5 -0
  56. package/dist/extensions/cli/inject-bridge.js.map +1 -0
  57. package/dist/extensions/cli/ledger-pin.js +5 -0
  58. package/dist/extensions/cli/ledger-pin.js.map +1 -0
  59. package/dist/extensions/cli/lens.js +13 -0
  60. package/dist/extensions/cli/lens.js.map +1 -0
  61. package/dist/extensions/cli/muted-theme.js +5 -0
  62. package/dist/extensions/cli/muted-theme.js.map +1 -0
  63. package/dist/extensions/cli/onboarding.js +5 -0
  64. package/dist/extensions/cli/onboarding.js.map +1 -0
  65. package/dist/extensions/cli/setup.js +5 -0
  66. package/dist/extensions/cli/setup.js.map +1 -0
  67. package/dist/extensions/cli/tui-view.js +5 -0
  68. package/dist/extensions/cli/tui-view.js.map +1 -0
  69. package/dist/extensions/companion-guard.js +361 -0
  70. package/dist/extensions/companion-guard.js.map +1 -0
  71. package/dist/extensions/debug-round.js +204 -0
  72. package/dist/extensions/debug-round.js.map +1 -0
  73. package/dist/extensions/inject-bridge.js +603 -0
  74. package/dist/extensions/inject-bridge.js.map +1 -0
  75. package/dist/extensions/muted-theme.js +12 -0
  76. package/dist/extensions/muted-theme.js.map +1 -0
  77. package/dist/extensions/onboarding.js +175 -0
  78. package/dist/extensions/onboarding.js.map +1 -0
  79. package/dist/extensions/setup-replay.js +269 -0
  80. package/dist/extensions/setup-replay.js.map +1 -0
  81. package/dist/extensions/setup-scan.js +998 -0
  82. package/dist/extensions/setup-scan.js.map +1 -0
  83. package/dist/extensions/setup-triage.js +175 -0
  84. package/dist/extensions/setup-triage.js.map +1 -0
  85. package/dist/extensions/setup.js +1160 -0
  86. package/dist/extensions/setup.js.map +1 -0
  87. package/dist/extensions/tui-view.js +389 -0
  88. package/dist/extensions/tui-view.js.map +1 -0
  89. package/dist/extensions/vendor/openviking-pi/client.js +376 -0
  90. package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
  91. package/dist/extensions/vendor/openviking-pi/config.js +189 -0
  92. package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
  93. package/dist/extensions/vendor/openviking-pi/index.js +421 -0
  94. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
  95. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
  96. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
  97. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
  98. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
  99. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
  100. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
  101. package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
  102. package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
  103. package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
  104. package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
  105. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
  106. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
  107. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
  108. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
  109. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
  110. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
  111. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
  112. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
  113. package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
  114. package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
  115. package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
  116. package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
  117. package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
  118. package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
  119. package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
  120. package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
  121. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
  122. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
  123. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
  124. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
  125. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
  126. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
  127. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
  128. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
  129. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
  130. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
  131. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
  132. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
  133. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
  134. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
  135. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
  136. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
  137. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
  138. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
  139. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  140. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
  141. package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
  142. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
  143. package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
  144. package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
  145. package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
  146. package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
  147. package/dist/src/anomalies.js +483 -0
  148. package/dist/src/anomalies.js.map +1 -0
  149. package/dist/src/answerworker.js +180 -0
  150. package/dist/src/answerworker.js.map +1 -0
  151. package/dist/src/appstamp.js +68 -0
  152. package/dist/src/appstamp.js.map +1 -0
  153. package/dist/src/archive/admission.js +3 -0
  154. package/dist/src/archive/admission.js.map +1 -0
  155. package/dist/src/archive/archivedb.js +474 -0
  156. package/dist/src/archive/archivedb.js.map +1 -0
  157. package/dist/src/archive/archiver.js +1770 -0
  158. package/dist/src/archive/archiver.js.map +1 -0
  159. package/dist/src/archive/archivewrite.js +198 -0
  160. package/dist/src/archive/archivewrite.js.map +1 -0
  161. package/dist/src/archive/bundle.js +266 -0
  162. package/dist/src/archive/bundle.js.map +1 -0
  163. package/dist/src/archive/dbmove.js +638 -0
  164. package/dist/src/archive/dbmove.js.map +1 -0
  165. package/dist/src/archive/duplicatecounts.js +80 -0
  166. package/dist/src/archive/duplicatecounts.js.map +1 -0
  167. package/dist/src/archive/eligibility.js +113 -0
  168. package/dist/src/archive/eligibility.js.map +1 -0
  169. package/dist/src/archive/extract.js +288 -0
  170. package/dist/src/archive/extract.js.map +1 -0
  171. package/dist/src/archive/fsutil.js +330 -0
  172. package/dist/src/archive/fsutil.js.map +1 -0
  173. package/dist/src/archive/gzfile.js +277 -0
  174. package/dist/src/archive/gzfile.js.map +1 -0
  175. package/dist/src/archive/kinds.js +22 -0
  176. package/dist/src/archive/kinds.js.map +1 -0
  177. package/dist/src/archive/livecheckpoint.js +178 -0
  178. package/dist/src/archive/livecheckpoint.js.map +1 -0
  179. package/dist/src/archive/liveretention.js +145 -0
  180. package/dist/src/archive/liveretention.js.map +1 -0
  181. package/dist/src/archive/manifest.js +284 -0
  182. package/dist/src/archive/manifest.js.map +1 -0
  183. package/dist/src/archive/overseer.js +283 -0
  184. package/dist/src/archive/overseer.js.map +1 -0
  185. package/dist/src/archive/piglobal.js +129 -0
  186. package/dist/src/archive/piglobal.js.map +1 -0
  187. package/dist/src/archive/plan.js +398 -0
  188. package/dist/src/archive/plan.js.map +1 -0
  189. package/dist/src/archive/prarchive.js +831 -0
  190. package/dist/src/archive/prarchive.js.map +1 -0
  191. package/dist/src/archive/prfiles.js +303 -0
  192. package/dist/src/archive/prfiles.js.map +1 -0
  193. package/dist/src/archive/report.js +169 -0
  194. package/dist/src/archive/report.js.map +1 -0
  195. package/dist/src/archive/restore.js +276 -0
  196. package/dist/src/archive/restore.js.map +1 -0
  197. package/dist/src/archive/rows.js +106 -0
  198. package/dist/src/archive/rows.js.map +1 -0
  199. package/dist/src/archive/seal.js +480 -0
  200. package/dist/src/archive/seal.js.map +1 -0
  201. package/dist/src/archive/status.js +528 -0
  202. package/dist/src/archive/status.js.map +1 -0
  203. package/dist/src/archive/storepass.js +409 -0
  204. package/dist/src/archive/storepass.js.map +1 -0
  205. package/dist/src/backfill.js +217 -0
  206. package/dist/src/backfill.js.map +1 -0
  207. package/dist/src/bench/cost.js +87 -0
  208. package/dist/src/bench/exec.js +76 -0
  209. package/dist/src/bench/values.js +14 -0
  210. package/dist/src/botcontrol.js +127 -0
  211. package/dist/src/botcontrol.js.map +1 -0
  212. package/dist/src/botworker.js +805 -0
  213. package/dist/src/botworker.js.map +1 -0
  214. package/dist/src/capture.js +234 -0
  215. package/dist/src/capture.js.map +1 -0
  216. package/dist/src/cases.js +1491 -0
  217. package/dist/src/cases.js.map +1 -0
  218. package/dist/src/channelfs.js +27 -0
  219. package/dist/src/channelfs.js.map +1 -0
  220. package/dist/src/checklist.js +312 -0
  221. package/dist/src/checklist.js.map +1 -0
  222. package/dist/src/ciretry.js +91 -0
  223. package/dist/src/ciretry.js.map +1 -0
  224. package/dist/src/cli.js +1916 -0
  225. package/dist/src/cli.js.map +1 -0
  226. package/dist/src/cliops.js +333 -0
  227. package/dist/src/cliops.js.map +1 -0
  228. package/dist/src/clonedir.js +15 -0
  229. package/dist/src/clonedir.js.map +1 -0
  230. package/dist/src/commandeffects.js +43 -0
  231. package/dist/src/commandeffects.js.map +1 -0
  232. package/dist/src/commands.js +1312 -0
  233. package/dist/src/commands.js.map +1 -0
  234. package/dist/src/companioninbox.js +203 -0
  235. package/dist/src/companioninbox.js.map +1 -0
  236. package/dist/src/compare.js +673 -0
  237. package/dist/src/compare.js.map +1 -0
  238. package/dist/src/config.js +1027 -0
  239. package/dist/src/config.js.map +1 -0
  240. package/dist/src/configcheck.js +111 -0
  241. package/dist/src/configcheck.js.map +1 -0
  242. package/dist/src/coreext.js +43 -0
  243. package/dist/src/coreext.js.map +1 -0
  244. package/dist/src/daemon.js +929 -0
  245. package/dist/src/daemon.js.map +1 -0
  246. package/dist/src/debuground.js +731 -0
  247. package/dist/src/debuground.js.map +1 -0
  248. package/dist/src/debugroundpi.js +219 -0
  249. package/dist/src/debugroundpi.js.map +1 -0
  250. package/dist/src/diagnostics.js +14 -0
  251. package/dist/src/diagnostics.js.map +1 -0
  252. package/dist/src/digest.js +139 -0
  253. package/dist/src/digest.js.map +1 -0
  254. package/dist/src/dispatch.js +1726 -0
  255. package/dist/src/dispatch.js.map +1 -0
  256. package/dist/src/engine/answerguard.js +18 -0
  257. package/dist/src/engine/answerguard.js.map +1 -0
  258. package/dist/src/engine/botsdk.js +262 -0
  259. package/dist/src/engine/botsdk.js.map +1 -0
  260. package/dist/src/engine/caserunner-sdk.js +332 -0
  261. package/dist/src/engine/caserunner-sdk.js.map +1 -0
  262. package/dist/src/engine/debugpi.js +194 -0
  263. package/dist/src/engine/debugpi.js.map +1 -0
  264. package/dist/src/eod.js +473 -0
  265. package/dist/src/eod.js.map +1 -0
  266. package/dist/src/eodpinwatch.js +391 -0
  267. package/dist/src/eodpinwatch.js.map +1 -0
  268. package/dist/src/escalation.js +1009 -0
  269. package/dist/src/escalation.js.map +1 -0
  270. package/dist/src/eventloop.js +779 -0
  271. package/dist/src/eventloop.js.map +1 -0
  272. package/dist/src/execboundary.js +430 -0
  273. package/dist/src/execboundary.js.map +1 -0
  274. package/dist/src/feedback.js +915 -0
  275. package/dist/src/feedback.js.map +1 -0
  276. package/dist/src/findingscheck.js +200 -0
  277. package/dist/src/findingscheck.js.map +1 -0
  278. package/dist/src/fsdenied.js +26 -0
  279. package/dist/src/fsdenied.js.map +1 -0
  280. package/dist/src/ghread.js +181 -0
  281. package/dist/src/ghread.js.map +1 -0
  282. package/dist/src/github.js +1063 -0
  283. package/dist/src/github.js.map +1 -0
  284. package/dist/src/homes.js +103 -0
  285. package/dist/src/homes.js.map +1 -0
  286. package/dist/src/hostroute.js +24 -0
  287. package/dist/src/hostroute.js.map +1 -0
  288. package/dist/src/improvements.js +625 -0
  289. package/dist/src/improvements.js.map +1 -0
  290. package/dist/src/install.js +946 -0
  291. package/dist/src/install.js.map +1 -0
  292. package/dist/src/installkind.js +40 -0
  293. package/dist/src/installkind.js.map +1 -0
  294. package/dist/src/ipc/capability.js +286 -0
  295. package/dist/src/ipc/capability.js.map +1 -0
  296. package/dist/src/ipc/client.js +226 -0
  297. package/dist/src/ipc/client.js.map +1 -0
  298. package/dist/src/ipc/control.js +70 -0
  299. package/dist/src/ipc/control.js.map +1 -0
  300. package/dist/src/ipc/ghreadshim.js +77 -0
  301. package/dist/src/ipc/ghreadshim.js.map +1 -0
  302. package/dist/src/ipc/launcher.js +65 -0
  303. package/dist/src/ipc/launcher.js.map +1 -0
  304. package/dist/src/ipc/permits.js +293 -0
  305. package/dist/src/ipc/permits.js.map +1 -0
  306. package/dist/src/ipc/postshim.js +137 -0
  307. package/dist/src/ipc/postshim.js.map +1 -0
  308. package/dist/src/ipc/protocol.js +239 -0
  309. package/dist/src/ipc/protocol.js.map +1 -0
  310. package/dist/src/ipc/server.js +312 -0
  311. package/dist/src/ipc/server.js.map +1 -0
  312. package/dist/src/ipc/workerspec.js +148 -0
  313. package/dist/src/ipc/workerspec.js.map +1 -0
  314. package/dist/src/learner/learner.js +1272 -0
  315. package/dist/src/learner/learner.js.map +1 -0
  316. package/dist/src/learner/miner.js +948 -0
  317. package/dist/src/learner/miner.js.map +1 -0
  318. package/dist/src/learner/packet.js +216 -0
  319. package/dist/src/learner/packet.js.map +1 -0
  320. package/dist/src/learner/receipt.js +135 -0
  321. package/dist/src/learner/receipt.js.map +1 -0
  322. package/dist/src/learner/status.js +38 -0
  323. package/dist/src/learner/status.js.map +1 -0
  324. package/dist/src/ledgercheck.js +120 -0
  325. package/dist/src/ledgercheck.js.map +1 -0
  326. package/dist/src/lessonledger.js +451 -0
  327. package/dist/src/lessonledger.js.map +1 -0
  328. package/dist/src/lessonselect.js +639 -0
  329. package/dist/src/lessonselect.js.map +1 -0
  330. package/dist/src/lessonshadow.js +540 -0
  331. package/dist/src/lessonshadow.js.map +1 -0
  332. package/dist/src/levers.js +302 -0
  333. package/dist/src/levers.js.map +1 -0
  334. package/dist/src/live/projection.js +450 -0
  335. package/dist/src/live/projection.js.map +1 -0
  336. package/dist/src/live/publish.js +83 -0
  337. package/dist/src/live/publish.js.map +1 -0
  338. package/dist/src/live/read.js +60 -0
  339. package/dist/src/live/read.js.map +1 -0
  340. package/dist/src/livecli.js +288 -0
  341. package/dist/src/livecli.js.map +1 -0
  342. package/dist/src/loginhint.js +42 -0
  343. package/dist/src/loginhint.js.map +1 -0
  344. package/dist/src/loginrefresh/atomic.js +66 -0
  345. package/dist/src/loginrefresh/atomic.js.map +1 -0
  346. package/dist/src/loginrefresh/lock.js +93 -0
  347. package/dist/src/loginrefresh/lock.js.map +1 -0
  348. package/dist/src/loginrefresh/main.js +58 -0
  349. package/dist/src/loginrefresh/main.js.map +1 -0
  350. package/dist/src/loginrefresh/pirefresh.js +49 -0
  351. package/dist/src/loginrefresh/pirefresh.js.map +1 -0
  352. package/dist/src/loginrefresh/refresher.js +286 -0
  353. package/dist/src/loginrefresh/refresher.js.map +1 -0
  354. package/dist/src/loginrefresh/supervisor.js +253 -0
  355. package/dist/src/loginrefresh/supervisor.js.map +1 -0
  356. package/dist/src/loginrefresh/types.js +4 -0
  357. package/dist/src/loginrefresh/types.js.map +1 -0
  358. package/dist/src/memory/context.js +251 -0
  359. package/dist/src/memory/context.js.map +1 -0
  360. package/dist/src/memory/datafence.js +5 -0
  361. package/dist/src/memory/datafence.js.map +1 -0
  362. package/dist/src/memory/delivery.js +131 -0
  363. package/dist/src/memory/delivery.js.map +1 -0
  364. package/dist/src/memory/eventtags.js +81 -0
  365. package/dist/src/memory/eventtags.js.map +1 -0
  366. package/dist/src/memory/health.js +713 -0
  367. package/dist/src/memory/health.js.map +1 -0
  368. package/dist/src/memory/import.js +504 -0
  369. package/dist/src/memory/import.js.map +1 -0
  370. package/dist/src/memory/layout.js +252 -0
  371. package/dist/src/memory/layout.js.map +1 -0
  372. package/dist/src/memory/legacy-file.js +100 -0
  373. package/dist/src/memory/legacy-file.js.map +1 -0
  374. package/dist/src/memory/lessons.js +370 -0
  375. package/dist/src/memory/lessons.js.map +1 -0
  376. package/dist/src/memory/ovsession.js +449 -0
  377. package/dist/src/memory/ovsession.js.map +1 -0
  378. package/dist/src/memory/relayout.js +259 -0
  379. package/dist/src/memory/relayout.js.map +1 -0
  380. package/dist/src/memory/roundactivity.js +180 -0
  381. package/dist/src/memory/roundactivity.js.map +1 -0
  382. package/dist/src/memory/selection.js +690 -0
  383. package/dist/src/memory/selection.js.map +1 -0
  384. package/dist/src/memory/shadowstatus.js +69 -0
  385. package/dist/src/memory/shadowstatus.js.map +1 -0
  386. package/dist/src/memory/store.js +83 -0
  387. package/dist/src/memory/store.js.map +1 -0
  388. package/dist/src/memory/textidentity.js +8 -0
  389. package/dist/src/memory/textidentity.js.map +1 -0
  390. package/dist/src/metrics.js +773 -0
  391. package/dist/src/metrics.js.map +1 -0
  392. package/dist/src/missingconfig.js +8 -0
  393. package/dist/src/missingconfig.js.map +1 -0
  394. package/dist/src/npmverbs.js +226 -0
  395. package/dist/src/npmverbs.js.map +1 -0
  396. package/dist/src/onboard/choices.js +191 -0
  397. package/dist/src/onboard/choices.js.map +1 -0
  398. package/dist/src/onboard/engine.js +344 -0
  399. package/dist/src/onboard/engine.js.map +1 -0
  400. package/dist/src/onboard/launch.js +83 -0
  401. package/dist/src/onboard/launch.js.map +1 -0
  402. package/dist/src/onboard/steps/appslug.js +211 -0
  403. package/dist/src/onboard/steps/appslug.js.map +1 -0
  404. package/dist/src/onboard/steps/bringup.js +408 -0
  405. package/dist/src/onboard/steps/bringup.js.map +1 -0
  406. package/dist/src/onboard/steps/compose.js +343 -0
  407. package/dist/src/onboard/steps/compose.js.map +1 -0
  408. package/dist/src/onboard/steps/configdelivery.js +50 -0
  409. package/dist/src/onboard/steps/configdelivery.js.map +1 -0
  410. package/dist/src/onboard/steps/configfill.js +1148 -0
  411. package/dist/src/onboard/steps/configfill.js.map +1 -0
  412. package/dist/src/onboard/steps/deploysh.js +80 -0
  413. package/dist/src/onboard/steps/deploysh.js.map +1 -0
  414. package/dist/src/onboard/steps/embeddings.js +472 -0
  415. package/dist/src/onboard/steps/embeddings.js.map +1 -0
  416. package/dist/src/onboard/steps/escalation.js +571 -0
  417. package/dist/src/onboard/steps/escalation.js.map +1 -0
  418. package/dist/src/onboard/steps/githubapp.js +1092 -0
  419. package/dist/src/onboard/steps/githubapp.js.map +1 -0
  420. package/dist/src/onboard/steps/golive.js +506 -0
  421. package/dist/src/onboard/steps/golive.js.map +1 -0
  422. package/dist/src/onboard/steps/index.js +67 -0
  423. package/dist/src/onboard/steps/index.js.map +1 -0
  424. package/dist/src/onboard/steps/ingress.js +762 -0
  425. package/dist/src/onboard/steps/ingress.js.map +1 -0
  426. package/dist/src/onboard/steps/inventory.js +420 -0
  427. package/dist/src/onboard/steps/inventory.js.map +1 -0
  428. package/dist/src/onboard/steps/llmauth.js +893 -0
  429. package/dist/src/onboard/steps/llmauth.js.map +1 -0
  430. package/dist/src/onboard/steps/memory.js +1060 -0
  431. package/dist/src/onboard/steps/memory.js.map +1 -0
  432. package/dist/src/onboard/steps/memorykeys.js +43 -0
  433. package/dist/src/onboard/steps/memorykeys.js.map +1 -0
  434. package/dist/src/onboard/steps/preflight.js +172 -0
  435. package/dist/src/onboard/steps/preflight.js.map +1 -0
  436. package/dist/src/onboard/steps/proof.js +502 -0
  437. package/dist/src/onboard/steps/proof.js.map +1 -0
  438. package/dist/src/onboard/steps/ssh.js +300 -0
  439. package/dist/src/onboard/steps/ssh.js.map +1 -0
  440. package/dist/src/onboard/steps/topology.js +291 -0
  441. package/dist/src/onboard/steps/topology.js.map +1 -0
  442. package/dist/src/onboard/steps/transport.js +511 -0
  443. package/dist/src/onboard/steps/transport.js.map +1 -0
  444. package/dist/src/onboard/steps/verifyup.js +362 -0
  445. package/dist/src/onboard/steps/verifyup.js.map +1 -0
  446. package/dist/src/onboard/steps/vmdeploy.js +368 -0
  447. package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
  448. package/dist/src/onboard/toml.js +53 -0
  449. package/dist/src/onboard/toml.js.map +1 -0
  450. package/dist/src/opencli.js +508 -0
  451. package/dist/src/opencli.js.map +1 -0
  452. package/dist/src/openpr.js +457 -0
  453. package/dist/src/openpr.js.map +1 -0
  454. package/dist/src/openviking/client.js +821 -0
  455. package/dist/src/openviking/client.js.map +1 -0
  456. package/dist/src/openviking/context.js +218 -0
  457. package/dist/src/openviking/context.js.map +1 -0
  458. package/dist/src/openviking/embedder.js +457 -0
  459. package/dist/src/openviking/embedder.js.map +1 -0
  460. package/dist/src/openviking/gapsink.js +38 -0
  461. package/dist/src/openviking/gapsink.js.map +1 -0
  462. package/dist/src/openviking/health.js +69 -0
  463. package/dist/src/openviking/health.js.map +1 -0
  464. package/dist/src/openviking/index.js +24 -0
  465. package/dist/src/openviking/index.js.map +1 -0
  466. package/dist/src/openviking/lessonfind.js +54 -0
  467. package/dist/src/openviking/lessonfind.js.map +1 -0
  468. package/dist/src/openviking/probe.js +89 -0
  469. package/dist/src/openviking/probe.js.map +1 -0
  470. package/dist/src/openviking/proc.js +95 -0
  471. package/dist/src/openviking/proc.js.map +1 -0
  472. package/dist/src/openviking/provisionreport.js +144 -0
  473. package/dist/src/openviking/provisionreport.js.map +1 -0
  474. package/dist/src/openviking/reembed.js +317 -0
  475. package/dist/src/openviking/reembed.js.map +1 -0
  476. package/dist/src/openviking/relayout.js +153 -0
  477. package/dist/src/openviking/relayout.js.map +1 -0
  478. package/dist/src/openviking/sidecar-main.js +39 -0
  479. package/dist/src/openviking/sidecar-main.js.map +1 -0
  480. package/dist/src/openviking/sidecar.js +79 -0
  481. package/dist/src/openviking/sidecar.js.map +1 -0
  482. package/dist/src/openviking/store.js +1758 -0
  483. package/dist/src/openviking/store.js.map +1 -0
  484. package/dist/src/openviking/supervisor.js +422 -0
  485. package/dist/src/openviking/supervisor.js.map +1 -0
  486. package/dist/src/openviking/types.js +5 -0
  487. package/dist/src/openviking/types.js.map +1 -0
  488. package/dist/src/operator-recovery.js +172 -0
  489. package/dist/src/operator-recovery.js.map +1 -0
  490. package/dist/src/operatormessage.js +44 -0
  491. package/dist/src/operatormessage.js.map +1 -0
  492. package/dist/src/outcomes.js +1361 -0
  493. package/dist/src/outcomes.js.map +1 -0
  494. package/dist/src/piauthcopy.js +5 -0
  495. package/dist/src/piauthcopy.js.map +1 -0
  496. package/dist/src/picli.js +45 -0
  497. package/dist/src/picli.js.map +1 -0
  498. package/dist/src/picwd.js +25 -0
  499. package/dist/src/picwd.js.map +1 -0
  500. package/dist/src/pilogin.js +35 -0
  501. package/dist/src/pilogin.js.map +1 -0
  502. package/dist/src/pingpong.js +1028 -0
  503. package/dist/src/pingpong.js.map +1 -0
  504. package/dist/src/posting-journal.js +441 -0
  505. package/dist/src/posting-journal.js.map +1 -0
  506. package/dist/src/posting-outbox.js +662 -0
  507. package/dist/src/posting-outbox.js.map +1 -0
  508. package/dist/src/posting-receipt.js +203 -0
  509. package/dist/src/posting-receipt.js.map +1 -0
  510. package/dist/src/posting.js +348 -0
  511. package/dist/src/posting.js.map +1 -0
  512. package/dist/src/postops.js +637 -0
  513. package/dist/src/postops.js.map +1 -0
  514. package/dist/src/pr-activity.js +11 -0
  515. package/dist/src/pr-activity.js.map +1 -0
  516. package/dist/src/pr-author.js +5 -0
  517. package/dist/src/pr-author.js.map +1 -0
  518. package/dist/src/pr-conversation.js +355 -0
  519. package/dist/src/pr-conversation.js.map +1 -0
  520. package/dist/src/pr-session-lifecycle.js +542 -0
  521. package/dist/src/pr-session-lifecycle.js.map +1 -0
  522. package/dist/src/preflight.js +341 -0
  523. package/dist/src/preflight.js.map +1 -0
  524. package/dist/src/priorresolve.js +275 -0
  525. package/dist/src/priorresolve.js.map +1 -0
  526. package/dist/src/qcrbin.js +180 -0
  527. package/dist/src/qcrbin.js.map +1 -0
  528. package/dist/src/recheck.js +1398 -0
  529. package/dist/src/recheck.js.map +1 -0
  530. package/dist/src/reconcile.js +820 -0
  531. package/dist/src/reconcile.js.map +1 -0
  532. package/dist/src/recordedfindings.js +15 -0
  533. package/dist/src/recordedfindings.js.map +1 -0
  534. package/dist/src/regression.js +546 -0
  535. package/dist/src/regression.js.map +1 -0
  536. package/dist/src/replay.js +598 -0
  537. package/dist/src/replay.js.map +1 -0
  538. package/dist/src/requiredpasses.js +113 -0
  539. package/dist/src/requiredpasses.js.map +1 -0
  540. package/dist/src/resumecheck.js +168 -0
  541. package/dist/src/resumecheck.js.map +1 -0
  542. package/dist/src/reviewcli.js +671 -0
  543. package/dist/src/reviewcli.js.map +1 -0
  544. package/dist/src/reviewcontext.js +22 -0
  545. package/dist/src/reviewcontext.js.map +1 -0
  546. package/dist/src/reviewdetach.js +574 -0
  547. package/dist/src/reviewdetach.js.map +1 -0
  548. package/dist/src/reviewhistory.js +284 -0
  549. package/dist/src/reviewhistory.js.map +1 -0
  550. package/dist/src/reviewhome.js +26 -0
  551. package/dist/src/reviewhome.js.map +1 -0
  552. package/dist/src/reviewmemory.js +110 -0
  553. package/dist/src/reviewmemory.js.map +1 -0
  554. package/dist/src/reviewrendercli.js +104 -0
  555. package/dist/src/reviewrendercli.js.map +1 -0
  556. package/dist/src/round-work.js +20 -0
  557. package/dist/src/round-work.js.map +1 -0
  558. package/dist/src/runhost.js +72 -0
  559. package/dist/src/runhost.js.map +1 -0
  560. package/dist/src/scanauth.js +25 -0
  561. package/dist/src/scanauth.js.map +1 -0
  562. package/dist/src/scanbox.js +846 -0
  563. package/dist/src/scanbox.js.map +1 -0
  564. package/dist/src/scancli.js +1074 -0
  565. package/dist/src/scancli.js.map +1 -0
  566. package/dist/src/scandriverlog.js +30 -0
  567. package/dist/src/scandriverlog.js.map +1 -0
  568. package/dist/src/session-bridge.js +75 -0
  569. package/dist/src/session-bridge.js.map +1 -0
  570. package/dist/src/session-command-payload.js +29 -0
  571. package/dist/src/session-command-payload.js.map +1 -0
  572. package/dist/src/session-command-supervisor.js +939 -0
  573. package/dist/src/session-command-supervisor.js.map +1 -0
  574. package/dist/src/sessionjsonl.js +85 -0
  575. package/dist/src/sessionjsonl.js.map +1 -0
  576. package/dist/src/sessions.js +541 -0
  577. package/dist/src/sessions.js.map +1 -0
  578. package/dist/src/setup/adapters.js +330 -0
  579. package/dist/src/setup/adapters.js.map +1 -0
  580. package/dist/src/setup/baselines.js +64 -0
  581. package/dist/src/setup/baselines.js.map +1 -0
  582. package/dist/src/setup/caserunner.js +554 -0
  583. package/dist/src/setup/caserunner.js.map +1 -0
  584. package/dist/src/setup/configcommit.js +358 -0
  585. package/dist/src/setup/configcommit.js.map +1 -0
  586. package/dist/src/setup/engine.js +672 -0
  587. package/dist/src/setup/engine.js.map +1 -0
  588. package/dist/src/setup/facts.js +593 -0
  589. package/dist/src/setup/facts.js.map +1 -0
  590. package/dist/src/setup/findings.js +948 -0
  591. package/dist/src/setup/findings.js.map +1 -0
  592. package/dist/src/setup/history.js +338 -0
  593. package/dist/src/setup/history.js.map +1 -0
  594. package/dist/src/setup/interview.js +185 -0
  595. package/dist/src/setup/interview.js.map +1 -0
  596. package/dist/src/setup/ledger.js +207 -0
  597. package/dist/src/setup/ledger.js.map +1 -0
  598. package/dist/src/setup/pins.js +43 -0
  599. package/dist/src/setup/pins.js.map +1 -0
  600. package/dist/src/setup/portrait.js +432 -0
  601. package/dist/src/setup/portrait.js.map +1 -0
  602. package/dist/src/setup/presentation.js +9 -0
  603. package/dist/src/setup/presentation.js.map +1 -0
  604. package/dist/src/setup/profile.js +417 -0
  605. package/dist/src/setup/profile.js.map +1 -0
  606. package/dist/src/setup/replay.js +240 -0
  607. package/dist/src/setup/replay.js.map +1 -0
  608. package/dist/src/setup/replaybuild.js +311 -0
  609. package/dist/src/setup/replaybuild.js.map +1 -0
  610. package/dist/src/setup/replayrun.js +806 -0
  611. package/dist/src/setup/replayrun.js.map +1 -0
  612. package/dist/src/setup/replaysource.js +169 -0
  613. package/dist/src/setup/replaysource.js.map +1 -0
  614. package/dist/src/setup/retune.js +333 -0
  615. package/dist/src/setup/retune.js.map +1 -0
  616. package/dist/src/setup/retunelog.js +51 -0
  617. package/dist/src/setup/retunelog.js.map +1 -0
  618. package/dist/src/setup/scan.js +725 -0
  619. package/dist/src/setup/scan.js.map +1 -0
  620. package/dist/src/setup/scanbuild.js +133 -0
  621. package/dist/src/setup/scanbuild.js.map +1 -0
  622. package/dist/src/setup/scanfacts.js +96 -0
  623. package/dist/src/setup/scanfacts.js.map +1 -0
  624. package/dist/src/setup/scanlease.js +251 -0
  625. package/dist/src/setup/scanlease.js.map +1 -0
  626. package/dist/src/setup/scanreport.js +289 -0
  627. package/dist/src/setup/scanreport.js.map +1 -0
  628. package/dist/src/setup/scanrun.js +1733 -0
  629. package/dist/src/setup/scanrun.js.map +1 -0
  630. package/dist/src/setup/scantree.js +257 -0
  631. package/dist/src/setup/scantree.js.map +1 -0
  632. package/dist/src/setup/stage2.js +617 -0
  633. package/dist/src/setup/stage2.js.map +1 -0
  634. package/dist/src/setup/stage2request.js +259 -0
  635. package/dist/src/setup/stage2request.js.map +1 -0
  636. package/dist/src/setup/survey.js +307 -0
  637. package/dist/src/setup/survey.js.map +1 -0
  638. package/dist/src/setup/surveyrun.js +549 -0
  639. package/dist/src/setup/surveyrun.js.map +1 -0
  640. package/dist/src/setup/textsniff.js +89 -0
  641. package/dist/src/setup/textsniff.js.map +1 -0
  642. package/dist/src/setup/trackersink.js +395 -0
  643. package/dist/src/setup/trackersink.js.map +1 -0
  644. package/dist/src/setup/triage.js +636 -0
  645. package/dist/src/setup/triage.js.map +1 -0
  646. package/dist/src/setup/tuning.js +585 -0
  647. package/dist/src/setup/tuning.js.map +1 -0
  648. package/dist/src/setup/unitcut.js +437 -0
  649. package/dist/src/setup/unitcut.js.map +1 -0
  650. package/dist/src/singleflight.js +72 -0
  651. package/dist/src/singleflight.js.map +1 -0
  652. package/dist/src/slack/announce.js +725 -0
  653. package/dist/src/slack/announce.js.map +1 -0
  654. package/dist/src/slack/api.js +227 -0
  655. package/dist/src/slack/api.js.map +1 -0
  656. package/dist/src/slack/messages.js +158 -0
  657. package/dist/src/slack/messages.js.map +1 -0
  658. package/dist/src/slack/origin.js +550 -0
  659. package/dist/src/slack/origin.js.map +1 -0
  660. package/dist/src/slack/policy.js +225 -0
  661. package/dist/src/slack/policy.js.map +1 -0
  662. package/dist/src/slack/reply.js +153 -0
  663. package/dist/src/slack/reply.js.map +1 -0
  664. package/dist/src/slack/router.js +189 -0
  665. package/dist/src/slack/router.js.map +1 -0
  666. package/dist/src/slack/socket.js +323 -0
  667. package/dist/src/slack/socket.js.map +1 -0
  668. package/dist/src/slack/types.js +11 -0
  669. package/dist/src/slack/types.js.map +1 -0
  670. package/dist/src/sqlitehint.js +30 -0
  671. package/dist/src/sqlitehint.js.map +1 -0
  672. package/dist/src/ssh-observation.js +25 -0
  673. package/dist/src/ssh-observation.js.map +1 -0
  674. package/dist/src/state.js +5869 -0
  675. package/dist/src/state.js.map +1 -0
  676. package/dist/src/statemachine.js +220 -0
  677. package/dist/src/statemachine.js.map +1 -0
  678. package/dist/src/stubs.js +52 -0
  679. package/dist/src/stubs.js.map +1 -0
  680. package/dist/src/takeover.js +506 -0
  681. package/dist/src/takeover.js.map +1 -0
  682. package/dist/src/takeoverpi.js +249 -0
  683. package/dist/src/takeoverpi.js.map +1 -0
  684. package/dist/src/terminal-failure.js +26 -0
  685. package/dist/src/terminal-failure.js.map +1 -0
  686. package/dist/src/thread-resolution-refusal.js +4 -0
  687. package/dist/src/thread-resolution-refusal.js.map +1 -0
  688. package/dist/src/tmux.js +126 -0
  689. package/dist/src/tmux.js.map +1 -0
  690. package/dist/src/trackerid.js +43 -0
  691. package/dist/src/trackerid.js.map +1 -0
  692. package/dist/src/transcriptindex.js +512 -0
  693. package/dist/src/transcriptindex.js.map +1 -0
  694. package/dist/src/tui/actions.js +404 -0
  695. package/dist/src/tui/actions.js.map +1 -0
  696. package/dist/src/tui/alerts.js +523 -0
  697. package/dist/src/tui/alerts.js.map +1 -0
  698. package/dist/src/tui/app.js +1835 -0
  699. package/dist/src/tui/app.js.map +1 -0
  700. package/dist/src/tui/archiveops.js +164 -0
  701. package/dist/src/tui/archiveops.js.map +1 -0
  702. package/dist/src/tui/autofile.js +191 -0
  703. package/dist/src/tui/autofile.js.map +1 -0
  704. package/dist/src/tui/board.js +1143 -0
  705. package/dist/src/tui/board.js.map +1 -0
  706. package/dist/src/tui/candidates.js +699 -0
  707. package/dist/src/tui/candidates.js.map +1 -0
  708. package/dist/src/tui/cellwidth.js +151 -0
  709. package/dist/src/tui/cellwidth.js.map +1 -0
  710. package/dist/src/tui/changelog-store.js +404 -0
  711. package/dist/src/tui/changelog-store.js.map +1 -0
  712. package/dist/src/tui/chord.js +26 -0
  713. package/dist/src/tui/chord.js.map +1 -0
  714. package/dist/src/tui/clipboard.js +81 -0
  715. package/dist/src/tui/clipboard.js.map +1 -0
  716. package/dist/src/tui/cmdbar.js +253 -0
  717. package/dist/src/tui/cmdbar.js.map +1 -0
  718. package/dist/src/tui/companion.js +1275 -0
  719. package/dist/src/tui/companion.js.map +1 -0
  720. package/dist/src/tui/companionplan.js +200 -0
  721. package/dist/src/tui/companionplan.js.map +1 -0
  722. package/dist/src/tui/companiontab.js +61 -0
  723. package/dist/src/tui/companiontab.js.map +1 -0
  724. package/dist/src/tui/confighome.js +105 -0
  725. package/dist/src/tui/confighome.js.map +1 -0
  726. package/dist/src/tui/connect.js +44 -0
  727. package/dist/src/tui/connect.js.map +1 -0
  728. package/dist/src/tui/deploy-ceremony.js +75 -0
  729. package/dist/src/tui/deploy-ceremony.js.map +1 -0
  730. package/dist/src/tui/deploy-drain.js +93 -0
  731. package/dist/src/tui/deploy-drain.js.map +1 -0
  732. package/dist/src/tui/deploy-process.js +234 -0
  733. package/dist/src/tui/deploy-process.js.map +1 -0
  734. package/dist/src/tui/deploytab.js +1263 -0
  735. package/dist/src/tui/deploytab.js.map +1 -0
  736. package/dist/src/tui/design.js +111 -0
  737. package/dist/src/tui/design.js.map +1 -0
  738. package/dist/src/tui/digesttab.js +148 -0
  739. package/dist/src/tui/digesttab.js.map +1 -0
  740. package/dist/src/tui/emptystate.js +32 -0
  741. package/dist/src/tui/emptystate.js.map +1 -0
  742. package/dist/src/tui/evidence.js +790 -0
  743. package/dist/src/tui/evidence.js.map +1 -0
  744. package/dist/src/tui/feed.js +415 -0
  745. package/dist/src/tui/feed.js.map +1 -0
  746. package/dist/src/tui/feedbacktab.js +907 -0
  747. package/dist/src/tui/feedbacktab.js.map +1 -0
  748. package/dist/src/tui/filters.js +72 -0
  749. package/dist/src/tui/filters.js.map +1 -0
  750. package/dist/src/tui/ghread.js +272 -0
  751. package/dist/src/tui/ghread.js.map +1 -0
  752. package/dist/src/tui/historypull.js +61 -0
  753. package/dist/src/tui/historypull.js.map +1 -0
  754. package/dist/src/tui/inboxpull.js +197 -0
  755. package/dist/src/tui/inboxpull.js.map +1 -0
  756. package/dist/src/tui/index.js +1425 -0
  757. package/dist/src/tui/index.js.map +1 -0
  758. package/dist/src/tui/issuesink.js +206 -0
  759. package/dist/src/tui/issuesink.js.map +1 -0
  760. package/dist/src/tui/jsonfile.js +32 -0
  761. package/dist/src/tui/jsonfile.js.map +1 -0
  762. package/dist/src/tui/keys.js +97 -0
  763. package/dist/src/tui/keys.js.map +1 -0
  764. package/dist/src/tui/layout.js +70 -0
  765. package/dist/src/tui/layout.js.map +1 -0
  766. package/dist/src/tui/learning.js +469 -0
  767. package/dist/src/tui/learning.js.map +1 -0
  768. package/dist/src/tui/lessonswitch.js +103 -0
  769. package/dist/src/tui/lessonswitch.js.map +1 -0
  770. package/dist/src/tui/livecontrol.js +167 -0
  771. package/dist/src/tui/livecontrol.js.map +1 -0
  772. package/dist/src/tui/memforward.js +310 -0
  773. package/dist/src/tui/memforward.js.map +1 -0
  774. package/dist/src/tui/memincident.js +287 -0
  775. package/dist/src/tui/memincident.js.map +1 -0
  776. package/dist/src/tui/memory.js +485 -0
  777. package/dist/src/tui/memory.js.map +1 -0
  778. package/dist/src/tui/memtab.js +513 -0
  779. package/dist/src/tui/memtab.js.map +1 -0
  780. package/dist/src/tui/metricstab.js +979 -0
  781. package/dist/src/tui/metricstab.js.map +1 -0
  782. package/dist/src/tui/model.js +104 -0
  783. package/dist/src/tui/model.js.map +1 -0
  784. package/dist/src/tui/mouse.js +83 -0
  785. package/dist/src/tui/mouse.js.map +1 -0
  786. package/dist/src/tui/msg.js +196 -0
  787. package/dist/src/tui/msg.js.map +1 -0
  788. package/dist/src/tui/nativecompanion.js +321 -0
  789. package/dist/src/tui/nativecompanion.js.map +1 -0
  790. package/dist/src/tui/openurl.js +17 -0
  791. package/dist/src/tui/openurl.js.map +1 -0
  792. package/dist/src/tui/paint.js +501 -0
  793. package/dist/src/tui/paint.js.map +1 -0
  794. package/dist/src/tui/panes.js +74 -0
  795. package/dist/src/tui/panes.js.map +1 -0
  796. package/dist/src/tui/pipane.js +166 -0
  797. package/dist/src/tui/pipane.js.map +1 -0
  798. package/dist/src/tui/poll-hooks.js +11 -0
  799. package/dist/src/tui/poll-hooks.js.map +1 -0
  800. package/dist/src/tui/poll.js +113 -0
  801. package/dist/src/tui/poll.js.map +1 -0
  802. package/dist/src/tui/proposals.js +715 -0
  803. package/dist/src/tui/proposals.js.map +1 -0
  804. package/dist/src/tui/prpause.js +132 -0
  805. package/dist/src/tui/prpause.js.map +1 -0
  806. package/dist/src/tui/reload.js +351 -0
  807. package/dist/src/tui/reload.js.map +1 -0
  808. package/dist/src/tui/remote.js +312 -0
  809. package/dist/src/tui/remote.js.map +1 -0
  810. package/dist/src/tui/rollover.js +767 -0
  811. package/dist/src/tui/rollover.js.map +1 -0
  812. package/dist/src/tui/scantab.js +1053 -0
  813. package/dist/src/tui/scantab.js.map +1 -0
  814. package/dist/src/tui/setupentry.js +81 -0
  815. package/dist/src/tui/setupentry.js.map +1 -0
  816. package/dist/src/tui/sidebar.js +602 -0
  817. package/dist/src/tui/sidebar.js.map +1 -0
  818. package/dist/src/tui/sshtriage.js +393 -0
  819. package/dist/src/tui/sshtriage.js.map +1 -0
  820. package/dist/src/tui/startup.js +172 -0
  821. package/dist/src/tui/startup.js.map +1 -0
  822. package/dist/src/tui/tabs.js +41 -0
  823. package/dist/src/tui/tabs.js.map +1 -0
  824. package/dist/src/tui/termmirror.js +217 -0
  825. package/dist/src/tui/termmirror.js.map +1 -0
  826. package/dist/src/tui/theme.js +95 -0
  827. package/dist/src/tui/theme.js.map +1 -0
  828. package/dist/src/tui/ticketstab.js +224 -0
  829. package/dist/src/tui/ticketstab.js.map +1 -0
  830. package/dist/src/tui/tmuxsocket.js +101 -0
  831. package/dist/src/tui/tmuxsocket.js.map +1 -0
  832. package/dist/src/tui/trackertickets.js +77 -0
  833. package/dist/src/tui/trackertickets.js.map +1 -0
  834. package/dist/src/tui/unwedge.js +245 -0
  835. package/dist/src/tui/unwedge.js.map +1 -0
  836. package/dist/src/tui/viewer.js +479 -0
  837. package/dist/src/tui/viewer.js.map +1 -0
  838. package/dist/src/tui/viewerinput.js +125 -0
  839. package/dist/src/tui/viewerinput.js.map +1 -0
  840. package/dist/src/updatenotice.js +143 -0
  841. package/dist/src/updatenotice.js.map +1 -0
  842. package/dist/src/usage.js +1007 -0
  843. package/dist/src/usage.js.map +1 -0
  844. package/dist/src/verdicts.js +3848 -0
  845. package/dist/src/verdicts.js.map +1 -0
  846. package/dist/src/version.js +58 -0
  847. package/dist/src/version.js.map +1 -0
  848. package/dist/src/wakes.js +411 -0
  849. package/dist/src/wakes.js.map +1 -0
  850. package/dist/src/webhook.js +725 -0
  851. package/dist/src/webhook.js.map +1 -0
  852. package/dist/src/webhookheal.js +238 -0
  853. package/dist/src/webhookheal.js.map +1 -0
  854. package/dist/src/wire.js +1801 -0
  855. package/dist/src/wire.js.map +1 -0
  856. package/dist/src/worker-session-host.js +531 -0
  857. package/dist/src/worker-session-host.js.map +1 -0
  858. package/dist/src/workerbreaker.js +112 -0
  859. package/dist/src/workerbreaker.js.map +1 -0
  860. package/dist/src/workercontrol.js +49 -0
  861. package/dist/src/workercontrol.js.map +1 -0
  862. package/dist/src/workerlease.js +351 -0
  863. package/dist/src/workerlease.js.map +1 -0
  864. package/dist/src/workerliveness.js +84 -0
  865. package/dist/src/workerliveness.js.map +1 -0
  866. package/dist/src/workerlog.js +65 -0
  867. package/dist/src/workerlog.js.map +1 -0
  868. package/dist/src/workerruntime.js +10 -0
  869. package/dist/src/workerruntime.js.map +1 -0
  870. package/dist/src/workersupervisor.js +1206 -0
  871. package/dist/src/workersupervisor.js.map +1 -0
  872. package/dist/src/workerusage.js +23 -0
  873. package/dist/src/workerusage.js.map +1 -0
  874. package/dist/src/workerworld.js +332 -0
  875. package/dist/src/workerworld.js.map +1 -0
  876. package/dist/src/worktrees.js +984 -0
  877. package/dist/src/worktrees.js.map +1 -0
  878. package/docker/Dockerfile +284 -0
  879. package/docker/Dockerfile.dockerignore +35 -0
  880. package/docker/README.md +221 -0
  881. package/docker/companion/Dockerfile +139 -0
  882. package/docker/companion/agents/evidence.md +34 -0
  883. package/docker/companion/agents/explore.md +26 -0
  884. package/docker/companion/agents/pass-survey.md +49 -0
  885. package/docker/companion/agents/study.md +26 -0
  886. package/docker/companion/entrypoint.sh +343 -0
  887. package/docker/companion/memory-health.mjs +269 -0
  888. package/docker/entrypoint.sh +782 -0
  889. package/docker/memory/Dockerfile +191 -0
  890. package/docker/memory/Dockerfile.dockerignore +20 -0
  891. package/docker/memory/README.md +60 -0
  892. package/docker/memory/entrypoint.sh +163 -0
  893. package/docker/setid/qcr-setid.c +109 -0
  894. package/docker/tunnel/Dockerfile.cloudflared +51 -0
  895. package/docker/tunnel/Dockerfile.ngrok +40 -0
  896. package/docker/tunnel/sidecar.mjs +411 -0
  897. package/docker/vm/tmux.conf +26 -0
  898. package/docker-compose.yml +348 -0
  899. package/extensions/cli/bash-guard-ambient.ts +71 -0
  900. package/extensions/cli/bash-guard.ts +12 -0
  901. package/extensions/cli/companion-guard.ts +8 -0
  902. package/extensions/cli/debug-round.ts +9 -0
  903. package/extensions/cli/inject-bridge.ts +8 -0
  904. package/extensions/cli/ledger-pin.ts +13 -0
  905. package/extensions/cli/lens.ts +22 -0
  906. package/extensions/cli/muted-theme.ts +9 -0
  907. package/extensions/cli/onboarding.ts +8 -0
  908. package/extensions/cli/setup.ts +8 -0
  909. package/extensions/cli/tui-view.ts +8 -0
  910. package/extensions/companion-guard.ts +411 -0
  911. package/extensions/debug-round.ts +281 -0
  912. package/extensions/inject-bridge.ts +630 -0
  913. package/extensions/inline-bash.ts +102 -0
  914. package/extensions/muted-theme.json +68 -0
  915. package/extensions/muted-theme.ts +23 -0
  916. package/extensions/onboarding.ts +233 -0
  917. package/extensions/setup-replay.ts +336 -0
  918. package/extensions/setup-scan.ts +1236 -0
  919. package/extensions/setup-triage.ts +190 -0
  920. package/extensions/setup.ts +1428 -0
  921. package/extensions/tui-view.ts +489 -0
  922. package/extensions/vendor/openviking-pi/LICENSE +201 -0
  923. package/extensions/vendor/openviking-pi/NOTICE +68 -0
  924. package/extensions/vendor/openviking-pi/README.md +550 -0
  925. package/extensions/vendor/openviking-pi/client.ts +588 -0
  926. package/extensions/vendor/openviking-pi/config.json +28 -0
  927. package/extensions/vendor/openviking-pi/config.ts +271 -0
  928. package/extensions/vendor/openviking-pi/index.ts +523 -0
  929. package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
  930. package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
  931. package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
  932. package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
  933. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
  934. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
  935. package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
  936. package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
  937. package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
  938. package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
  939. package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
  940. package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
  941. package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
  942. package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
  943. package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
  944. package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
  945. package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
  946. package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
  947. package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
  948. package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
  949. package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
  950. package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
  951. package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
  952. package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
  953. package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
  954. package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
  955. package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
  956. package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
  957. package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
  958. package/extensions/vendor/openviking-pi/recall.ts +149 -0
  959. package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
  960. package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
  961. package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
  962. package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
  963. package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
  964. package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
  965. package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
  966. package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
  967. package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
  968. package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
  969. package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
  970. package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
  971. package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
  972. package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
  973. package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
  974. package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
  975. package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  976. package/extensions/vendor/openviking-pi/sync.ts +163 -0
  977. package/extensions/vendor/openviking-pi/takeover.ts +36 -0
  978. package/extensions/vendor/openviking-pi/tools.ts +479 -0
  979. package/extensions/vendor/openviking-pi/upstream.json +48 -0
  980. package/package.json +138 -4
  981. package/profiles/default/README.md +21 -0
  982. package/profiles/default/addenda/.gitkeep +0 -0
  983. package/profiles/default/profile.toml +17 -0
  984. package/profiles/example-django/README.md +21 -0
  985. package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  986. package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  987. package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  988. package/profiles/example-django/addenda/40-orm-usage.md +19 -0
  989. package/profiles/example-django/addenda/50-declared-shape.md +10 -0
  990. package/profiles/example-django/addenda/60-config-placement.md +14 -0
  991. package/profiles/example-django/heuristics.toml +24 -0
  992. package/profiles/example-django/profile.toml +9 -0
  993. package/profiles/fast/README.md +24 -0
  994. package/profiles/fast/profile.toml +59 -0
  995. package/profiles/minimal/README.md +17 -0
  996. package/profiles/minimal/addenda/.gitkeep +0 -0
  997. package/profiles/minimal/profile.toml +8 -0
  998. package/profiles/snappy/README.md +24 -0
  999. package/profiles/snappy/addenda/10-race-as-question.md +6 -0
  1000. package/profiles/snappy/profile.toml +46 -0
  1001. package/prompts/companion-day-open.md +46 -0
  1002. package/prompts/companion-first-run.md +104 -0
  1003. package/prompts/companion.md +821 -0
  1004. package/prompts/conversation-system.md +117 -0
  1005. package/prompts/eod-sweep.md +120 -0
  1006. package/prompts/onboarding.md +74 -0
  1007. package/prompts/pass-common.md +52 -0
  1008. package/prompts/pass-quality-common.md +22 -0
  1009. package/prompts/round-first.md +874 -0
  1010. package/prompts/round-follow-up.md +991 -0
  1011. package/prompts/round-re-review.md +889 -0
  1012. package/prompts/round-recheck.md +196 -0
  1013. package/prompts/round-staleness.md +856 -0
  1014. package/schemas/review-result.v1.json +391 -0
  1015. package/schemas/scan-result.v1.json +421 -0
  1016. package/schemas/version.v1.json +57 -0
  1017. package/scripts/approve-pr.sh +16 -0
  1018. package/scripts/build-stamp.mjs +67 -0
  1019. package/scripts/commit-verdict.sh +155 -0
  1020. package/scripts/companion-e2e.sh +250 -0
  1021. package/scripts/companion-memory-forward.mjs +1966 -0
  1022. package/scripts/config-fingerprint.sh +28 -0
  1023. package/scripts/deploy-manifest.mjs +141 -0
  1024. package/scripts/deploy.sh +1435 -0
  1025. package/scripts/directory-lock.mjs +193 -0
  1026. package/scripts/heartbeat-fresh.mjs +54 -0
  1027. package/scripts/install-locator.sh +328 -0
  1028. package/scripts/install-wrapper.sh +76 -0
  1029. package/scripts/memory-search-check.mjs +98 -0
  1030. package/scripts/mint-token.sh +212 -0
  1031. package/scripts/openviking-embedder.py +392 -0
  1032. package/scripts/openviking-ov-conf.mjs +161 -0
  1033. package/scripts/openviking-provision.mjs +1122 -0
  1034. package/scripts/openviking-reembed.mjs +1154 -0
  1035. package/scripts/post-review.sh +16 -0
  1036. package/scripts/pr-checks.sh +256 -0
  1037. package/scripts/pr-context.py +174 -0
  1038. package/scripts/pr-context.sh +63 -0
  1039. package/scripts/pr-self.sh +144 -0
  1040. package/scripts/pr-threads.sh +145 -0
  1041. package/scripts/qcr +15 -0
  1042. package/scripts/qcr-host.sh +769 -0
  1043. package/scripts/render-review.sh +24 -0
  1044. package/scripts/reply-thread.sh +16 -0
  1045. package/scripts/required-passes.py +112 -0
  1046. package/scripts/smoke-companion.sh +211 -0
  1047. package/scripts/tui-deploy-runner.mjs +61 -0
  1048. package/scripts/verify-dist.mjs +201 -0
  1049. package/scripts/vm-ghcr-login.sh +188 -0
  1050. package/seeds/agents/pass-baseline.md +61 -0
  1051. package/seeds/agents/pass-contracts.md +30 -0
  1052. package/seeds/agents/pass-data-safety.md +54 -0
  1053. package/seeds/agents/pass-delivery.md +37 -0
  1054. package/seeds/agents/pass-prod-risk.md +24 -0
  1055. package/seeds/agents/pass-security.md +31 -0
  1056. package/seeds/agents/pass-statelessness.md +27 -0
  1057. package/seeds/agents/pass-ui.md +23 -0
  1058. package/seeds/agents/pass-validate.md +50 -0
  1059. package/seeds/prompts/conversation-system.md +117 -0
  1060. package/seeds/prompts/eod-sweep.md +120 -0
  1061. package/seeds/prompts/pass-common.md +52 -0
  1062. package/skills/companion/adjudicate/SKILL.md +247 -0
  1063. package/skills/companion/evidence/SKILL.md +150 -0
  1064. package/skills/companion/land/SKILL.md +200 -0
  1065. package/skills/companion/replay/SKILL.md +138 -0
  1066. package/skills/companion/setup/SKILL.md +167 -0
  1067. package/skills/companion/survey/SKILL.md +160 -0
  1068. package/skills/selfimprove/README.md +34 -0
  1069. package/skills/selfimprove/SKILL.md +127 -0
  1070. package/skills/selfimprove/agents/openai.yaml +2 -0
  1071. package/skills/selfimprove/commands/selfimprove.md +6 -0
  1072. package/skills/selfimprove/references/fix-loop.md +49 -0
  1073. package/skills/selfimprove/references/ledger.md +20 -0
  1074. package/skills/selfimprove/references/setup.md +40 -0
  1075. package/src/anomalies.ts +566 -0
  1076. package/src/answerworker.ts +251 -0
  1077. package/src/appstamp.ts +65 -0
  1078. package/src/archive/admission.ts +3 -0
  1079. package/src/archive/archivedb.ts +548 -0
  1080. package/src/archive/archiver.ts +1963 -0
  1081. package/src/archive/archivewrite.ts +279 -0
  1082. package/src/archive/bundle.ts +333 -0
  1083. package/src/archive/dbmove.ts +911 -0
  1084. package/src/archive/duplicatecounts.ts +98 -0
  1085. package/src/archive/eligibility.ts +181 -0
  1086. package/src/archive/extract.ts +342 -0
  1087. package/src/archive/fsutil.ts +415 -0
  1088. package/src/archive/gzfile.ts +370 -0
  1089. package/src/archive/kinds.ts +21 -0
  1090. package/src/archive/livecheckpoint.ts +217 -0
  1091. package/src/archive/liveretention.ts +180 -0
  1092. package/src/archive/manifest.ts +519 -0
  1093. package/src/archive/overseer.ts +336 -0
  1094. package/src/archive/piglobal.ts +174 -0
  1095. package/src/archive/plan.ts +612 -0
  1096. package/src/archive/prarchive.ts +984 -0
  1097. package/src/archive/prfiles.ts +406 -0
  1098. package/src/archive/report.ts +252 -0
  1099. package/src/archive/restore.ts +330 -0
  1100. package/src/archive/rows.ts +187 -0
  1101. package/src/archive/seal.ts +630 -0
  1102. package/src/archive/status.ts +719 -0
  1103. package/src/archive/storepass.ts +581 -0
  1104. package/src/backfill.ts +362 -0
  1105. package/src/bench/cost.ts +113 -0
  1106. package/src/bench/exec.ts +90 -0
  1107. package/src/bench/values.ts +13 -0
  1108. package/src/botcontrol.ts +176 -0
  1109. package/src/botworker.ts +953 -0
  1110. package/src/capture.ts +321 -0
  1111. package/src/cases.ts +1789 -0
  1112. package/src/channelfs.ts +26 -0
  1113. package/src/checklist.ts +506 -0
  1114. package/src/ciretry.ts +141 -0
  1115. package/src/cli.ts +2271 -0
  1116. package/src/cliops.ts +384 -0
  1117. package/src/clonedir.ts +15 -0
  1118. package/src/commandeffects.ts +57 -0
  1119. package/src/commands.ts +1678 -0
  1120. package/src/companioninbox.ts +239 -0
  1121. package/src/compare.ts +957 -0
  1122. package/src/config.ts +2050 -0
  1123. package/src/configcheck.ts +133 -0
  1124. package/src/coreext.ts +47 -0
  1125. package/src/daemon.ts +1302 -0
  1126. package/src/debuground.ts +927 -0
  1127. package/src/debugroundpi.ts +262 -0
  1128. package/src/diagnostics.ts +14 -0
  1129. package/src/digest.ts +183 -0
  1130. package/src/dispatch.ts +2250 -0
  1131. package/src/engine/answerguard.ts +27 -0
  1132. package/src/engine/botsdk.ts +336 -0
  1133. package/src/engine/caserunner-sdk.ts +423 -0
  1134. package/src/engine/debugpi.ts +235 -0
  1135. package/src/eod.ts +669 -0
  1136. package/src/eodpinwatch.ts +556 -0
  1137. package/src/escalation.ts +1253 -0
  1138. package/src/eventloop.ts +1045 -0
  1139. package/src/execboundary.ts +633 -0
  1140. package/src/feedback.ts +1162 -0
  1141. package/src/findingscheck.ts +298 -0
  1142. package/src/fsdenied.ts +24 -0
  1143. package/src/ghread.ts +242 -0
  1144. package/src/github.ts +1497 -0
  1145. package/src/homes.ts +175 -0
  1146. package/src/hostroute.ts +53 -0
  1147. package/src/improvements.ts +787 -0
  1148. package/src/install.ts +1103 -0
  1149. package/src/installkind.ts +50 -0
  1150. package/src/ipc/capability.ts +333 -0
  1151. package/src/ipc/client.ts +269 -0
  1152. package/src/ipc/control.ts +90 -0
  1153. package/src/ipc/ghreadshim.ts +94 -0
  1154. package/src/ipc/launcher.ts +65 -0
  1155. package/src/ipc/permits.ts +362 -0
  1156. package/src/ipc/postshim.ts +164 -0
  1157. package/src/ipc/protocol.ts +342 -0
  1158. package/src/ipc/server.ts +371 -0
  1159. package/src/ipc/workerspec.ts +218 -0
  1160. package/src/learner/learner.ts +1655 -0
  1161. package/src/learner/miner.ts +1290 -0
  1162. package/src/learner/packet.ts +340 -0
  1163. package/src/learner/receipt.ts +168 -0
  1164. package/src/learner/status.ts +107 -0
  1165. package/src/ledgercheck.ts +157 -0
  1166. package/src/lessonledger.ts +703 -0
  1167. package/src/lessonselect.ts +888 -0
  1168. package/src/lessonshadow.ts +810 -0
  1169. package/src/levers.ts +393 -0
  1170. package/src/live/projection.ts +491 -0
  1171. package/src/live/publish.ts +92 -0
  1172. package/src/live/read.ts +86 -0
  1173. package/src/livecli.ts +336 -0
  1174. package/src/loginhint.ts +56 -0
  1175. package/src/loginrefresh/atomic.ts +76 -0
  1176. package/src/loginrefresh/lock.ts +104 -0
  1177. package/src/loginrefresh/main.ts +59 -0
  1178. package/src/loginrefresh/pirefresh.ts +57 -0
  1179. package/src/loginrefresh/refresher.ts +326 -0
  1180. package/src/loginrefresh/supervisor.ts +273 -0
  1181. package/src/loginrefresh/types.ts +35 -0
  1182. package/src/memory/context.ts +385 -0
  1183. package/src/memory/datafence.ts +9 -0
  1184. package/src/memory/delivery.ts +171 -0
  1185. package/src/memory/eventtags.ts +80 -0
  1186. package/src/memory/health.ts +953 -0
  1187. package/src/memory/import.ts +664 -0
  1188. package/src/memory/layout.ts +261 -0
  1189. package/src/memory/legacy-file.ts +104 -0
  1190. package/src/memory/lessons.ts +447 -0
  1191. package/src/memory/ovsession.ts +601 -0
  1192. package/src/memory/relayout.ts +335 -0
  1193. package/src/memory/roundactivity.ts +209 -0
  1194. package/src/memory/selection.ts +989 -0
  1195. package/src/memory/shadowstatus.ts +125 -0
  1196. package/src/memory/store.ts +275 -0
  1197. package/src/memory/textidentity.ts +8 -0
  1198. package/src/metrics.ts +1056 -0
  1199. package/src/missingconfig.ts +7 -0
  1200. package/src/npmverbs.ts +264 -0
  1201. package/src/onboard/choices.ts +222 -0
  1202. package/src/onboard/engine.ts +518 -0
  1203. package/src/onboard/launch.ts +115 -0
  1204. package/src/onboard/steps/appslug.ts +265 -0
  1205. package/src/onboard/steps/bringup.ts +482 -0
  1206. package/src/onboard/steps/compose.ts +459 -0
  1207. package/src/onboard/steps/configdelivery.ts +58 -0
  1208. package/src/onboard/steps/configfill.ts +1352 -0
  1209. package/src/onboard/steps/deploysh.ts +105 -0
  1210. package/src/onboard/steps/embeddings.ts +550 -0
  1211. package/src/onboard/steps/escalation.ts +640 -0
  1212. package/src/onboard/steps/githubapp.ts +1414 -0
  1213. package/src/onboard/steps/golive.ts +577 -0
  1214. package/src/onboard/steps/index.ts +71 -0
  1215. package/src/onboard/steps/ingress.ts +907 -0
  1216. package/src/onboard/steps/inventory.ts +526 -0
  1217. package/src/onboard/steps/llmauth.ts +1095 -0
  1218. package/src/onboard/steps/memory.ts +1290 -0
  1219. package/src/onboard/steps/memorykeys.ts +85 -0
  1220. package/src/onboard/steps/preflight.ts +191 -0
  1221. package/src/onboard/steps/proof.ts +624 -0
  1222. package/src/onboard/steps/ssh.ts +377 -0
  1223. package/src/onboard/steps/topology.ts +337 -0
  1224. package/src/onboard/steps/transport.ts +665 -0
  1225. package/src/onboard/steps/verifyup.ts +448 -0
  1226. package/src/onboard/steps/vmdeploy.ts +442 -0
  1227. package/src/onboard/toml.ts +50 -0
  1228. package/src/opencli.ts +591 -0
  1229. package/src/openpr.ts +568 -0
  1230. package/src/openviking/client.ts +1131 -0
  1231. package/src/openviking/context.ts +263 -0
  1232. package/src/openviking/embedder.ts +516 -0
  1233. package/src/openviking/gapsink.ts +63 -0
  1234. package/src/openviking/health.ts +83 -0
  1235. package/src/openviking/index.ts +119 -0
  1236. package/src/openviking/lessonfind.ts +92 -0
  1237. package/src/openviking/probe.ts +129 -0
  1238. package/src/openviking/proc.ts +110 -0
  1239. package/src/openviking/provisionreport.ts +160 -0
  1240. package/src/openviking/reembed.ts +412 -0
  1241. package/src/openviking/relayout.ts +177 -0
  1242. package/src/openviking/sidecar-main.ts +38 -0
  1243. package/src/openviking/sidecar.ts +106 -0
  1244. package/src/openviking/store.ts +2025 -0
  1245. package/src/openviking/supervisor.ts +474 -0
  1246. package/src/openviking/types.ts +174 -0
  1247. package/src/operator-recovery.ts +237 -0
  1248. package/src/operatormessage.ts +52 -0
  1249. package/src/outcomes.ts +1670 -0
  1250. package/src/piauthcopy.ts +12 -0
  1251. package/src/picli.ts +49 -0
  1252. package/src/picwd.ts +25 -0
  1253. package/src/pilogin.ts +37 -0
  1254. package/src/pingpong.ts +1339 -0
  1255. package/src/posting-journal.ts +549 -0
  1256. package/src/posting-outbox.ts +802 -0
  1257. package/src/posting-receipt.ts +324 -0
  1258. package/src/posting.ts +603 -0
  1259. package/src/postops.ts +832 -0
  1260. package/src/pr-activity.ts +20 -0
  1261. package/src/pr-author.ts +6 -0
  1262. package/src/pr-conversation.ts +432 -0
  1263. package/src/pr-session-lifecycle.ts +631 -0
  1264. package/src/preflight.ts +391 -0
  1265. package/src/priorresolve.ts +343 -0
  1266. package/src/qcrbin.ts +186 -0
  1267. package/src/recheck.ts +1775 -0
  1268. package/src/reconcile.ts +1030 -0
  1269. package/src/recordedfindings.ts +27 -0
  1270. package/src/regression.ts +833 -0
  1271. package/src/replay.ts +816 -0
  1272. package/src/requiredpasses.ts +148 -0
  1273. package/src/resumecheck.ts +246 -0
  1274. package/src/reviewcli.ts +787 -0
  1275. package/src/reviewcontext.ts +35 -0
  1276. package/src/reviewdetach.ts +734 -0
  1277. package/src/reviewhistory.ts +427 -0
  1278. package/src/reviewhome.ts +36 -0
  1279. package/src/reviewmemory.ts +133 -0
  1280. package/src/reviewrendercli.ts +139 -0
  1281. package/src/round-work.ts +42 -0
  1282. package/src/runhost.ts +108 -0
  1283. package/src/scanauth.ts +36 -0
  1284. package/src/scanbox.ts +1081 -0
  1285. package/src/scancli.ts +1296 -0
  1286. package/src/scandriverlog.ts +32 -0
  1287. package/src/session-bridge.ts +139 -0
  1288. package/src/session-command-payload.ts +35 -0
  1289. package/src/session-command-supervisor.ts +1142 -0
  1290. package/src/sessionjsonl.ts +88 -0
  1291. package/src/sessions.ts +659 -0
  1292. package/src/setup/adapters.ts +335 -0
  1293. package/src/setup/baselines.ts +110 -0
  1294. package/src/setup/caserunner.ts +611 -0
  1295. package/src/setup/configcommit.ts +443 -0
  1296. package/src/setup/engine.ts +1001 -0
  1297. package/src/setup/facts.ts +627 -0
  1298. package/src/setup/findings.ts +1264 -0
  1299. package/src/setup/history.ts +443 -0
  1300. package/src/setup/interview.ts +243 -0
  1301. package/src/setup/ledger.ts +324 -0
  1302. package/src/setup/pins.ts +66 -0
  1303. package/src/setup/portrait.ts +635 -0
  1304. package/src/setup/presentation.ts +18 -0
  1305. package/src/setup/profile.ts +508 -0
  1306. package/src/setup/replay.ts +370 -0
  1307. package/src/setup/replaybuild.ts +392 -0
  1308. package/src/setup/replayrun.ts +1096 -0
  1309. package/src/setup/replaysource.ts +198 -0
  1310. package/src/setup/retune.ts +415 -0
  1311. package/src/setup/retunelog.ts +95 -0
  1312. package/src/setup/scan.ts +1033 -0
  1313. package/src/setup/scanbuild.ts +168 -0
  1314. package/src/setup/scanfacts.ts +107 -0
  1315. package/src/setup/scanlease.ts +297 -0
  1316. package/src/setup/scanreport.ts +373 -0
  1317. package/src/setup/scanrun.ts +2268 -0
  1318. package/src/setup/scantree.ts +259 -0
  1319. package/src/setup/stage2.ts +926 -0
  1320. package/src/setup/stage2request.ts +347 -0
  1321. package/src/setup/survey.ts +458 -0
  1322. package/src/setup/surveyrun.ts +758 -0
  1323. package/src/setup/textsniff.ts +95 -0
  1324. package/src/setup/trackersink.ts +433 -0
  1325. package/src/setup/triage.ts +768 -0
  1326. package/src/setup/tuning.ts +766 -0
  1327. package/src/setup/unitcut.ts +523 -0
  1328. package/src/singleflight.ts +86 -0
  1329. package/src/slack/announce.ts +911 -0
  1330. package/src/slack/api.ts +303 -0
  1331. package/src/slack/messages.ts +288 -0
  1332. package/src/slack/origin.ts +599 -0
  1333. package/src/slack/policy.ts +354 -0
  1334. package/src/slack/reply.ts +201 -0
  1335. package/src/slack/router.ts +285 -0
  1336. package/src/slack/socket.ts +384 -0
  1337. package/src/slack/types.ts +108 -0
  1338. package/src/sqlitehint.ts +32 -0
  1339. package/src/ssh-observation.ts +30 -0
  1340. package/src/state.ts +8243 -0
  1341. package/src/statemachine.ts +369 -0
  1342. package/src/stubs.ts +68 -0
  1343. package/src/takeover.ts +634 -0
  1344. package/src/takeoverpi.ts +293 -0
  1345. package/src/terminal-failure.ts +41 -0
  1346. package/src/thread-resolution-refusal.ts +2 -0
  1347. package/src/tmux.ts +153 -0
  1348. package/src/trackerid.ts +41 -0
  1349. package/src/transcriptindex.ts +665 -0
  1350. package/src/tui/actions.ts +484 -0
  1351. package/src/tui/alerts.ts +666 -0
  1352. package/src/tui/app.ts +1979 -0
  1353. package/src/tui/archiveops.ts +192 -0
  1354. package/src/tui/autofile.ts +233 -0
  1355. package/src/tui/board.ts +1432 -0
  1356. package/src/tui/candidates.ts +889 -0
  1357. package/src/tui/cellwidth.ts +148 -0
  1358. package/src/tui/changelog-store.ts +503 -0
  1359. package/src/tui/chord.ts +25 -0
  1360. package/src/tui/clipboard.ts +92 -0
  1361. package/src/tui/cmdbar.ts +296 -0
  1362. package/src/tui/companion.ts +1427 -0
  1363. package/src/tui/companionplan.ts +247 -0
  1364. package/src/tui/companiontab.ts +83 -0
  1365. package/src/tui/confighome.ts +155 -0
  1366. package/src/tui/connect.ts +68 -0
  1367. package/src/tui/deploy-ceremony.ts +104 -0
  1368. package/src/tui/deploy-drain.ts +129 -0
  1369. package/src/tui/deploy-process.ts +265 -0
  1370. package/src/tui/deploytab.ts +1523 -0
  1371. package/src/tui/design.ts +125 -0
  1372. package/src/tui/digesttab.ts +214 -0
  1373. package/src/tui/emptystate.ts +39 -0
  1374. package/src/tui/evidence.ts +999 -0
  1375. package/src/tui/feed.ts +604 -0
  1376. package/src/tui/feedbacktab.ts +1077 -0
  1377. package/src/tui/filters.ts +90 -0
  1378. package/src/tui/ghread.ts +321 -0
  1379. package/src/tui/historypull.ts +81 -0
  1380. package/src/tui/inboxpull.ts +236 -0
  1381. package/src/tui/index.ts +1565 -0
  1382. package/src/tui/issuesink.ts +238 -0
  1383. package/src/tui/jsonfile.ts +42 -0
  1384. package/src/tui/keys.ts +109 -0
  1385. package/src/tui/layout.ts +108 -0
  1386. package/src/tui/learning.ts +627 -0
  1387. package/src/tui/lessonswitch.ts +118 -0
  1388. package/src/tui/livecontrol.ts +228 -0
  1389. package/src/tui/memforward.ts +443 -0
  1390. package/src/tui/memincident.ts +363 -0
  1391. package/src/tui/memory.ts +620 -0
  1392. package/src/tui/memtab.ts +606 -0
  1393. package/src/tui/metricstab.ts +1177 -0
  1394. package/src/tui/model.ts +131 -0
  1395. package/src/tui/mouse.ts +98 -0
  1396. package/src/tui/msg.ts +268 -0
  1397. package/src/tui/nativecompanion.ts +417 -0
  1398. package/src/tui/openurl.ts +21 -0
  1399. package/src/tui/paint.ts +511 -0
  1400. package/src/tui/panes.ts +98 -0
  1401. package/src/tui/pipane.ts +219 -0
  1402. package/src/tui/poll-hooks.ts +12 -0
  1403. package/src/tui/poll.ts +147 -0
  1404. package/src/tui/proposals.ts +838 -0
  1405. package/src/tui/prpause.ts +150 -0
  1406. package/src/tui/reload.ts +436 -0
  1407. package/src/tui/remote.ts +413 -0
  1408. package/src/tui/rollover.ts +969 -0
  1409. package/src/tui/scantab.ts +1290 -0
  1410. package/src/tui/setupentry.ts +93 -0
  1411. package/src/tui/sidebar.ts +749 -0
  1412. package/src/tui/sshtriage.ts +510 -0
  1413. package/src/tui/startup.ts +231 -0
  1414. package/src/tui/tabs.ts +45 -0
  1415. package/src/tui/termmirror.ts +224 -0
  1416. package/src/tui/theme.ts +95 -0
  1417. package/src/tui/ticketstab.ts +310 -0
  1418. package/src/tui/tmuxsocket.ts +129 -0
  1419. package/src/tui/trackertickets.ts +84 -0
  1420. package/src/tui/unwedge.ts +286 -0
  1421. package/src/tui/viewer.ts +575 -0
  1422. package/src/tui/viewerinput.ts +173 -0
  1423. package/src/updatenotice.ts +178 -0
  1424. package/src/usage.ts +1154 -0
  1425. package/src/verdicts.ts +4689 -0
  1426. package/src/version.ts +80 -0
  1427. package/src/wakes.ts +476 -0
  1428. package/src/webhook.ts +942 -0
  1429. package/src/webhookheal.ts +276 -0
  1430. package/src/wire.ts +2153 -0
  1431. package/src/worker-session-host.ts +602 -0
  1432. package/src/workerbreaker.ts +156 -0
  1433. package/src/workercontrol.ts +104 -0
  1434. package/src/workerlease.ts +441 -0
  1435. package/src/workerliveness.ts +103 -0
  1436. package/src/workerlog.ts +79 -0
  1437. package/src/workerruntime.ts +30 -0
  1438. package/src/workersupervisor.ts +1538 -0
  1439. package/src/workerusage.ts +42 -0
  1440. package/src/workerworld.ts +427 -0
  1441. package/src/worktrees.ts +1138 -0
  1442. package/tests/fixtures/regression-cases.json +795 -0
  1443. package/tests/fixtures/seed-manifest.json +19 -0
package/src/github.ts ADDED
@@ -0,0 +1,1497 @@
1
+ // The GitHub adapter. Reads run directly on the installation token; every
2
+ // mutation is expressed as a MutationIntent and pushed through the gate
3
+ // (dry-run writes it to state/dryrun/ instead of executing). There is no
4
+ // review-dismissal function in this adapter, by design — never add one.
5
+ import {
6
+ GitHubRequestError,
7
+ type PrFileEntry,
8
+ } from "@quickchatai/pi-quickreview/src/githubcore.js";
9
+ import { ThreadResolutionRefusal } from "./thread-resolution-refusal.js";
10
+ import type { CheckSummary } from "@quickchatai/pi-quickreview/src/cigate.js";
11
+ import { reviewTeamSlug, type Config } from "./config.js";
12
+ import { CHECK_THIS_RE, DEFERRED_RE } from "@quickchatai/pi-quickreview/src/severity.js";
13
+ import {
14
+ APP_SELF_CONFIG_KINDS,
15
+ type MutationGate,
16
+ type MutationIntent,
17
+ type GateResult,
18
+ type ReviewPurpose,
19
+ } from "@quickchatai/pi-quickreview/src/gate.js";
20
+ import {
21
+ appActorAlias,
22
+ GITHUB_REQUEST_TIMEOUT_MS,
23
+ type TokenMinter,
24
+ type FetchFn,
25
+ } from "@quickchatai/pi-quickreview/src/githubAuth.js";
26
+ import type { PrReviews } from "./reviewhistory.js";
27
+
28
+ export interface PrConversation {
29
+ body: string;
30
+ labels: string[];
31
+ comments: Array<{ author: string; body: string; created_at: string }>;
32
+ }
33
+
34
+ export interface PrRef {
35
+ number: number;
36
+ title: string;
37
+ author: string;
38
+ head_sha: string;
39
+ /** The branch the PR targets (`base.ref`) — what the review compares against
40
+ * and the branch the worktree keeps fresh each round. */
41
+ base_ref: string;
42
+ /** The PR description: pass-intent's whole intent artifact and the
43
+ * substance-bar input. Optional so test fakes of PrRef stay valid; the real
44
+ * adapter always sets it ("" when GitHub returns null). */
45
+ body?: string;
46
+ updated_at: string;
47
+ state: "open" | "closed";
48
+ draft: boolean;
49
+ merged: boolean;
50
+ labels: string[];
51
+ }
52
+
53
+ /** The App's own webhook configuration (`GET /app/hook/config`, JWT-authed). */
54
+ export interface AppWebhookConfig {
55
+ url: string;
56
+ content_type: string;
57
+ insecure_ssl: string;
58
+ }
59
+
60
+ export interface RequestedReviewers {
61
+ users: string[];
62
+ teams: string[];
63
+ }
64
+
65
+ export interface StalePr {
66
+ number: number;
67
+ head_sha: string;
68
+ approved_sha: string;
69
+ approved_at: string;
70
+ }
71
+
72
+ // A changed file as the files API reports it lives with the core's half.
73
+ export type { PrFileEntry };
74
+
75
+ export interface ReviewFull {
76
+ id: number;
77
+ user: string;
78
+ /** GitHub's account type ("User" | "Bot") — the human/bot discriminator the
79
+ * feedback capture sweep filters on. Optional: not every caller reads it. */
80
+ user_type?: string;
81
+ state: string;
82
+ body: string;
83
+ commit_id: string;
84
+ submitted_at: string | null;
85
+ }
86
+
87
+ export interface ReviewCommentFull {
88
+ id: number;
89
+ user: string;
90
+ /** GitHub's account type — see ReviewFull.user_type. */
91
+ user_type?: string;
92
+ body: string;
93
+ path: string;
94
+ /** GitHub's `line`, which follows the diff as the branch moves and goes
95
+ * null once the comment is outdated — then this adapter's fallback,
96
+ * `original_line`. So a non-null `line` does not mean the comment is
97
+ * current. */
98
+ line: number | null;
99
+ pull_request_review_id: number | null;
100
+ in_reply_to_id: number | null;
101
+ /** When GitHub authored it. Optional for the same reason. */
102
+ created_at?: string | null;
103
+ /** The line in the commit the comment was posted on. Unlike GitHub's
104
+ * `line` it does not move with the diff, so it is what the outcome capture
105
+ * matches a finding's anchor by. Optional: not every caller reads it. */
106
+ original_line?: number | null;
107
+ }
108
+
109
+ /** A pulls-endpoint PR as a `PrRef`. `merged` is the caller's: the single-PR
110
+ * read carries it, the open-PR list does not. */
111
+ function toPrRef(pr: any, merged: boolean): PrRef {
112
+ return {
113
+ number: pr.number,
114
+ title: pr.title,
115
+ author: pr.user.login,
116
+ head_sha: pr.head.sha,
117
+ base_ref: pr.base?.ref ?? "",
118
+ body: pr.body ?? "",
119
+ updated_at: pr.updated_at,
120
+ state: pr.state,
121
+ draft: pr.draft,
122
+ merged,
123
+ labels: (pr.labels ?? []).map((l: { name: string }) => l.name),
124
+ };
125
+ }
126
+
127
+ function toReviewCommentFull(c: any): ReviewCommentFull {
128
+ return {
129
+ id: c.id,
130
+ user: c.user?.login ?? "",
131
+ user_type: c.user?.type ?? "User",
132
+ body: c.body ?? "",
133
+ path: c.path ?? "",
134
+ line: c.line ?? c.original_line ?? null,
135
+ pull_request_review_id: c.pull_request_review_id ?? null,
136
+ in_reply_to_id: c.in_reply_to_id ?? null,
137
+ created_at: c.created_at ?? null,
138
+ original_line: c.original_line ?? null,
139
+ };
140
+ }
141
+
142
+ /** One top-level PR conversation comment (the issues API). Read-only; read by
143
+ * the feedback capture sweep. */
144
+ export interface IssueCommentFull {
145
+ id: number;
146
+ user: string;
147
+ user_type: string;
148
+ body: string;
149
+ created_at: string | null;
150
+ }
151
+
152
+ /** One past review by the bot identity, as the importer sees it. */
153
+ export interface PrReviewRecord {
154
+ database_id: number;
155
+ state: string;
156
+ submitted_at: string | null;
157
+ body: string;
158
+ commit_oid: string | null;
159
+ }
160
+
161
+ /** A timeline item relevant to trigger reconstruction. */
162
+ export interface PrTimelineRecord {
163
+ type: string;
164
+ created_at: string;
165
+ label: string | null;
166
+ actor: string | null;
167
+ requested_team: string | null;
168
+ requested_user: string | null;
169
+ }
170
+
171
+ export interface PrReviewHistory {
172
+ number: number;
173
+ title: string;
174
+ author: string;
175
+ created_at: string;
176
+ closed_at: string | null;
177
+ merged_at: string | null;
178
+ merged: boolean;
179
+ reviews: PrReviewRecord[];
180
+ timeline: PrTimelineRecord[];
181
+ }
182
+
183
+ interface RawReviewNode {
184
+ databaseId?: number;
185
+ state?: string;
186
+ submittedAt?: string | null;
187
+ body?: string;
188
+ commit?: { oid?: string } | null;
189
+ }
190
+
191
+ interface RawTimelineNode {
192
+ __typename?: string;
193
+ createdAt?: string;
194
+ label?: { name?: string } | null;
195
+ actor?: { login?: string } | null;
196
+ requestedReviewer?: { slug?: string; login?: string } | null;
197
+ }
198
+
199
+ /** A GraphQL connection as the history queries request it: nodes plus the
200
+ * cursor to continue from. */
201
+ interface Connection<N> {
202
+ nodes?: Array<N | null>;
203
+ pageInfo?: { hasNextPage?: boolean; endCursor?: string | null } | null;
204
+ }
205
+
206
+ /** How many newest-first pages `listRecentReviewComments` reads to reach the
207
+ * comment a lost reply answered. */
208
+ const RECENT_COMMENTS_MAX_PAGES = 10;
209
+
210
+ /** GitHub search serves at most 1,000 hits: ten pages of 100. */
211
+ const TRIGGER_SEARCH_MAX_PAGES = 10;
212
+
213
+ /** The trigger events the history importer reconstructs — one list for the
214
+ * first query and every follow-up page. */
215
+ const HISTORY_TIMELINE_TYPES =
216
+ "[LABELED_EVENT, REVIEW_REQUESTED_EVENT, HEAD_REF_FORCE_PUSHED_EVENT]";
217
+
218
+ /** Hard stop for a connection walk — a server that keeps answering
219
+ * hasNextPage with a cursor that never ends cannot loop forever. */
220
+ const MAX_CONNECTION_PAGES = 50;
221
+
222
+ /** Every node of a connection: the first page as already fetched, then each
223
+ * `next(cursor)` until GitHub says there is no next page. A connection still
224
+ * going after `MAX_CONNECTION_PAGES` throws rather than answer a partial
225
+ * history as the whole of it. */
226
+ async function walkConnection<N>(
227
+ first: Connection<N> | null | undefined,
228
+ next: (after: string) => Promise<Connection<N> | null | undefined>,
229
+ ): Promise<N[]> {
230
+ const out: N[] = [];
231
+ let conn = first;
232
+ for (let page = 1; conn; page++) {
233
+ out.push(...(conn.nodes ?? []).filter((n): n is N => n !== null && n !== undefined));
234
+ const info = conn.pageInfo;
235
+ if (!info?.hasNextPage || !info.endCursor) break;
236
+ if (page >= MAX_CONNECTION_PAGES)
237
+ throw new Error(
238
+ `connection exceeded ${MAX_CONNECTION_PAGES} pages — history would be partial`,
239
+ );
240
+ conn = await next(info.endCursor);
241
+ }
242
+ return out;
243
+ }
244
+
245
+ /** Selection sets shared by a history query's first page and its follow-ups,
246
+ * so the two can never request different fields. */
247
+ const HISTORY_REVIEWS_BODY =
248
+ "pageInfo { hasNextPage endCursor } nodes { databaseId state submittedAt body commit { oid } }";
249
+ const HISTORY_TIMELINE_BODY = `pageInfo { hasNextPage endCursor }
250
+ nodes {
251
+ __typename
252
+ ... on LabeledEvent { createdAt label { name } actor { login } }
253
+ ... on ReviewRequestedEvent { createdAt requestedReviewer { ... on Team { slug } ... on User { login } } }
254
+ ... on HeadRefForcePushedEvent { createdAt }
255
+ }`;
256
+ const DIGEST_COMMENTS_BODY =
257
+ "pageInfo { hasNextPage endCursor } nodes { author { __typename login } path body }";
258
+ const DIGEST_REVIEWS_BODY = `pageInfo { hasNextPage endCursor }
259
+ nodes {
260
+ id author { __typename login } state submittedAt body
261
+ comments(first: 100) { ${DIGEST_COMMENTS_BODY} }
262
+ }`;
263
+
264
+ /** GitHub serves a compare's changed files on its first page only, capped at
265
+ * this many. A list that long is a floor, not the delta. */
266
+ export const COMPARE_FILES_CAP = 300;
267
+
268
+ /** GitHub serves at most this many pages of 100 from `pulls/<n>/files`. */
269
+ const PR_FILES_MAX_PAGES = 30;
270
+
271
+ /** A file list that may have been cut short by GitHub's own cap. */
272
+ export type FileList = PrFileEntry[] & { truncated?: boolean };
273
+
274
+ interface RawPrHistory {
275
+ number?: number;
276
+ title?: string;
277
+ createdAt?: string;
278
+ closedAt?: string | null;
279
+ mergedAt?: string | null;
280
+ merged?: boolean;
281
+ author?: { login?: string } | null;
282
+ reviews?: Connection<RawReviewNode> | null;
283
+ timelineItems?: Connection<RawTimelineNode> | null;
284
+ }
285
+
286
+ export type PrDisposition = { kind: "open" | "closed"; head_sha: string } | { kind: "absent" };
287
+
288
+ // The error a refused request throws is the review core's (packages/pi-plugin/src/githubcore.ts).
289
+ export { GitHubRequestError };
290
+
291
+ export interface SearchIssueItem {
292
+ number: number;
293
+ title: string;
294
+ user: { login: string; type: string };
295
+ updated_at: string;
296
+ created_at: string;
297
+ }
298
+
299
+ /** How much of a raw read's body is kept (GitHubAdapter.readText). */
300
+ export interface RawReadOptions {
301
+ maxBytes: number;
302
+ /** Keep the end instead of the head (a job log's tail). */
303
+ tail?: boolean;
304
+ }
305
+
306
+ /** A raw read as GitHub answered it. */
307
+ export interface RawReadAnswer {
308
+ status: number;
309
+ body: string;
310
+ /** Bytes past `maxBytes` were dropped. */
311
+ truncated: boolean;
312
+ }
313
+
314
+ /** A response body as UTF-8 text, at most `max` bytes of it, read as a stream so
315
+ * a large log never sits in memory whole: the first `max` bytes, or with `tail`
316
+ * the last. A cut that splits a character loses that character only. */
317
+ export async function cappedText(
318
+ res: Response,
319
+ max: number,
320
+ tail: boolean,
321
+ ): Promise<{ text: string; truncated: boolean }> {
322
+ if (res.body === null) return { text: "", truncated: false };
323
+ const chunks: Buffer[] = [];
324
+ let held = 0;
325
+ let truncated = false;
326
+ const reader = res.body.getReader();
327
+ for (;;) {
328
+ const { done, value } = await reader.read();
329
+ if (done) break;
330
+ let chunk = Buffer.from(value);
331
+ if (!tail) {
332
+ if (held + chunk.length > max) {
333
+ chunk = chunk.subarray(0, max - held);
334
+ truncated = true;
335
+ }
336
+ chunks.push(chunk);
337
+ held += chunk.length;
338
+ if (truncated) {
339
+ await reader.cancel().catch(() => {});
340
+ break;
341
+ }
342
+ continue;
343
+ }
344
+ chunks.push(chunk);
345
+ held += chunk.length;
346
+ while (held > max && chunks.length > 0) {
347
+ truncated = true;
348
+ const first = chunks[0]!;
349
+ const over = held - max;
350
+ if (first.length <= over) {
351
+ chunks.shift();
352
+ held -= first.length;
353
+ } else {
354
+ chunks[0] = first.subarray(over);
355
+ held -= over;
356
+ }
357
+ }
358
+ }
359
+ return { text: Buffer.concat(chunks).toString("utf8"), truncated };
360
+ }
361
+
362
+ /** One files-API entry (pulls/files and compare share the shape). */
363
+ function fileEntry(f: any): PrFileEntry {
364
+ return {
365
+ filename: f.filename,
366
+ additions: f.additions,
367
+ deletions: f.deletions,
368
+ status: f.status,
369
+ ...(typeof f.patch === "string" ? { patch: f.patch } : {}),
370
+ ...(typeof f.previous_filename === "string" ? { previous_filename: f.previous_filename } : {}),
371
+ };
372
+ }
373
+
374
+ export class GitHubAdapter {
375
+ readonly owner: string;
376
+ readonly repo: string;
377
+
378
+ constructor(
379
+ private readonly cfg: Config,
380
+ private readonly minter: TokenMinter,
381
+ readonly gate: MutationGate,
382
+ private readonly fetchFn: FetchFn = fetch,
383
+ private readonly apiBase = "https://api.github.com",
384
+ ) {
385
+ const [owner, repo] = cfg.github.repo.split("/");
386
+ if (!owner || !repo) throw new Error(`bad repo: ${cfg.github.repo}`);
387
+ this.owner = owner;
388
+ this.repo = repo;
389
+ }
390
+
391
+ // ---- plumbing ----------------------------------------------------------
392
+
393
+ private async request<T>(
394
+ method: string,
395
+ path: string,
396
+ body?: unknown,
397
+ auth: "installation" | "jwt" = "installation",
398
+ ): Promise<T> {
399
+ const send = (token: string): Promise<Response> =>
400
+ this.fetchFn(`${this.apiBase}${path}`, {
401
+ method,
402
+ signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
403
+ headers: {
404
+ authorization: `Bearer ${token}`,
405
+ accept: "application/vnd.github+json",
406
+ "x-github-api-version": "2022-11-28",
407
+ ...(body !== undefined ? { "content-type": "application/json" } : {}),
408
+ },
409
+ body: body !== undefined ? JSON.stringify(body) : undefined,
410
+ });
411
+ const res =
412
+ auth === "jwt" ? await send(this.minter.appJwt()) : await this.sendInstallation(send);
413
+ if (!res.ok) {
414
+ const retryAfter = res.headers?.get("retry-after");
415
+ const reset = res.headers?.get("x-ratelimit-reset");
416
+ const retryAfterMs = retryAfter
417
+ ? /^\d+$/.test(retryAfter)
418
+ ? Number(retryAfter) * 1000
419
+ : Math.max(0, Date.parse(retryAfter) - Date.now())
420
+ : reset
421
+ ? Math.max(0, Number(reset) * 1000 - Date.now())
422
+ : undefined;
423
+ throw new GitHubRequestError(
424
+ method,
425
+ path,
426
+ res.status,
427
+ (await res.text()).slice(0, 300),
428
+ Number.isFinite(retryAfterMs) ? retryAfterMs : undefined,
429
+ );
430
+ }
431
+ if (res.status === 204) return undefined as T;
432
+ return (await res.json()) as T;
433
+ }
434
+
435
+ /** Send with an installation token; on a 401 drop the rejected response
436
+ * body, invalidate the cached token and send once more with a fresh one.
437
+ * The one owner of the installation-token 401 policy. */
438
+ private async sendInstallation(send: (token: string) => Promise<Response>): Promise<Response> {
439
+ let res = await send(await this.minter.installationToken());
440
+ if (res.status === 401) {
441
+ await res.body?.cancel().catch(() => {});
442
+ this.minter.invalidate();
443
+ res = await send(await this.minter.installationToken());
444
+ }
445
+ return res;
446
+ }
447
+
448
+ private graphqlFetch(
449
+ token: string,
450
+ query: string,
451
+ variables: Record<string, unknown>,
452
+ ): Promise<Response> {
453
+ return this.fetchFn(`${this.apiBase}/graphql`, {
454
+ method: "POST",
455
+ signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
456
+ headers: { authorization: `Bearer ${token}`, "content-type": "application/json" },
457
+ body: JSON.stringify({ query, variables }),
458
+ });
459
+ }
460
+
461
+ /** One GraphQL query, with REST's deadline and its one re-mint on a 401. */
462
+ private async graphql<T>(query: string, variables: Record<string, unknown>): Promise<T> {
463
+ const res = await this.sendInstallation((token) => this.graphqlFetch(token, query, variables));
464
+ if (!res.ok) {
465
+ let detail = "";
466
+ try {
467
+ detail = (await res.text()).slice(0, 300);
468
+ } catch {
469
+ // No readable body: the status alone.
470
+ }
471
+ throw new Error(`graphql → ${res.status}${detail ? `: ${detail}` : ""}`);
472
+ }
473
+ const payload = (await res.json()) as { data?: T; errors?: unknown[] };
474
+ if (payload.errors?.length)
475
+ throw new Error(`graphql errors: ${JSON.stringify(payload.errors).slice(0, 300)}`);
476
+ return payload.data as T;
477
+ }
478
+
479
+ /**
480
+ * A session helper's read, served by the daemon (src/ghread.ts): one
481
+ * GET of `path` with the installation token, redirects followed (fetch drops
482
+ * the Authorization header on a cross-origin hop, which is what the Actions
483
+ * log storage wants), answered as GitHub answered it — the status and at most
484
+ * `maxBytes` of the body as text, the head or, with `tail`, the end. An HTTP
485
+ * error status is an answer, not a throw; only a request that never got one
486
+ * throws. The caller has already checked `path` is a read of the reviewed repo.
487
+ */
488
+ async readText(path: string, o: RawReadOptions): Promise<RawReadAnswer> {
489
+ return this.rawAnswer(o, (token) =>
490
+ this.fetchFn(`${this.apiBase}${path}`, {
491
+ method: "GET",
492
+ redirect: "follow",
493
+ signal: AbortSignal.timeout(GITHUB_REQUEST_TIMEOUT_MS),
494
+ headers: {
495
+ authorization: `Bearer ${token}`,
496
+ accept: "application/vnd.github+json",
497
+ "x-github-api-version": "2022-11-28",
498
+ },
499
+ }),
500
+ );
501
+ }
502
+
503
+ /** The same for one GraphQL read: the caller supplies a query it holds itself
504
+ * (src/ghread.ts GH_READ_QUERIES), never one a session wrote. */
505
+ async graphqlText(
506
+ query: string,
507
+ variables: Record<string, unknown>,
508
+ o: RawReadOptions,
509
+ ): Promise<RawReadAnswer> {
510
+ return this.rawAnswer(o, (token) => this.graphqlFetch(token, query, variables));
511
+ }
512
+
513
+ /** One raw request with REST's one re-mint on a 401, its body capped. */
514
+ private async rawAnswer(
515
+ o: RawReadOptions,
516
+ send: (token: string) => Promise<Response>,
517
+ ): Promise<RawReadAnswer> {
518
+ const res = await this.sendInstallation(send);
519
+ const { text, truncated } = await cappedText(res, o.maxBytes, o.tail === true);
520
+ return { status: res.status, body: text, truncated };
521
+ }
522
+
523
+ /** The mutation executor the gate runs when it decides a mutation is real —
524
+ * every kind in live mode, and the app-self-config class in dry-run too.
525
+ * The gate, never a call site, makes that decision. */
526
+ executeMutation = async (intent: MutationIntent): Promise<unknown> => {
527
+ if (intent.method === "git")
528
+ throw new Error("git pushes are executed by the caller, not the REST executor");
529
+ if (intent.path === "/graphql") {
530
+ const body = intent.body as { query?: unknown; variables?: unknown } | undefined;
531
+ if (
532
+ typeof body?.query !== "string" ||
533
+ typeof body.variables !== "object" ||
534
+ body.variables === null
535
+ ) {
536
+ throw new Error(`invalid GraphQL mutation intent for ${intent.kind}`);
537
+ }
538
+ return this.graphql(body.query, body.variables as Record<string, unknown>);
539
+ }
540
+ // App-self-config intents address the App's own configuration:
541
+ // GitHub authenticates those routes with the App JWT, not an installation
542
+ // token — the same auth split request() already carries for the
543
+ // /app/hook/deliveries read.
544
+ const auth = (APP_SELF_CONFIG_KINDS as readonly string[]).includes(intent.kind)
545
+ ? "jwt"
546
+ : "installation";
547
+ return this.request(intent.method, intent.path, intent.body, auth);
548
+ };
549
+
550
+ // ---- triggered-set queries (reads) -------------------------------------
551
+
552
+ /** One page of issue search (100 hits). GitHub's search may time out and
553
+ * answer a partial set; absence read from it would be invented, so the read
554
+ * fails with `incompleteMessage` rather than answers. */
555
+ private async searchIssuePage(
556
+ q: string,
557
+ page: number,
558
+ incompleteMessage: string,
559
+ ): Promise<SearchIssueItem[]> {
560
+ const res = await this.request<{ items?: SearchIssueItem[]; incomplete_results?: boolean }>(
561
+ "GET",
562
+ `/search/issues?q=${encodeURIComponent(q)}&per_page=100&page=${page}`,
563
+ );
564
+ if (res.incomplete_results === true) throw new Error(incompleteMessage);
565
+ return res.items ?? [];
566
+ }
567
+
568
+ /** Every hit, not the first 100: the reconcile pass reads a PR missing from
569
+ * this list as trigger-ABSENT, so a truncated page would fake an absent →
570
+ * present edge the moment search order reshuffled it back in. */
571
+ private async searchIssues(q: string): Promise<SearchIssueItem[]> {
572
+ const out: SearchIssueItem[] = [];
573
+ for (let page = 1; page <= TRIGGER_SEARCH_MAX_PAGES; page++) {
574
+ const items = await this.searchIssuePage(
575
+ q,
576
+ page,
577
+ `trigger search "${q}" answered incomplete results`,
578
+ );
579
+ out.push(...items);
580
+ if (items.length < 100) return out;
581
+ }
582
+ throw new Error(`trigger search "${q}" exceeded ${TRIGGER_SEARCH_MAX_PAGES} pages`);
583
+ }
584
+
585
+ /** Every page of a list endpoint that answers a bare JSON array. `path`
586
+ * carries `per_page=100`; a short page ends the read and `maxPages` bounds
587
+ * it — `truncated` says the bound, not the list, ended it. */
588
+ private async pagedArray<T = any>(
589
+ path: string,
590
+ maxPages = 30,
591
+ ): Promise<{ rows: T[]; truncated: boolean }> {
592
+ const rows: T[] = [];
593
+ for (let page = 1; page <= maxPages; page++) {
594
+ const batch = await this.request<T[]>("GET", `${path}&page=${page}`);
595
+ rows.push(...batch);
596
+ if (batch.length < 100) return { rows, truncated: false };
597
+ }
598
+ return { rows, truncated: true };
599
+ }
600
+
601
+ /** Paginated issue search — read-only, used by the history importer.
602
+ * Stops on a short page or after `maxPages` (search caps at 1,000 hits). */
603
+ async searchIssuesPaged(q: string, maxPages = 10): Promise<SearchIssueItem[]> {
604
+ const out: SearchIssueItem[] = [];
605
+ for (let page = 1; page <= maxPages; page++) {
606
+ const items = await this.searchIssuePage(q, page, "incomplete history search results");
607
+ out.push(...items);
608
+ if (items.length < 100) break;
609
+ }
610
+ return out;
611
+ }
612
+
613
+ /** Read-only GraphQL per PR: the reviews `author` submitted plus the
614
+ * timeline items the backfill importer reconstructs triggers from — one
615
+ * query, then one more per further page of either connection. Returns null
616
+ * when the PR is gone. */
617
+ async prReviewHistory(n: number, author: string): Promise<PrReviewHistory | null> {
618
+ const data = await this.graphql<{ repository?: { pullRequest?: RawPrHistory | null } | null }>(
619
+ `query ($owner: String!, $repo: String!, $number: Int!, $author: String!) {
620
+ repository(owner: $owner, name: $repo) {
621
+ pullRequest(number: $number) {
622
+ number title createdAt closedAt mergedAt merged
623
+ author { login }
624
+ reviews(author: $author, first: 100) { ${HISTORY_REVIEWS_BODY} }
625
+ timelineItems(itemTypes: ${HISTORY_TIMELINE_TYPES}, first: 100) { ${HISTORY_TIMELINE_BODY} }
626
+ }
627
+ }
628
+ }`,
629
+ { owner: this.owner, repo: this.repo, number: n, author },
630
+ );
631
+ const pr = data.repository?.pullRequest;
632
+ if (!pr) return null;
633
+ // A long PR's bot reviews run past 100 (every thread reply is a review):
634
+ // follow the cursors, or the importer silently loses the NEWEST ones.
635
+ const vars = { owner: this.owner, repo: this.repo, number: n, author };
636
+ const reviews = await walkConnection(pr.reviews, async (after) => {
637
+ const d = await this.graphql<{ repository?: { pullRequest?: RawPrHistory | null } | null }>(
638
+ `query ($owner: String!, $repo: String!, $number: Int!, $author: String!, $after: String!) {
639
+ repository(owner: $owner, name: $repo) {
640
+ pullRequest(number: $number) {
641
+ reviews(author: $author, first: 100, after: $after) { ${HISTORY_REVIEWS_BODY} }
642
+ }
643
+ }
644
+ }`,
645
+ { ...vars, after },
646
+ );
647
+ return d.repository?.pullRequest?.reviews;
648
+ });
649
+ const timeline = await walkConnection(pr.timelineItems, async (after) => {
650
+ const d = await this.graphql<{ repository?: { pullRequest?: RawPrHistory | null } | null }>(
651
+ `query ($owner: String!, $repo: String!, $number: Int!, $after: String!) {
652
+ repository(owner: $owner, name: $repo) {
653
+ pullRequest(number: $number) {
654
+ timelineItems(itemTypes: ${HISTORY_TIMELINE_TYPES}, first: 100, after: $after) { ${HISTORY_TIMELINE_BODY} }
655
+ }
656
+ }
657
+ }`,
658
+ { owner: this.owner, repo: this.repo, number: n, after },
659
+ );
660
+ return d.repository?.pullRequest?.timelineItems;
661
+ });
662
+ return {
663
+ number: pr.number ?? n,
664
+ title: pr.title ?? "",
665
+ author: pr.author?.login ?? "",
666
+ created_at: pr.createdAt ?? "",
667
+ closed_at: pr.closedAt ?? null,
668
+ merged_at: pr.mergedAt ?? null,
669
+ merged: Boolean(pr.merged),
670
+ reviews: reviews.map((r) => ({
671
+ database_id: r.databaseId ?? 0,
672
+ state: r.state ?? "",
673
+ submitted_at: r.submittedAt ?? null,
674
+ body: r.body ?? "",
675
+ commit_oid: r.commit?.oid ?? null,
676
+ })),
677
+ timeline: timeline
678
+ .filter((t) => Boolean(t.createdAt))
679
+ .map((t) => ({
680
+ type: t.__typename ?? "",
681
+ created_at: t.createdAt!,
682
+ label: t.label?.name ?? null,
683
+ actor: t.actor?.login ?? null,
684
+ requested_team: t.requestedReviewer?.slug ?? null,
685
+ requested_user: t.requestedReviewer?.login ?? null,
686
+ })),
687
+ };
688
+ }
689
+
690
+ /** Read-only GraphQL per PR for the review history digest: every author's
691
+ * reviews, each with its inline comments, and the author's type so the
692
+ * digest can keep people and drop Apps — one query, then one more per
693
+ * further page of reviews or of one review's comments. Returns null when
694
+ * the PR is gone. */
695
+ async prReviews(n: number): Promise<PrReviews | null> {
696
+ interface RawAuthor {
697
+ __typename?: string;
698
+ login?: string;
699
+ }
700
+ interface RawComment {
701
+ author?: RawAuthor | null;
702
+ path?: string | null;
703
+ body?: string;
704
+ }
705
+ interface RawReview {
706
+ id?: string;
707
+ author?: RawAuthor | null;
708
+ state?: string;
709
+ submittedAt?: string | null;
710
+ body?: string;
711
+ comments?: Connection<RawComment> | null;
712
+ }
713
+ interface RawPr {
714
+ number?: number;
715
+ title?: string;
716
+ mergedAt?: string | null;
717
+ author?: { login?: string } | null;
718
+ reviews?: Connection<RawReview> | null;
719
+ }
720
+ const data = await this.graphql<{ repository?: { pullRequest?: RawPr | null } | null }>(
721
+ `query ($owner: String!, $repo: String!, $number: Int!) {
722
+ repository(owner: $owner, name: $repo) {
723
+ pullRequest(number: $number) {
724
+ number title mergedAt
725
+ author { login }
726
+ reviews(first: 100) { ${DIGEST_REVIEWS_BODY} }
727
+ }
728
+ }
729
+ }`,
730
+ { owner: this.owner, repo: this.repo, number: n },
731
+ );
732
+ const pr = data.repository?.pullRequest;
733
+ if (!pr) return null;
734
+ // Reviews come oldest-first, so a truncated read drops the NEWEST human
735
+ // feedback — follow the cursors, and each long review's comment cursor.
736
+ const rawReviews = await walkConnection(pr.reviews, async (after) => {
737
+ const d = await this.graphql<{ repository?: { pullRequest?: RawPr | null } | null }>(
738
+ `query ($owner: String!, $repo: String!, $number: Int!, $after: String!) {
739
+ repository(owner: $owner, name: $repo) {
740
+ pullRequest(number: $number) {
741
+ reviews(first: 100, after: $after) { ${DIGEST_REVIEWS_BODY} }
742
+ }
743
+ }
744
+ }`,
745
+ { owner: this.owner, repo: this.repo, number: n, after },
746
+ );
747
+ return d.repository?.pullRequest?.reviews;
748
+ });
749
+ const commentsOf = (r: RawReview): Promise<RawComment[]> =>
750
+ walkConnection(r.comments, async (after) => {
751
+ if (!r.id) return null;
752
+ const d = await this.graphql<{
753
+ node?: { comments?: Connection<RawComment> | null } | null;
754
+ }>(
755
+ `query ($id: ID!, $after: String!) {
756
+ node(id: $id) { ... on PullRequestReview { comments(first: 100, after: $after) { ${DIGEST_COMMENTS_BODY} } } }
757
+ }`,
758
+ { id: r.id, after },
759
+ );
760
+ return d.node?.comments;
761
+ });
762
+ const reviews: Array<{ r: RawReview; comments: RawComment[] }> = [];
763
+ for (const r of rawReviews) reviews.push({ r, comments: await commentsOf(r) });
764
+ const author = (a: RawAuthor | null | undefined): { login: string; type: string } | null =>
765
+ a ? { login: a.login ?? "", type: a.__typename ?? "" } : null;
766
+ return {
767
+ number: pr.number ?? n,
768
+ title: pr.title ?? "",
769
+ author: pr.author?.login ?? "",
770
+ merged_at: pr.mergedAt ?? null,
771
+ reviews: reviews.map(({ r, comments }) => ({
772
+ author: author(r.author),
773
+ state: r.state ?? "",
774
+ submitted_at: r.submittedAt ?? null,
775
+ body: r.body ?? "",
776
+ comments: comments.map((c) => ({
777
+ author: author(c.author),
778
+ path: c.path ?? null,
779
+ body: c.body ?? "",
780
+ })),
781
+ })),
782
+ };
783
+ }
784
+
785
+ /** PRs carrying the trigger label. Bot-authored PRs are excluded — the app
786
+ * can never approve its own PRs. */
787
+ async searchLabelTriggered(): Promise<SearchIssueItem[]> {
788
+ const q = `repo:${this.cfg.github.repo} is:open is:pr label:"${this.cfg.github.trigger_label}" -author:app/${this.cfg.github.app_slug}`;
789
+ return this.searchIssues(q);
790
+ }
791
+
792
+ /** PRs with a pending review-team request. */
793
+ async searchTeamTriggered(): Promise<SearchIssueItem[]> {
794
+ const q = `repo:${this.cfg.github.repo} is:open is:pr team-review-requested:${this.cfg.github.review_team} -author:app/${this.cfg.github.app_slug}`;
795
+ return this.searchIssues(q);
796
+ }
797
+
798
+ // ---- authoritative per-PR reads ----------------------------------------
799
+
800
+ async getPr(n: number): Promise<PrRef> {
801
+ const pr = await this.request<any>("GET", `/repos/${this.owner}/${this.repo}/pulls/${n}`);
802
+ return toPrRef(pr, Boolean(pr.merged));
803
+ }
804
+
805
+ /** Authoritative terminality read for operator debt cleanup. A PR-endpoint
806
+ * 404 is not enough by itself: GitHub also conceals inaccessible private
807
+ * repositories as 404. Absence therefore requires a second installation-
808
+ * authenticated read proving access to this exact repository. */
809
+ async getPrDisposition(n: number): Promise<PrDisposition> {
810
+ const path = `/repos/${this.owner}/${this.repo}/pulls/${n}`;
811
+ try {
812
+ const raw = await this.request<unknown>("GET", path);
813
+ if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
814
+ throw new Error(`malformed PR #${n} response`);
815
+ }
816
+ const pr = raw as { number?: unknown; state?: unknown; head?: { sha?: unknown } | null };
817
+ if (pr.number !== n)
818
+ throw new Error(`PR endpoint #${n} returned number ${String(pr.number)}`);
819
+ if (pr.state !== "open" && pr.state !== "closed")
820
+ throw new Error(`unexpected PR #${n} state ${String(pr.state)}`);
821
+ const head = pr.head?.sha;
822
+ if (typeof head !== "string" || head.length === 0)
823
+ throw new Error(`malformed PR #${n} head sha`);
824
+ return { kind: pr.state, head_sha: head };
825
+ } catch (error) {
826
+ if (!(
827
+ error instanceof GitHubRequestError &&
828
+ error.method === "GET" &&
829
+ error.path === path &&
830
+ error.status === 404
831
+ ))
832
+ throw error;
833
+ const repoPath = `/repos/${this.owner}/${this.repo}`;
834
+ const rawRepo = await this.request<unknown>("GET", repoPath);
835
+ if (typeof rawRepo !== "object" || rawRepo === null || Array.isArray(rawRepo)) {
836
+ throw new Error(`malformed repository proof for ${this.owner}/${this.repo}`, {
837
+ cause: error,
838
+ });
839
+ }
840
+ const fullName = (rawRepo as { full_name?: unknown }).full_name;
841
+ const expected = `${this.owner}/${this.repo}`;
842
+ if (typeof fullName !== "string" || fullName.toLowerCase() !== expected.toLowerCase()) {
843
+ throw new Error(`repository proof mismatch for ${expected}`, { cause: error });
844
+ }
845
+ return { kind: "absent" };
846
+ }
847
+ }
848
+
849
+ /** Read-only conversation surface for the TUI detail pane:
850
+ * PR description, labels, and the newest issue comments, oldest-first.
851
+ *
852
+ * The per-issue comments endpoint ignores `sort`/`direction` (those belong
853
+ * to the repo-wide listing) and always answers oldest-first, so the newest
854
+ * comments are on the LAST page: its index comes off the PR's own comment
855
+ * count, and the page before it is read too when the last one is short. */
856
+ async prConversation(n: number, commentCount = 3): Promise<PrConversation> {
857
+ const pr = await this.request<any>("GET", `/repos/${this.owner}/${this.repo}/pulls/${n}`);
858
+ const path = `/repos/${this.owner}/${this.repo}/issues/${n}/comments?per_page=100`;
859
+ let comments: any[];
860
+ if (typeof pr.comments === "number") {
861
+ const last = Math.max(1, Math.ceil(pr.comments / 100));
862
+ comments = await this.request<any[]>("GET", `${path}&page=${last}`);
863
+ if (comments.length < commentCount && last > 1)
864
+ comments = [...(await this.request<any[]>("GET", `${path}&page=${last - 1}`)), ...comments];
865
+ } else {
866
+ comments = (await this.pagedArray(path)).rows;
867
+ }
868
+ return {
869
+ body: pr.body ?? "",
870
+ labels: (pr.labels ?? []).map((l: { name: string }) => l.name),
871
+ comments: comments.slice(-commentCount).map((c) => ({
872
+ author: c.user?.login ?? "?",
873
+ body: c.body ?? "",
874
+ created_at: c.created_at ?? "",
875
+ })),
876
+ };
877
+ }
878
+
879
+ /** EVERY open PR: the list is newest-first, so a first-page read drops the
880
+ * oldest tracked PRs out of reconcile step 1 entirely. */
881
+ async listOpenPrs(): Promise<PrRef[]> {
882
+ const { rows: prs, truncated } = await this.pagedArray<any>(
883
+ `/repos/${this.owner}/${this.repo}/pulls?state=open&per_page=100`,
884
+ );
885
+ // A PR past the bound would silently miss every head refresh: a partial
886
+ // list fails the read instead.
887
+ if (truncated) throw new Error("open PRs exceeded the pagination bound");
888
+ // An open PR is not merged; the list endpoint does not carry `merged`.
889
+ return prs.map((pr) => toPrRef(pr, false));
890
+ }
891
+
892
+ async getRequestedReviewers(n: number): Promise<RequestedReviewers> {
893
+ const res = await this.request<{
894
+ users: Array<{ login: string }>;
895
+ teams: Array<{ slug: string }>;
896
+ }>("GET", `/repos/${this.owner}/${this.repo}/pulls/${n}/requested_reviewers`);
897
+ return { users: res.users.map((u) => u.login), teams: res.teams.map((t) => t.slug) };
898
+ }
899
+
900
+ /** Every file GitHub will list for the PR (it serves 3,000 at most). A list
901
+ * that reaches that cap is flagged `truncated`: it is a floor, and ownership
902
+ * decisions made off it must not treat a missing file as not-the-PR's. */
903
+ async listPrFiles(n: number): Promise<FileList> {
904
+ const { rows, truncated } = await this.pagedArray<any>(
905
+ `/repos/${this.owner}/${this.repo}/pulls/${n}/files?per_page=100`,
906
+ PR_FILES_MAX_PAGES,
907
+ );
908
+ const out: FileList = rows.map(fileEntry);
909
+ if (truncated) out.truncated = true;
910
+ return out;
911
+ }
912
+
913
+ /**
914
+ * Files a commit range changed (`GET /compare/<base>...<head>`) — the delta
915
+ * source for the post-approval recheck. Read-only. GitHub serves
916
+ * the files on the FIRST page only (later pages page the commits) and caps
917
+ * them at COMPARE_FILES_CAP, so a list that long is flagged `truncated`: the
918
+ * classifier must not read an unseen remainder as "nothing PR-owned".
919
+ * A behind/diverged comparison is merge-base-relative rather than the two
920
+ * reviewed trees; it also cannot prove an empty post-approval delta.
921
+ */
922
+ async compareFiles(base: string, head: string): Promise<FileList> {
923
+ const res = await this.request<{ files?: any[]; status?: string }>(
924
+ "GET",
925
+ `/repos/${this.owner}/${this.repo}/compare/${encodeURIComponent(base)}...${encodeURIComponent(head)}?per_page=100&page=1`,
926
+ );
927
+ const files = res.files ?? [];
928
+ const out: FileList = files.map(fileEntry);
929
+ if (files.length >= COMPARE_FILES_CAP || res.status === "behind" || res.status === "diverged")
930
+ out.truncated = true;
931
+ return out;
932
+ }
933
+
934
+ /**
935
+ * Checks at a ref — check-runs plus the legacy commit statuses, since a repo
936
+ * can gate on either (guard 4). Read-only: CI is consulted, never
937
+ * triggered or re-run. Both endpoints are paginated the way listPrFiles is:
938
+ * guard 4 classifies what it is handed, so a failing or pending check past
939
+ * the first 100 rows must reach it, or a truncated list reads green.
940
+ */
941
+ async listChecks(ref: string): Promise<CheckSummary[]> {
942
+ const out: CheckSummary[] = [];
943
+ const enc = encodeURIComponent(ref);
944
+ const runs = await this.completeCheckPages<any>(
945
+ `/repos/${this.owner}/${this.repo}/commits/${enc}/check-runs`,
946
+ "check_runs",
947
+ );
948
+ for (const r of runs)
949
+ out.push({
950
+ name: r.name ?? "check",
951
+ source: "check_run",
952
+ status: r.status ?? "",
953
+ conclusion: r.conclusion ?? null,
954
+ started_at: r.started_at ?? null,
955
+ });
956
+ const statuses = await this.completeCheckPages<any>(
957
+ `/repos/${this.owner}/${this.repo}/commits/${enc}/status`,
958
+ "statuses",
959
+ );
960
+ for (const s of statuses)
961
+ out.push({
962
+ name: s.context ?? "status",
963
+ source: "status",
964
+ status: s.state === "pending" ? "pending" : "completed",
965
+ conclusion: s.state === "pending" ? null : s.state === "success" ? "success" : "failure",
966
+ });
967
+ return out;
968
+ }
969
+
970
+ /** Complete CI evidence, or an unreadable result that callers classify unknown. */
971
+ private async completeCheckPages<T>(path: string, field: string): Promise<T[]> {
972
+ const rows: T[] = [];
973
+ let expected: number | undefined;
974
+ for (let page = 1; page <= 10; page++) {
975
+ const result = await this.request<Record<string, unknown>>(
976
+ "GET",
977
+ `${path}?per_page=100&page=${page}`,
978
+ );
979
+ const count = result.total_count;
980
+ if (count !== undefined) {
981
+ if (typeof count !== "number" || !Number.isInteger(count) || count < 0)
982
+ throw new Error(`unreadable ${field} total_count`);
983
+ if (expected !== undefined && expected !== count)
984
+ throw new Error(`incomplete ${field}: count changed while reading`);
985
+ expected = count;
986
+ }
987
+ const batch = result[field];
988
+ if (!Array.isArray(batch)) throw new Error(`unreadable ${field} page`);
989
+ rows.push(...(batch as T[]));
990
+ if (expected !== undefined && rows.length > expected)
991
+ throw new Error(`incomplete ${field}: rows exceed advertised count`);
992
+ if (batch.length < 100 || (expected !== undefined && rows.length === expected)) {
993
+ if (expected !== undefined && rows.length !== expected)
994
+ throw new Error(`incomplete ${field}: advertised count not reached`);
995
+ return rows;
996
+ }
997
+ }
998
+ throw new Error(`incomplete ${field}: page limit reached`);
999
+ }
1000
+
1001
+ async listReviews(
1002
+ n: number,
1003
+ ): Promise<Array<{ id: number; user: string; state: string; commit_id: string }>> {
1004
+ // Every page, with no page bound: reviews are oldest-first and every
1005
+ // thread reply is one, so on a long PR the human approval at the head is
1006
+ // exactly what a bounded read misses — and guard 1 would then not soften
1007
+ // over it. A short page ends the read, as for listReviewsFull.
1008
+ return (await this.listReviewsFull(n)).map((r) => ({
1009
+ id: r.id,
1010
+ user: r.user,
1011
+ state: r.state,
1012
+ commit_id: r.commit_id,
1013
+ }));
1014
+ }
1015
+
1016
+ /** Full review rows (bodies included) — read-only, for compare and
1017
+ * posting-marker reconciliation. Paginated so long-lived PRs cannot hide the
1018
+ * accepted review beyond the first 100 rows. */
1019
+ async listReviewsFull(n: number): Promise<ReviewFull[]> {
1020
+ const { rows } = await this.pagedArray<any>(
1021
+ `/repos/${this.owner}/${this.repo}/pulls/${n}/reviews?per_page=100`,
1022
+ Number.POSITIVE_INFINITY,
1023
+ );
1024
+ return rows.map((r) => ({
1025
+ id: r.id,
1026
+ user: r.user?.login ?? "",
1027
+ user_type: r.user?.type ?? "User",
1028
+ state: r.state,
1029
+ body: r.body ?? "",
1030
+ commit_id: r.commit_id,
1031
+ submitted_at: r.submitted_at ?? null,
1032
+ }));
1033
+ }
1034
+
1035
+ /** Top-level PR conversation comments — read-only, the feedback capture
1036
+ * sweep's third source (the issues API is where GitHub keeps them). */
1037
+ async listIssueComments(n: number): Promise<IssueCommentFull[]> {
1038
+ const { rows: res, truncated } = await this.pagedArray<any>(
1039
+ `/repos/${this.owner}/${this.repo}/issues/${n}/comments?per_page=100`,
1040
+ );
1041
+ // A conversation retry must not read a partial list as proof that its
1042
+ // earlier POST did not land. Capture callers already treat failure as unknown.
1043
+ if (truncated) throw new Error(`issue comments on #${n} exceeded the pagination bound`);
1044
+ return res.map((c) => ({
1045
+ id: c.id,
1046
+ user: c.user?.login ?? "",
1047
+ user_type: c.user?.type ?? "User",
1048
+ body: c.body ?? "",
1049
+ created_at: c.created_at ?? null,
1050
+ }));
1051
+ }
1052
+
1053
+ /** Every inline review comment, oldest first — read-only: the reconcile
1054
+ * comment sweep, the case writer's backfill, the compare harness. A list
1055
+ * past the pagination bound throws rather than answer its oldest part as
1056
+ * the whole: a sweep that read a partial list would stamp its watermark and
1057
+ * never look for the newer comments again. */
1058
+ async listReviewCommentsFull(n: number): Promise<ReviewCommentFull[]> {
1059
+ const { rows: res, truncated } = await this.pagedArray<any>(
1060
+ `/repos/${this.owner}/${this.repo}/pulls/${n}/comments?per_page=100`,
1061
+ );
1062
+ if (truncated) throw new Error(`inline comments on #${n} exceeded the pagination bound`);
1063
+ return res.map(toReviewCommentFull);
1064
+ }
1065
+
1066
+ /** The NEWEST inline review comments, newest first — read-only. The
1067
+ * ping-pong retry path reads this to learn whether a reply whose POST
1068
+ * response was lost actually landed, so the newest comments are the ones
1069
+ * it needs. With `newerThan`, pages continue until one reaches a comment at
1070
+ * or below that id (ids grow with creation, so every comment created after
1071
+ * it has then been read) — or throw past `RECENT_COMMENTS_MAX_PAGES`, never
1072
+ * answer a window that may have missed the reply. Without it, one page. */
1073
+ async listRecentReviewComments(n: number, newerThan?: number): Promise<ReviewCommentFull[]> {
1074
+ const out: ReviewCommentFull[] = [];
1075
+ for (let page = 1; page <= RECENT_COMMENTS_MAX_PAGES; page++) {
1076
+ const res = await this.request<any[]>(
1077
+ "GET",
1078
+ `/repos/${this.owner}/${this.repo}/pulls/${n}/comments?sort=created&direction=desc&per_page=100&page=${page}`,
1079
+ );
1080
+ out.push(...res.map(toReviewCommentFull));
1081
+ if (newerThan === undefined || res.length < 100) return out;
1082
+ if (res.some((c) => typeof c.id === "number" && c.id <= newerThan)) return out;
1083
+ }
1084
+ throw new Error(
1085
+ `inline comments on #${n} newer than ${newerThan} exceeded ${RECENT_COMMENTS_MAX_PAGES} pages`,
1086
+ );
1087
+ }
1088
+
1089
+ /** Review comments the bot authored on a PR — the registry backfill source.
1090
+ * Read newest first, so a PR past the page bound loses its OLDEST comments
1091
+ * (registered by earlier passes), never the newest. */
1092
+ async listBotReviewComments(n: number): Promise<number[]> {
1093
+ const { rows: res } = await this.pagedArray<any>(
1094
+ `/repos/${this.owner}/${this.repo}/pulls/${n}/comments?sort=created&direction=desc&per_page=100`,
1095
+ );
1096
+ return res.filter((c) => c.user?.login === this.cfg.github.bot_login).map((c) => c.id);
1097
+ }
1098
+
1099
+ // ---- staleness scan (GraphQL) -------------------------------------------
1100
+
1101
+ /** Open PRs whose latest bot APPROVED review no longer matches the head:
1102
+ * `first: 100`, `UPDATED_AT DESC`, review author the configured
1103
+ * `[github] bot_login`. */
1104
+ async stalenessScan(): Promise<StalePr[]> {
1105
+ const q = `repo:${this.cfg.github.repo} is:pr is:open sort:updated-desc`;
1106
+ const data = await this.graphql<{
1107
+ search: {
1108
+ nodes: Array<{
1109
+ number?: number;
1110
+ headRefOid?: string;
1111
+ reviews?: {
1112
+ nodes: Array<{ state: string; submittedAt: string; commit: { oid: string } | null }>;
1113
+ };
1114
+ }>;
1115
+ };
1116
+ }>(
1117
+ `query ($q: String!, $author: String!) {
1118
+ search(query: $q, type: ISSUE, first: 100) {
1119
+ nodes {
1120
+ ... on PullRequest {
1121
+ number
1122
+ headRefOid
1123
+ reviews(author: $author, states: [APPROVED], last: 1) {
1124
+ nodes { state submittedAt commit { oid } }
1125
+ }
1126
+ }
1127
+ }
1128
+ }
1129
+ }`,
1130
+ { q, author: this.cfg.github.bot_login },
1131
+ );
1132
+ const stale: StalePr[] = [];
1133
+ for (const node of data.search.nodes) {
1134
+ if (!node.number || !node.headRefOid) continue;
1135
+ const review = node.reviews?.nodes?.[0];
1136
+ if (!review?.commit) continue;
1137
+ if (review.commit.oid !== node.headRefOid) {
1138
+ stale.push({
1139
+ number: node.number,
1140
+ head_sha: node.headRefOid,
1141
+ approved_sha: review.commit.oid,
1142
+ approved_at: review.submittedAt,
1143
+ });
1144
+ }
1145
+ }
1146
+ return stale;
1147
+ }
1148
+
1149
+ // ---- webhook redeliveries (JWT) ----------------------------------------
1150
+
1151
+ async listWebhookDeliveries(
1152
+ perPage = 100,
1153
+ ): Promise<Array<{ id: number; guid: string; status: string; delivered_at: string }>> {
1154
+ return this.request("GET", `/app/hook/deliveries?per_page=${perPage}`, undefined, "jwt");
1155
+ }
1156
+
1157
+ // ---- the App's own webhook configuration -------------------------------
1158
+
1159
+ /** The App's own webhook config — the self-heal's read-back source. Direct
1160
+ * JWT read, like the deliveries listing above. */
1161
+ async getAppWebhookConfig(): Promise<AppWebhookConfig> {
1162
+ const res = await this.request<{ url?: string; content_type?: string; insecure_ssl?: string }>(
1163
+ "GET",
1164
+ "/app/hook/config",
1165
+ undefined,
1166
+ "jwt",
1167
+ );
1168
+ return {
1169
+ url: res.url ?? "",
1170
+ content_type: res.content_type ?? "",
1171
+ insecure_ssl: res.insecure_ssl ?? "",
1172
+ };
1173
+ }
1174
+
1175
+ /**
1176
+ * Re-point the App's OWN webhook URL (`PATCH /app/hook/config`, JWT-authed).
1177
+ * The one member of the app-self-config gate class (AGENTS.md invariant 6): it
1178
+ * touches only the App's own
1179
+ * configuration, never the reviewed repo, and the gate executes it for real
1180
+ * even in dry-run — loudly logged, with the note naming old and new URL.
1181
+ */
1182
+ async updateOwnWebhookUrl(newUrl: string, oldUrl: string): Promise<GateResult> {
1183
+ return this.gate.execute({
1184
+ kind: "update_own_webhook_url",
1185
+ method: "PATCH",
1186
+ path: "/app/hook/config",
1187
+ body: { url: newUrl },
1188
+ note: `re-point own App webhook URL ${oldUrl || "(unknown)"} → ${newUrl}`,
1189
+ });
1190
+ }
1191
+
1192
+ // ---- mutations (all through the gate) ----------------------------------
1193
+
1194
+ async addLabel(n: number, label: string): Promise<GateResult> {
1195
+ return this.gate.execute({
1196
+ kind: "add_label",
1197
+ method: "POST",
1198
+ path: `/repos/${this.owner}/${this.repo}/issues/${n}/labels`,
1199
+ body: { labels: [label] },
1200
+ note: `add label "${label}" to #${n}`,
1201
+ pr: n,
1202
+ });
1203
+ }
1204
+
1205
+ async removeLabel(n: number, label: string): Promise<GateResult> {
1206
+ try {
1207
+ return await this.gate.execute({
1208
+ kind: "remove_label",
1209
+ method: "DELETE",
1210
+ path: `/repos/${this.owner}/${this.repo}/issues/${n}/labels/${encodeURIComponent(label)}`,
1211
+ note: `remove label "${label}" from #${n}`,
1212
+ pr: n,
1213
+ });
1214
+ } catch (err) {
1215
+ // Removing an already-absent label is the outcome we wanted — GitHub's
1216
+ // 404 ("Label does not exist") is idempotent success, not an error —
1217
+ // e.g. a label consumed by an earlier failed-dispatch cycle.
1218
+ if (err instanceof GitHubRequestError && err.status === 404)
1219
+ return { executed: false, response: { already_absent: label } };
1220
+ throw err;
1221
+ }
1222
+ }
1223
+
1224
+ /** Consume the team trigger: DELETE with the JSON body, then verify. */
1225
+ async deleteTeamReviewRequest(n: number): Promise<GateResult> {
1226
+ const teamSlug = reviewTeamSlug(this.cfg.github.review_team);
1227
+ const result = await this.gate.execute({
1228
+ kind: "delete_team_review_request",
1229
+ method: "DELETE",
1230
+ path: `/repos/${this.owner}/${this.repo}/pulls/${n}/requested_reviewers`,
1231
+ body: { reviewers: [], team_reviewers: [teamSlug] },
1232
+ note: `clear team review request (${teamSlug}) on #${n}`,
1233
+ pr: n,
1234
+ });
1235
+ if (result.executed) {
1236
+ const after = await this.getRequestedReviewers(n);
1237
+ if (after.teams.includes(teamSlug)) throw new Error(`team request on #${n} survived DELETE`);
1238
+ }
1239
+ return result;
1240
+ }
1241
+
1242
+ /**
1243
+ * Put named people in this PR's review-requested queue — the second
1244
+ * delivery shape of the optional escalation feature (the never-hand-to-a-human rule), an
1245
+ * ordinary gated mutation.
1246
+ *
1247
+ * Three things this is not:
1248
+ * * It is not a way to hand the review back. `src/escalation.ts` calls it
1249
+ * only after the round's verdict has been decided and applied, and only
1250
+ * alongside a comment that names the ONE thing being asked about —
1251
+ * GitHub words the queue entry "review this pull request", which is
1252
+ * exactly the framing the comment exists to correct.
1253
+ * * It is not a dry-run exception. `request_reviewer` is an ordinary
1254
+ * `MutationKind`: suppressed to `state/dryrun/` like everything else,
1255
+ * `forceLive` throws on it, and it is not in `APP_SELF_CONFIG_KINDS`.
1256
+ * * It is not review-dismissing. Rule 2 is untouched; requesting a review
1257
+ * adds a reviewer and changes no existing review.
1258
+ *
1259
+ * Needs no new App permission: this is a POST on the same
1260
+ * `/pulls/{n}/requested_reviewers` route the team-request DELETE already
1261
+ * uses, and both are Pull requests: write, which the App manifest grants.
1262
+ */
1263
+ async requestEscalationReview(n: number, logins: string[]): Promise<GateResult> {
1264
+ const reviewers = logins.map((l) => l.trim().replace(/^@/, "")).filter(Boolean);
1265
+ return this.gate.execute({
1266
+ kind: "request_reviewer",
1267
+ method: "POST",
1268
+ path: `/repos/${this.owner}/${this.repo}/pulls/${n}/requested_reviewers`,
1269
+ body: { reviewers },
1270
+ note: `escalation: request review from ${reviewers.map((r) => `@${r}`).join(", ")} on #${n}`,
1271
+ pr: n,
1272
+ });
1273
+ }
1274
+
1275
+ async createReview(
1276
+ n: number,
1277
+ payload: {
1278
+ commit_id: string;
1279
+ event: "APPROVE" | "REQUEST_CHANGES" | "COMMENT";
1280
+ body: string;
1281
+ comments?: unknown[];
1282
+ },
1283
+ options?: { purpose?: ReviewPurpose; postingRetry?: string },
1284
+ ): Promise<GateResult> {
1285
+ return this.gate.execute({
1286
+ kind: "create_review",
1287
+ method: "POST",
1288
+ path: `/repos/${this.owner}/${this.repo}/pulls/${n}/reviews`,
1289
+ body: payload,
1290
+ note: `post ${payload.event} review on #${n}${options?.postingRetry ? " (deferred delivery)" : ""}`,
1291
+ pr: n,
1292
+ reviewPurpose: options?.purpose,
1293
+ ...(options?.postingRetry !== undefined ? { postingRetry: options.postingRetry } : {}),
1294
+ });
1295
+ }
1296
+
1297
+ /** A claimed PR-level author response; ordinary posting guards and dry-run apply. */
1298
+ async createIssueComment(n: number, body: string): Promise<GateResult> {
1299
+ return this.gate.execute({
1300
+ kind: "create_issue_comment",
1301
+ method: "POST",
1302
+ path: `/repos/${this.owner}/${this.repo}/issues/${n}/comments`,
1303
+ body: { body },
1304
+ note: `answer the author in PR #${n}'s conversation`,
1305
+ pr: n,
1306
+ });
1307
+ }
1308
+
1309
+ async createReviewCommentReply(n: number, commentId: number, body: string): Promise<GateResult> {
1310
+ return this.gate.execute({
1311
+ kind: "create_review_comment_reply",
1312
+ method: "POST",
1313
+ path: `/repos/${this.owner}/${this.repo}/pulls/${n}/comments/${commentId}/replies`,
1314
+ body: { body },
1315
+ note: `reply in thread ${commentId} on #${n}`,
1316
+ pr: n,
1317
+ });
1318
+ }
1319
+
1320
+ /** Resolve one inline review thread, but only after a read verifies that the
1321
+ * numeric root comment belongs to this PR and was authored by this app.
1322
+ * There is intentionally no generic resolve-thread mutation surface. */
1323
+ async resolveOwnReviewThread(
1324
+ n: number,
1325
+ rootCommentId: number,
1326
+ ): Promise<{
1327
+ skipped: "already-resolved" | "check-this-human-only" | "deferred-human-only" | null;
1328
+ result?: GateResult;
1329
+ }> {
1330
+ const query = `query ($owner: String!, $repo: String!, $number: Int!, $after: String) {
1331
+ repository(owner: $owner, name: $repo) {
1332
+ pullRequest(number: $number) {
1333
+ reviewThreads(first: 100, after: $after) {
1334
+ nodes { id isResolved comments(first: 1) { nodes { databaseId author { login } body } } }
1335
+ pageInfo { hasNextPage endCursor }
1336
+ }
1337
+ }
1338
+ }
1339
+ }`;
1340
+ type ThreadPage = {
1341
+ repository?: {
1342
+ pullRequest?: {
1343
+ reviewThreads?: {
1344
+ nodes: Array<{
1345
+ id: string;
1346
+ isResolved: boolean;
1347
+ comments: {
1348
+ nodes: Array<{ databaseId: number; author?: { login?: string }; body?: string }>;
1349
+ };
1350
+ }>;
1351
+ pageInfo: { hasNextPage: boolean; endCursor: string | null };
1352
+ };
1353
+ };
1354
+ };
1355
+ };
1356
+ let after: string | null = null;
1357
+ let found: { id: string; isResolved: boolean; author: string; body: string } | undefined;
1358
+ do {
1359
+ const data: ThreadPage = await this.graphql(query, {
1360
+ owner: this.owner,
1361
+ repo: this.repo,
1362
+ number: n,
1363
+ after,
1364
+ });
1365
+ const threads = data.repository?.pullRequest?.reviewThreads;
1366
+ if (!threads) throw new Error(`review threads unavailable for #${n}`);
1367
+ for (const thread of threads.nodes) {
1368
+ const root = thread.comments.nodes[0];
1369
+ if (root?.databaseId === rootCommentId) {
1370
+ found = {
1371
+ id: thread.id,
1372
+ isResolved: thread.isResolved,
1373
+ author: root.author?.login ?? "",
1374
+ body: root.body ?? "",
1375
+ };
1376
+ break;
1377
+ }
1378
+ }
1379
+ if (found || !threads.pageInfo.hasNextPage) break;
1380
+ after = threads.pageInfo.endCursor;
1381
+ } while (after);
1382
+
1383
+ if (!found)
1384
+ throw new ThreadResolutionRefusal(
1385
+ `comment ${rootCommentId} is not a review-thread root on #${n}`,
1386
+ );
1387
+ // GitHub exposes the same App actor with two spellings: review-author
1388
+ // filters use the `[bot]` login, while review-thread GraphQL actors use the
1389
+ // bare App slug. Both values are independently configured; recognize
1390
+ // exactly that pair here and nowhere else. Every unrelated identity stays
1391
+ // structurally refused.
1392
+ const bareAppActor = appActorAlias(this.cfg.github.bot_login, this.cfg.github.app_slug);
1393
+ const ownActors = new Set(
1394
+ [this.cfg.github.bot_login, bareAppActor].filter((v): v is string => v !== undefined),
1395
+ );
1396
+ if (!ownActors.has(found.author)) {
1397
+ throw new ThreadResolutionRefusal(
1398
+ `refusing to resolve thread ${rootCommentId} on #${n}: root author is ${found.author || "unknown"}`,
1399
+ );
1400
+ }
1401
+ // A "[check this]" thread exists to force a human's eyes before merge (repo
1402
+ // rule: every conversation must be resolved). The reviewer never resolves
1403
+ // one — the author or another human only.
1404
+ if (CHECK_THIS_RE.test(found.body)) return { skipped: "check-this-human-only" };
1405
+ // A "[deferred]" thread says this finding is real and this PR is not where
1406
+ // it gets fixed. If the author ignores the drafted ticket, the reviewer
1407
+ // must not be able to tidy the thread away — that would make a deferral
1408
+ // free again, which is the exact thing this mechanism exists to stop. So it
1409
+ // takes the same structural refusal as "[check this]", and the repo's merge
1410
+ // rule (every conversation resolved) puts a person in front of it. Distinct
1411
+ // marker, distinct reason string: the two are never conflated, here or
1412
+ // anywhere.
1413
+ if (DEFERRED_RE.test(found.body)) return { skipped: "deferred-human-only" };
1414
+ // An "[ask your human]" thread is deliberately not refused here. It is the
1415
+ // sibling marker, not a second spelling of the one above: it addresses the
1416
+ // PR author's AI implementer ("do not answer this yourself — ask a person
1417
+ // and bring the answer back"), so what a human owes the thread is the
1418
+ // answer, not the resolution. Making it human-only would force a person
1419
+ // into every such thread, and the ordinary path already lands in the right
1420
+ // place — ping-pong resolves such a thread only after the author replied
1421
+ // and the session judged the reply settles it, and the prior-finding
1422
+ // resolver only for a finding a later round reported fixed or obsolete.
1423
+ // Rule 2 is
1424
+ // unaffected: this resolves the bot's OWN finding thread and never
1425
+ // dismisses or edits any review, anyone's.
1426
+ if (found.isResolved) return { skipped: "already-resolved" };
1427
+
1428
+ const mutation = `mutation ($threadId: ID!) {
1429
+ resolveReviewThread(input: {threadId: $threadId}) { thread { id isResolved } }
1430
+ }`;
1431
+ const result = await this.gate.execute({
1432
+ kind: "resolve_own_review_thread",
1433
+ method: "POST",
1434
+ path: "/graphql",
1435
+ body: { query: mutation, variables: { threadId: found.id } },
1436
+ note: `resolve own review thread ${rootCommentId} on #${n}`,
1437
+ pr: n,
1438
+ });
1439
+ return { skipped: null, result };
1440
+ }
1441
+
1442
+ async createIssue(title: string, body: string, labels: string[]): Promise<GateResult> {
1443
+ return this.gate.execute({
1444
+ kind: "create_issue",
1445
+ method: "POST",
1446
+ path: `/repos/${this.owner}/${this.repo}/issues`,
1447
+ body: { title, body, labels },
1448
+ note: `file issue "${title}"`,
1449
+ });
1450
+ }
1451
+
1452
+ /** Every open issue carrying the configured label, whoever filed it. */
1453
+ async listOpenLabelledIssues(): Promise<Array<{ number: number; title: string }>> {
1454
+ const q = `repo:${this.cfg.github.repo} is:open is:issue label:"${this.cfg.github.issue_label}"`;
1455
+ return (await this.searchIssues(q)).map((i) => ({ number: i.number, title: i.title }));
1456
+ }
1457
+
1458
+ /** Recover only an exact daemon marker posted by our configured identity.
1459
+ * Absence is not permission to repeat an uncertain creation. */
1460
+ async findFiledItem(
1461
+ kind: "issue" | "improvement_pr",
1462
+ marker: string,
1463
+ branch?: { head: string; base: string },
1464
+ ): Promise<number | null> {
1465
+ const path =
1466
+ kind === "issue"
1467
+ ? `/repos/${this.owner}/${this.repo}/issues?state=all&per_page=100`
1468
+ : `/repos/${this.owner}/${this.repo}/pulls?state=all&per_page=100&head=${encodeURIComponent(`${this.owner}:${branch!.head}`)}&base=${encodeURIComponent(branch!.base)}`;
1469
+ const { rows, truncated } = await this.pagedArray<any>(path);
1470
+ if (truncated) throw new Error("filing reconciliation exceeded pagination bound");
1471
+ const matches = rows.filter(
1472
+ (row) =>
1473
+ row.user?.login === this.cfg.github.bot_login &&
1474
+ typeof row.body === "string" &&
1475
+ row.body.split(/\r?\n/).includes(marker) &&
1476
+ (kind === "issue"
1477
+ ? !row.pull_request
1478
+ : row.head?.ref === branch?.head && row.base?.ref === branch?.base),
1479
+ );
1480
+ if (matches.length > 1) throw new Error("ambiguous filing markers");
1481
+ if (matches.length === 0) return null;
1482
+ const number: unknown = matches[0].number;
1483
+ if (typeof number !== "number" || !Number.isSafeInteger(number) || number <= 0)
1484
+ throw new Error("invalid filing receipt number");
1485
+ return number;
1486
+ }
1487
+
1488
+ async createPull(head: string, base: string, title: string, body: string): Promise<GateResult> {
1489
+ return this.gate.execute({
1490
+ kind: "create_pull",
1491
+ method: "POST",
1492
+ path: `/repos/${this.owner}/${this.repo}/pulls`,
1493
+ body: { head, base, title, body },
1494
+ note: `open PR ${head} → ${base}: "${title}"`,
1495
+ });
1496
+ }
1497
+ }