@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/wire.ts ADDED
@@ -0,0 +1,2153 @@
1
+ // Production assembly of quickreviewd: createDaemon() with the real pi session
2
+ // manager, the round-template renderer, the agents build step, the EOD timer,
3
+ // the overseer command executor, and the weekly overseer rotation. `npm run
4
+ // daemon` runs this; createDaemon() alone (src/daemon.ts) stays the
5
+ // stub-wired, test-friendly core.
6
+ import { appendFileSync, mkdirSync, readFileSync } from "node:fs";
7
+ import { homedir } from "node:os";
8
+ import { join } from "node:path";
9
+ import {
10
+ fsSecretReader,
11
+ ingressHostnameFile,
12
+ liveConfigPath,
13
+ loadConfig,
14
+ openVikingBaseUrl,
15
+ sandboxRoot,
16
+ slackTokenPath,
17
+ validateSlackConfig,
18
+ type Config,
19
+ type SecretReader,
20
+ } from "./config.js";
21
+ import {
22
+ CommandWatcher,
23
+ processOperatorSeal,
24
+ type CommandDeps,
25
+ type CommandWatcherOptions,
26
+ } from "./commands.js";
27
+ import { drainLever, pauseLever, resumeLever, type LeverDeps } from "./levers.js";
28
+ import { CaseWriter } from "./cases.js";
29
+ import { Archiver } from "./archive/archiver.js";
30
+ import { piGlobalSessionsDir } from "./archive/piglobal.js";
31
+ import { runDirOf, toolUidSplit } from "./ipc/capability.js";
32
+ import { PermitBroker } from "./ipc/permits.js";
33
+ import { LoginRefreshSupervisor } from "./loginrefresh/supervisor.js";
34
+ import { pendingArchiveStatus } from "./archive/status.js";
35
+ import { Learner, learnerKnobsOf } from "./learner/learner.js";
36
+ import { renderNoPacketSection } from "./learner/packet.js";
37
+ import type { CurationReceipt } from "./learner/receipt.js";
38
+ import { LessonLedger } from "./lessonledger.js";
39
+ import { RoundLessonSelector, type RoundLessonHook } from "./lessonselect.js";
40
+ import { lessonDeliveryStatus } from "./lessonshadow.js";
41
+ import { RoundMemoryActivity } from "./memory/roundactivity.js";
42
+ import type { LessonDeliveryStatus } from "./memory/shadowstatus.js";
43
+ import { lessonFindClient, openVikingLessonEngine } from "./openviking/lessonfind.js";
44
+ import { createDaemon, type Daemon } from "./daemon.js";
45
+ import { reviewsInFlight } from "./dispatch.js";
46
+ import { eventLoopPolicy } from "./eventloop.js";
47
+ import { digestWritten, eodDigestContext, latestDigestHeadline } from "./digest.js";
48
+ import { startHeartbeat, type HeartbeatTimer } from "./anomalies.js";
49
+ import { CompanionInbox, type InboxWriter } from "./companioninbox.js";
50
+ import { opsDay, promptReader, startEodTimer, type EodTimer } from "./eod.js";
51
+ import { MutationGate, type MutationIntent } from "@quickchatai/pi-quickreview/src/gate.js";
52
+ import { GitHubAdapter } from "./github.js";
53
+ import { priorSidecarPath } from "@quickchatai/pi-quickreview/src/priorfindings.js";
54
+ import { completeRecordedFindings } from "./recordedfindings.js";
55
+ import { makePostingGuard } from "@quickchatai/pi-quickreview/src/guards.js";
56
+ import { classifyCi, ciBlocksApproval } from "@quickchatai/pi-quickreview/src/cigate.js";
57
+ import { TokenMinter } from "@quickchatai/pi-quickreview/src/githubAuth.js";
58
+ import {
59
+ FeedbackEngine,
60
+ FeedbackMemoryPump,
61
+ dayScopedRebuild,
62
+ feedbackDayAddendum,
63
+ ledgerFeedbackSignal,
64
+ type FeedbackEvent,
65
+ } from "./feedback.js";
66
+ import {
67
+ PASS_ADDENDA,
68
+ buildAgents,
69
+ configAddenda,
70
+ lessonAddendaFor,
71
+ identityVars,
72
+ } from "@quickchatai/pi-quickreview/src/include.js";
73
+ import { ecosystemFor } from "@quickchatai/pi-quickreview/src/ecosystem.js";
74
+ import { lensTuningAddenda, roundAgentModels } from "@quickchatai/pi-quickreview/src/lenstuning.js";
75
+ import {
76
+ applyProfileOverlays,
77
+ loadProfileAddenda,
78
+ loadProfileEcosystem,
79
+ loadProfileHeuristics,
80
+ } from "@quickchatai/pi-quickreview/src/profiles.js";
81
+ import { postingPolicyAddenda } from "@quickchatai/pi-quickreview/src/postingpolicy.js";
82
+ import {
83
+ prepareDependencySources,
84
+ renderDependencyNote,
85
+ } from "@quickchatai/pi-quickreview/src/depsources.js";
86
+ import { fileImprovementPr, fileIssue, gitRunner, type FileOutcome } from "./improvements.js";
87
+ import { homeProfileLookup } from "./install.js";
88
+ import { hostFacts, type HostFacts, type RunHostStatus } from "./runhost.js";
89
+ import {
90
+ EMBEDDER_GATE_MAX_WAIT_MS,
91
+ LocalEmbedderSupervisor,
92
+ OpenVikingClient,
93
+ OpenVikingSupervisor,
94
+ ReembedDriver,
95
+ keyFromFile,
96
+ memoryServing,
97
+ openVikingContextAssembler,
98
+ openVikingStore,
99
+ type OvRequestRecord,
100
+ } from "./openviking/index.js";
101
+ import {
102
+ OV_REVIEWER_KEY_SECRET,
103
+ keyChangeWatcher,
104
+ openVikingSessionLifecycle,
105
+ type OvSessionLifecycle,
106
+ readOvcliKey,
107
+ readRetiredUrisFile,
108
+ writeRecallHint,
109
+ writeRetiredUris,
110
+ } from "./memory/ovsession.js";
111
+ import type { ContextAssembler, RoundMemory, RoundMemorySeed } from "./memory/context.js";
112
+ import { MEMORY_LAYOUT_NOTE, ROUND_SEARCH_AREAS } from "./memory/layout.js";
113
+ import {
114
+ MemoryHealthMonitor,
115
+ disabledMemoryStatus,
116
+ pendingMemoryStatus,
117
+ type CaseWritesStatus,
118
+ type MemoryStatus,
119
+ } from "./memory/health.js";
120
+ import { credentialProbe } from "./openviking/probe.js";
121
+ import {
122
+ provisionReportPath,
123
+ raiseProvisionWake,
124
+ readProvisionReport,
125
+ } from "./openviking/provisionreport.js";
126
+ import { countedGapSink } from "./openviking/gapsink.js";
127
+ import { recoverPrForOperator } from "./operator-recovery.js";
128
+ import { createWorkerLiveness, inFlightPrs } from "./workerliveness.js";
129
+ import { resolveTerminalFailure } from "./terminal-failure.js";
130
+ import type { MemoryStore } from "./memory/store.js";
131
+ import { readPinnedFromSweep, startEodPinWatch, type PinnedSweepSource } from "./eodpinwatch.js";
132
+ import { reconcileOrphanedRounds } from "./reconcile.js";
133
+ import { PiSessionManager, prSessionName } from "./sessions.js";
134
+ import { SlackApiClient } from "./slack/api.js";
135
+ import { makeAnnouncer, type SlackAnnouncer } from "./slack/announce.js";
136
+ import { clearOriginRecords, OriginStore } from "./slack/origin.js";
137
+ import { makeSlackReply } from "./slack/reply.js";
138
+ import { SlackRouter } from "./slack/router.js";
139
+ import { SlackSocketClient } from "./slack/socket.js";
140
+ import type { SlackTransport } from "./slack/types.js";
141
+ import { StateStore, type RoundRow } from "./state.js";
142
+ import { deliverablePosting } from "./posting-outbox.js";
143
+ import { readPostingReceipt } from "./posting-receipt.js";
144
+ import type { RecheckOrder } from "./recheck.js";
145
+ import {
146
+ RECHECK_TEMPLATE_FILE,
147
+ renderRecheckPrompt,
148
+ renderRoundPrompt,
149
+ templateFileForOrder,
150
+ } from "@quickchatai/pi-quickreview/src/templates.js";
151
+ import { assertOrderFitsHost } from "@quickchatai/pi-quickreview/src/launchvocab.js";
152
+ import { nodeExec } from "./tmux.js";
153
+ import type { RoundOrder, SessionHooks } from "@quickchatai/pi-quickreview/src/types.js";
154
+ import { versionInfo } from "./version.js";
155
+ import { WebhookHealer } from "./webhookheal.js";
156
+ import { buildWorkerWorld } from "./workerworld.js";
157
+
158
+ export interface ProductionParts {
159
+ cfg: Config;
160
+ root: string;
161
+ minter: TokenMinter;
162
+ gate: MutationGate;
163
+ adapter: GitHubAdapter;
164
+ /** The tmux manager: it serves what has not moved to workers yet (the overseer) and
165
+ * lends the worker world its worktrees, session environment and path helpers. */
166
+ sessions: PiSessionManager;
167
+ /** The PR's raw-notes tier, which the worker host creates and deletes. */
168
+ ovSessions?: OvSessionLifecycle;
169
+ /** Late-bound observer for the raw-tier client's requests:
170
+ * buildParts builds that client before the memory health monitor exists,
171
+ * so createProductionDaemon points `sink` at the monitor once it does. */
172
+ memoryRequests?: { sink: ((record: OvRequestRecord) => void) | null };
173
+ /** Configured memory whose optional boot migration could not complete. */
174
+ memoryBootFailure?: string;
175
+ /** Where the daemon runs (src/runhost.ts): logged at start, and status.json
176
+ * `run_host`. Absent: not asked (tests, the CLI's own parts). */
177
+ runHost?: RunHostStatus;
178
+ /** Memory configured on but switched off for this host — no Docker for
179
+ * the sidecar outside the image. The config's `openviking.enabled` is
180
+ * already false; this is the reason the status block says. */
181
+ memoryOff?: string;
182
+ }
183
+
184
+ /** A configured engine refused before it could open partial migrated state. */
185
+ export function migrationMemoryStatus(reason: string, at: Date = new Date()): MemoryStatus {
186
+ return {
187
+ ...pendingMemoryStatus(at),
188
+ state: "down",
189
+ down_cause: "unanswered",
190
+ last_error: {
191
+ at: at.toISOString(),
192
+ op: "boot",
193
+ status: 0,
194
+ code: "migration_incomplete",
195
+ message: reason,
196
+ },
197
+ serving: { ok: false, since: at.toISOString(), reason },
198
+ };
199
+ }
200
+
201
+ export function buildParts(
202
+ root = sandboxRoot(),
203
+ loaded = loadConfig(),
204
+ /** The entry point's view of its host (`hostFacts`); absent, nothing about
205
+ * the host changes the config. */
206
+ host?: HostFacts,
207
+ ): ProductionParts {
208
+ // The profile's per-lens tuning overlays the parsed config once, here, so
209
+ // every consumer downstream (the plan, the agents build, the round row)
210
+ // reads the one resolved `cfg.lenses` — the profile wins over
211
+ // config.toml, the preset and the defaults. A malformed `[lenses]` table
212
+ // fails the boot loudly, like every other profile loader.
213
+ // The profile's `[posting]` policy rides the same overlay (packages/pi-plugin/src/postingpolicy.ts).
214
+ const overlaid = applyProfileOverlays(root, loaded, homeProfileLookup());
215
+ // A failed optional boot migration leaves its destination incomplete. No
216
+ // client, raw session or supervisor may open it until the next clean boot.
217
+ // Memory switched off for this host (no Docker outside the image) is the
218
+ // same clean off: no client, no supervisor, no probe to fail.
219
+ const memoryOff = overlaid.openviking.enabled ? host?.memoryOff : undefined;
220
+ const cfg =
221
+ process.env.QCR_OPENVIKING_BOOT_DISABLED === "1" || memoryOff !== undefined
222
+ ? { ...overlaid, openviking: { ...overlaid.openviking, enabled: false } }
223
+ : overlaid;
224
+ const memoryBootFailure =
225
+ process.env.QCR_OPENVIKING_BOOT_DISABLED === "1" && overlaid.openviking.enabled
226
+ ? "OpenViking state migration is incomplete; memory is unavailable for this boot — original files were retained; retry after correcting storage and restarting"
227
+ : undefined;
228
+ const minter = new TokenMinter(cfg.github.secrets_dir);
229
+ const gate: MutationGate = new MutationGate(
230
+ cfg.dry_run,
231
+ join(root, "state", "dryrun"),
232
+ (intent: MutationIntent) => adapter.executeMutation(intent),
233
+ );
234
+ const adapter: GitHubAdapter = new GitHubAdapter(cfg, minter, gate);
235
+ // The PR's raw memory tier: the daemon owns create/delete of the
236
+ // OpenViking session a PR's pi attaches to. Its own client, on the reviewer
237
+ // user key — never ADMIN/ROOT. Both directions are total, so a store that is
238
+ // down or unkeyed costs the raw tier and never a session.
239
+ const memoryRequests: NonNullable<ProductionParts["memoryRequests"]> = { sink: null };
240
+ const ovSessions = cfg.openviking.enabled
241
+ ? openVikingSessionLifecycle(
242
+ new OpenVikingClient({
243
+ ...ovConnection(cfg.openviking),
244
+ apiKey: keyFromFile(join(cfg.github.secrets_dir, OV_REVIEWER_KEY_SECRET)),
245
+ // Counted with the daemon's other requests.
246
+ onRequest: (r) => memoryRequests.sink?.(r),
247
+ }),
248
+ // The lifecycle logs a failed delete.
249
+ { log: (msg) => console.log(`[quickreviewd] ${msg}`) },
250
+ )
251
+ : undefined;
252
+ const sessions = new PiSessionManager({
253
+ cfg,
254
+ root,
255
+ ovSessions,
256
+ configPath: liveConfigPath(),
257
+ });
258
+ return {
259
+ cfg,
260
+ root,
261
+ minter,
262
+ gate,
263
+ adapter,
264
+ sessions,
265
+ ...(ovSessions ? { ovSessions } : {}),
266
+ memoryRequests,
267
+ ...(memoryBootFailure ? { memoryBootFailure } : {}),
268
+ ...(host ? { runHost: host.runHost } : {}),
269
+ ...(memoryOff !== undefined && memoryBootFailure === undefined ? { memoryOff } : {}),
270
+ };
271
+ }
272
+
273
+ // Exit-code checked, cwd mandatory: a runner that ignored git's exit or
274
+ // defaulted to the daemon's own cwd (a repo with no remote) would leave the
275
+ // improvement channel unable to push without saying so.
276
+ const gitRun = gitRunner(nodeExec);
277
+
278
+ /** The status.json a previous run left, or undefined (first boot, unreadable). */
279
+ function readPreviousStatus(path: string): string | undefined {
280
+ try {
281
+ return readFileSync(path, "utf8");
282
+ } catch {
283
+ return undefined;
284
+ }
285
+ }
286
+
287
+ /** package.json version for the `daemon.up` line; never worth failing a boot. */
288
+ function packageVersion(root: string): string | undefined {
289
+ const version = versionInfo(root).image.version;
290
+ return version === "unknown" ? undefined : version;
291
+ }
292
+
293
+ // ---- the Slack concierge ---------------------------------------------------
294
+ //
295
+ // Everything Slack is assembled here and nowhere else, because this is the one
296
+ // module allowed to read the two tokens off disk. Three properties the shape
297
+ // enforces rather than documents:
298
+ //
299
+ // * `[slack] enabled = false` ⇒ this function returns before it touches a
300
+ // secret reader, a socket or a clock. Nothing is constructed, so nothing
301
+ // can post.
302
+ // * A missing token, a malformed [slack] block or an auth.test that cannot
303
+ // name us is a hard boot error. A bot that cannot identify itself cannot
304
+ // filter its own echoes — running anyway is a loop risk, not a degraded
305
+ // mode.
306
+ // * Socket Mode is the only inbound transport. There is no HTTP route here
307
+ // and none in src/webhook.ts (tests/slack-no-route.test.ts makes that
308
+ // structural).
309
+
310
+ /** How often the duplicate-consumer check reads `hello.num_connections`. The
311
+ * socket exposes the count, not an event; `noteConnections` only fires on a
312
+ * CHANGE, so polling it is equivalent to hooking the frame. */
313
+ const SLACK_CONNECTIONS_TICK_MS = 60_000;
314
+
315
+ export interface SlackWiringOptions {
316
+ cfg: Config;
317
+ root: string;
318
+ store: StateStore;
319
+ /** The daemon's command handlers; the Slack dir gets these plus slackReply. */
320
+ commandDeps: CommandDeps;
321
+ /** The companion inbox — where ops signals go (the connection-count
322
+ * anomaly). Never the channel: v1 ships ops causes off. */
323
+ writeInbox: InboxWriter;
324
+ log: (msg: string, extra?: unknown) => void;
325
+ version?: string;
326
+ now?: () => Date;
327
+ /** Test seam: how a token file is read. Default is the shared fs reader. */
328
+ readSecret?: SecretReader;
329
+ /** Test seam: how the transport is built from the two tokens. `log` is the
330
+ * wiring's own sink, so the socket's connection diagnostics reach it. */
331
+ makeTransport?: (opts: {
332
+ cfg: Config;
333
+ botToken: string;
334
+ appToken: string;
335
+ log: (msg: string, extra?: unknown) => void;
336
+ }) => SlackTransport;
337
+ /** The previous run's `state/status.json`, read before this boot wrote its
338
+ * own first snapshot — the downtime `daemon.up` reports is measured from
339
+ * its heartbeat. Absent ⇒ the file is read at announce time. */
340
+ previousStatus?: string;
341
+ /**
342
+ * The origin store, when the caller already owns one. `createProductionDaemon`
343
+ * builds it before the daemon so the belt reads the overseer's accepted
344
+ * wakes. Omitted ⇒ built here (tests).
345
+ */
346
+ origins?: OriginStore;
347
+ }
348
+
349
+ /** The live Slack subsystem, as `onStop` needs to see it. */
350
+ export interface SlackRuntime {
351
+ announcer: SlackAnnouncer;
352
+ router: SlackRouter;
353
+ origins: OriginStore;
354
+ transport: SlackTransport;
355
+ /** The tier-checked `commands/slack/` watcher. */
356
+ commands: CommandWatcher;
357
+ /** `CommandDeps.slackReply` — also handed to the legacy-dir watcher. */
358
+ slackReply: NonNullable<CommandDeps["slackReply"]>;
359
+ stop(reason: string): Promise<void>;
360
+ }
361
+
362
+ /** The real transport: Web API out, Socket Mode in, composed into one object. */
363
+ export function defaultTransport(opts: {
364
+ cfg: Config;
365
+ botToken: string;
366
+ appToken: string;
367
+ log: (msg: string, extra?: unknown) => void;
368
+ }): SlackTransport {
369
+ const { cfg, log } = opts;
370
+ // Both transports log to the daemon log: an auth failure, a reconnect, a
371
+ // ping timeout or a dropped post is what an operator reads to tell why the
372
+ // concierge went quiet.
373
+ const api = new SlackApiClient({
374
+ botToken: opts.botToken,
375
+ postMinIntervalMs: cfg.slack.post_min_interval_ms,
376
+ userCacheTtlSec: cfg.slack.name_cache_ttl_s,
377
+ log: (message, meta) => log(`slack: ${message}`, meta),
378
+ });
379
+ const socket = new SlackSocketClient({
380
+ appToken: opts.appToken,
381
+ backoffMs: cfg.slack.reconnect_backoff_ms,
382
+ serverPingTimeoutMs: cfg.slack.server_ping_timeout_ms,
383
+ log: (message, meta) => log(`slack: ${message}`, meta),
384
+ });
385
+ return {
386
+ postMessage: (a) => api.postMessage(a),
387
+ updateMessage: (a) => api.updateMessage(a),
388
+ usersInfo: (id) => api.usersInfo(id),
389
+ authTest: () => api.authTest(),
390
+ pinsAdd: (a) => api.pinsAdd(a),
391
+ startSocket: (h) => socket.startSocket(h),
392
+ stopSocket: () => socket.stopSocket(),
393
+ get numConnections(): number {
394
+ return socket.numConnections;
395
+ },
396
+ };
397
+ }
398
+
399
+ /**
400
+ * Build + start the concierge. Returns undefined when Slack is off — callers
401
+ * pass that straight through, which is why nothing downstream needs a flag.
402
+ */
403
+ export async function startSlack(opts: SlackWiringOptions): Promise<SlackRuntime | undefined> {
404
+ const { cfg, root, store, log } = opts;
405
+ if (!cfg.slack.enabled) return undefined;
406
+ const now = opts.now ?? (() => new Date());
407
+ const readSecret = opts.readSecret ?? fsSecretReader;
408
+
409
+ // Fail loud, at boot, on a missing token or a half-filled [slack] block.
410
+ validateSlackConfig(cfg, { readSecret });
411
+ const botToken = readSecret(slackTokenPath(cfg, "bot"));
412
+ const appToken = readSecret(slackTokenPath(cfg, "app"));
413
+ if (!botToken || !appToken)
414
+ throw new Error("slack: token files vanished between validation and read");
415
+
416
+ const transport = (opts.makeTransport ?? defaultTransport)({ cfg, botToken, appToken, log });
417
+
418
+ // Who are we? Resolved once, and required: the router's echo filter is built
419
+ // on this identity, and a bot that cannot recognise its own messages will
420
+ // answer itself forever.
421
+ const identity = await transport.authTest();
422
+ if (!identity) {
423
+ throw new Error(
424
+ "slack: auth.test failed — refusing to start (a bot that cannot identify itself will answer its own echoes)",
425
+ );
426
+ }
427
+ log(`slack: connected as ${identity.user_id} in ${cfg.slack.channel}`);
428
+
429
+ const origins = opts.origins ?? new OriginStore(join(root, "state", "slack", "origin"), now);
430
+ const announcer = makeAnnouncer({
431
+ cfg,
432
+ store,
433
+ slack: transport,
434
+ now,
435
+ log,
436
+ version: opts.version,
437
+ statusPath: join(root, "state", "status.json"),
438
+ ...(opts.previousStatus !== undefined ? { readStatus: () => opts.previousStatus } : {}),
439
+ onAnomalySignal: (message) => {
440
+ // Durable anomaly record beside the inbox item.
441
+ store.logAnomaly("slack_connections", null, null, message);
442
+ return opts.writeInbox(message, "anomaly");
443
+ },
444
+ });
445
+ if (!announcer) throw new Error("slack: announcer not built despite enabled = true");
446
+
447
+ const router = new SlackRouter({
448
+ cfg,
449
+ store,
450
+ identity,
451
+ });
452
+
453
+ const slackReply = makeSlackReply({ cfg, store, api: transport, now, log: (line) => log(line) });
454
+ const commands = new CommandWatcher({
455
+ dir: join(root, "commands", "slack"),
456
+ scope: "slack",
457
+ tier: { dir: "slack", currentOrigin: () => origins.latest() },
458
+ deps: { ...opts.commandDeps, slackReply },
459
+ store,
460
+ log: (line) => log(line),
461
+ });
462
+ await commands.start();
463
+
464
+ await transport.startSocket((event) => {
465
+ void router
466
+ .handle(event)
467
+ .catch((err) => log(`slack: router threw (event ${event.event_id} dropped)`, err));
468
+ });
469
+
470
+ const timers: Array<ReturnType<typeof setInterval>> = [];
471
+ const every = (ms: number, fn: () => void): void => {
472
+ const t = setInterval(fn, ms);
473
+ t.unref?.();
474
+ timers.push(t);
475
+ };
476
+ // Half the grace window, so a PR is noticed roughly when it goes overdue.
477
+ every(Math.max(1, Math.round(cfg.slack.policy.pickup_grace_min / 2)) * 60_000, () => {
478
+ void announcer
479
+ .announceNotPickedUp()
480
+ .catch((err) => log("slack: not-picked-up sweep failed", err));
481
+ });
482
+ // Every timer body is its own try/catch: a throw inside setInterval is an
483
+ // uncaught exception, and a duplicate-consumer probe must never kill the daemon.
484
+ every(SLACK_CONNECTIONS_TICK_MS, () => {
485
+ try {
486
+ announcer.noteConnections(transport.numConnections);
487
+ } catch (err) {
488
+ log("slack: connections probe failed", err);
489
+ }
490
+ });
491
+
492
+ await announcer.daemonUp();
493
+ announcer.startHeartbeat();
494
+
495
+ return {
496
+ announcer,
497
+ router,
498
+ origins,
499
+ transport,
500
+ commands,
501
+ slackReply,
502
+ async stop(reason: string): Promise<void> {
503
+ for (const t of timers) clearInterval(t);
504
+ timers.length = 0;
505
+ announcer.stopHeartbeat();
506
+ commands.stop();
507
+ // The store is still open here (onStop runs before teardown), so the
508
+ // post is ledgered; `daemonStopping` is bounded internally, so awaiting
509
+ // it can never hold the exit open.
510
+ await announcer.daemonStopping(reason);
511
+ await transport.stopSocket().catch((err) => log("slack: socket stop failed", err));
512
+ },
513
+ };
514
+ }
515
+
516
+ /**
517
+ * The local Slack belt both daemon-side watchers install (`commands/` and
518
+ * `commands/operator/`): with Slack live, a file is refused while the turn
519
+ * being served was caused by a non-owner Slack message — read through
520
+ * `latestAny()`, which only a daemon wake's `local` origin supersedes, and
521
+ * `slack_reply` is executable there. Slack off ⇒ no tier gate at all and the
522
+ * handlers as given.
523
+ */
524
+ function localSlackBelt(
525
+ slack: SlackRuntime | null,
526
+ deps: CommandDeps,
527
+ ): Pick<CommandWatcherOptions, "tier" | "deps"> {
528
+ return slack
529
+ ? {
530
+ tier: { dir: "local", currentOrigin: () => slack.origins.latestAny() },
531
+ deps: { ...deps, slackReply: slack.slackReply },
532
+ }
533
+ : { deps };
534
+ }
535
+
536
+ /**
537
+ * The legacy `commands/` watcher. Two things change when Slack is live, and
538
+ * only those two: `slack_reply` becomes executable from this dir as well, and
539
+ * the belt gate is installed — when the turn being served was caused by a
540
+ * non-owner Slack message, the legacy dir refuses, so a tier-checked command
541
+ * cannot slip past by being written to the wrong place. Slack off ⇒ no tier
542
+ * gate at all.
543
+ *
544
+ * The gate reads `latestAny()`, not `latest()`: the belt must not lift merely
545
+ * because a turn ran past the 15-minute TTL (text in the channel can ask for
546
+ * exactly that — "take your time"). Slack inbound turns are retired and no
547
+ * wake stamps an origin (see `slackOriginStore`), so the belt stays lifted.
548
+ */
549
+ export function buildLocalCommandWatcher(opts: {
550
+ root: string;
551
+ deps: CommandDeps;
552
+ store: StateStore;
553
+ slack: SlackRuntime | null;
554
+ log: (line: string) => void;
555
+ }): CommandWatcher {
556
+ const { slack } = opts;
557
+ return new CommandWatcher({
558
+ dir: join(opts.root, "commands"),
559
+ ...localSlackBelt(slack, opts.deps),
560
+ store: opts.store,
561
+ log: opts.log,
562
+ });
563
+ }
564
+
565
+ /**
566
+ * The operator's command watcher: `commands/operator/`, the directory
567
+ * `qcr drain`, the board's `u` and the deploy tab write as root.
568
+ * Same vocabulary, same handlers (`slack_reply` included while Slack is
569
+ * live); the difference is the issuer the watcher stamps, and only the real
570
+ * process seal decides it. The same local Slack
571
+ * belt as `commands/` is installed, and binds only files the seal does not
572
+ * vouch for: the overseer cannot write into a sealed directory, but the
573
+ * parent is the daemon's, so it can make or swap in an unsealed one — and a
574
+ * file there is a session's, so it must be refused exactly where `commands/`
575
+ * would refuse it. The daemon never creates the directory — see
576
+ * `CommandWatcherOptions`.
577
+ */
578
+ export function buildOperatorCommandWatcher(opts: {
579
+ root: string;
580
+ deps: CommandDeps;
581
+ store: StateStore;
582
+ slack: SlackRuntime | null;
583
+ log: (line: string) => void;
584
+ }): CommandWatcher {
585
+ const { slack } = opts;
586
+ return new CommandWatcher({
587
+ dir: join(opts.root, "commands", "operator"),
588
+ operator: processOperatorSeal(),
589
+ ...localSlackBelt(slack, opts.deps),
590
+ store: opts.store,
591
+ log: opts.log,
592
+ });
593
+ }
594
+
595
+ /**
596
+ * Where the passive auth watch looks. PR sessions are spawned
597
+ * with `--session-dir <root>/<pi.session_dir>/<session>`, so THAT is where
598
+ * their expired-token errors land; pi's default store alone would miss every
599
+ * reviewer session. The default store under the container user's home (the
600
+ * pihome volume) stays covered for anything spawned without an explicit dir.
601
+ */
602
+ export function piAuthWatchDirs(sessions: Pick<PiSessionManager, "sessionsRoot">): string[] {
603
+ // pi's default global store — the same dir the archiver's pi-global store
604
+ // moves day-old files out of: one definition, `piGlobalSessionsDir`.
605
+ return [sessions.sessionsRoot(), piGlobalSessionsDir()];
606
+ }
607
+
608
+ /**
609
+ * Where the `eod-curation-receipt` command lands. With no learner on this
610
+ * daemon (memory switched off) the receipt is refused, with the reason —
611
+ * which is what the sweep prompt's "off" no-packet section
612
+ * (`renderNoPacketSection`) tells the curator: "the daemon would refuse
613
+ * one". tests/eod-seed-coherence.test.ts holds the two together.
614
+ */
615
+ export function routeCurationReceipt(
616
+ learner: Pick<Learner, "applyReceipt"> | null,
617
+ receipt: CurationReceipt,
618
+ ): FileOutcome {
619
+ if (!learner)
620
+ return {
621
+ ok: false,
622
+ reason: "the learner is not running on this daemon ([openviking] enabled = false)",
623
+ };
624
+ return learner.applyReceipt(receipt);
625
+ }
626
+
627
+ /**
628
+ * The daemon's round-memory seed, or none when memory is switched off: a
629
+ * round with no seed renders memory-less without a read, so a deliberate off
630
+ * never reads as degraded memory in the prompt or the round's record.
631
+ */
632
+ export function roundMemorySeed(
633
+ enabled: boolean,
634
+ seed: RoundMemorySeed,
635
+ ): { memory?: RoundMemorySeed } {
636
+ return enabled ? { memory: seed } : {};
637
+ }
638
+
639
+ /** Where and as whom every daemon-side OpenViking client connects — the
640
+ * credential, the observers and the client instance stay each caller's own. */
641
+ function ovConnection(ov: Config["openviking"]): {
642
+ baseUrl: string;
643
+ user: string;
644
+ account: string;
645
+ timeoutMs: number;
646
+ } {
647
+ return {
648
+ baseUrl: openVikingBaseUrl(ov),
649
+ user: ov.user,
650
+ account: ov.account,
651
+ timeoutMs: ov.request_timeout_ms,
652
+ };
653
+ }
654
+
655
+ /**
656
+ * The startup orphan sweep's posting debt. Two rounds are no phantom:
657
+ * - one whose outbox job is still pending owes its review, and the outbox
658
+ * delivers it with no session. Only a job the outbox will actually process
659
+ * counts — live mode, this repo (`outboxProcesses`): a pending dry-run
660
+ * snapshot left over a flip to live is never delivered, so keeping its
661
+ * round would hold it until the operator's `u`. A dry run never owes one;
662
+ * - one whose review is already on GitHub (a `confirmed` receipt for exactly
663
+ * this round and posting key) but whose verdict never landed: its stage
664
+ * limit settles it from the receipt, where a cancel and requeue would run
665
+ * the lenses again for a review already posted.
666
+ */
667
+ export function orphanPostingOwed(
668
+ receiptDir: string,
669
+ dryRun: boolean,
670
+ repo: string,
671
+ ): (round: Pick<RoundRow, "pr" | "round_no" | "posting_key">) => boolean {
672
+ return (round) => {
673
+ if (round.posting_key === null) return false;
674
+ const receipt = readPostingReceipt(receiptDir, round.pr, round.round_no);
675
+ if (
676
+ receipt?.status === "confirmed" &&
677
+ receipt.posting_key === round.posting_key &&
678
+ receipt.pr === round.pr &&
679
+ receipt.round_no === round.round_no
680
+ )
681
+ return true;
682
+ return (
683
+ !dryRun && deliverablePosting(receiptDir, round.pr, round.round_no, round.posting_key, repo)
684
+ );
685
+ };
686
+ }
687
+
688
+ /**
689
+ * The daemon's origin store. It has no accepted-wake view, so the newest
690
+ * record is in force at once.
691
+ *
692
+ * Slack inbound turns are retired, so no record can describe a live turn any
693
+ * more, and no daemon wake stamps a superseding `local` one. A guest's record
694
+ * left by the previous run would therefore hold the legacy-dir belt on for
695
+ * good: the records are dropped at boot, before the store reads them.
696
+ */
697
+ export function slackOriginStore(root: string, now?: () => Date): OriginStore {
698
+ clearOriginRecords(join(root, "state", "slack", "origin"));
699
+ return new OriginStore(join(root, "state", "slack", "origin"), now);
700
+ }
701
+
702
+ export function createProductionDaemon(parts: ProductionParts = buildParts()): Daemon {
703
+ const { cfg, root, gate, adapter, sessions } = parts;
704
+ const memoryBootAt = new Date();
705
+ const log = (msg: string, extra?: unknown) => console.log(`[quickreviewd] ${msg}`, extra ?? "");
706
+ // The last run's heartbeat, read before start() writes this boot's first
707
+ // status.json over it: the Slack `daemon.up` downtime is measured from it.
708
+ const previousStatus = readPreviousStatus(join(root, "state", "status.json"));
709
+
710
+ // The daemon's one memory store, built in onStart once secrets are readable.
711
+ // Everything that needs it is late-bound through this holder: the round
712
+ // assembler and the EOD pinned-area watchdog are constructed before it
713
+ // exists, and both degrade to "no memory" rather than to an error.
714
+ let memoryStore: MemoryStore | null = null;
715
+ /** The same store, as the sweep that keeps what it could not read (the
716
+ * pinned watchdog must tell an outage from an empty pinned area). */
717
+ let memorySweep: PinnedSweepSource | null = null;
718
+ let memoryAssembler: ContextAssembler | null = null;
719
+ const notBuiltYet = {
720
+ status: "unreadable",
721
+ reason: "memory store not built yet (daemon still starting)",
722
+ } as const;
723
+ const lateAssembler: ContextAssembler = {
724
+ pinnedHead: () =>
725
+ memoryAssembler ? memoryAssembler.pinnedHead() : Promise.resolve(notBuiltYet),
726
+ contextBlock: (q, o) =>
727
+ memoryAssembler ? memoryAssembler.contextBlock(q, o) : Promise.resolve(notBuiltYet),
728
+ };
729
+
730
+ // The profile-selected ecosystem seam, loaded once at assembly (a
731
+ // malformed profile.toml fails the boot loudly). It decides the tier-forcing
732
+ // migration gate, the round pre-check and the prompt's migration-gate prose;
733
+ // "none" (no profile, no file, no key) runs none of that machinery.
734
+ const ecosystem = ecosystemFor(
735
+ loadProfileEcosystem(root, cfg.review.profile, homeProfileLookup()),
736
+ );
737
+ log(`profile ${cfg.review.profile || "(none)"}: ecosystem ${ecosystem.name}`);
738
+
739
+ // The identity variable map every prompt render and agent build
740
+ // resolves {{owner_name}}/{{repo}}/{{bot_login}} against.
741
+ const identity = identityVars(cfg);
742
+ // The prompt is bound to the host's lens tools (packages/pi-plugin/src/launchvocab.ts). The worker
743
+ // host's sessions have `lens` and `lens_wave`; the tmux host's have `subagent`
744
+ // and `launch_plan`. Dispatch writes the host's names into every order it builds
745
+ // (`SessionHooks.lensRuntime`), whatever `[engine] runtime`, `[pi] lens_runtime` or
746
+ // `engine_wave` say, and this refuses an order written for the other pair.
747
+ // The central lens permit broker (src/ipc/permits.ts): one count of lens
748
+ // sessions across every worker. The WorkerSupervisor serves its ops on the worker
749
+ // socket and status.json shows it.
750
+ const lensPermits = new PermitBroker({
751
+ ceiling: cfg.daemon.lens_permits,
752
+ staggerMs: cfg.daemon.lens_grant_stagger_ms,
753
+ log: (m) => log(m),
754
+ });
755
+ // The daemon's one store, built here so the worker world (the host the daemon's
756
+ // state machine speaks to) can exist before the daemon does.
757
+ const store = new StateStore(join(root, "state", "state.db"));
758
+ // The bot login's single refresher (src/loginrefresh/), spawned by
759
+ // path so this process imports neither pi nor src/engine/. Only built when
760
+ // `[login_refresh] enabled`.
761
+ let loginRefresh: LoginRefreshSupervisor | null = null;
762
+ // Workers are the bot's ONLY host: the IPC server,
763
+ // the supervisor and its spec builder, the leases and the controls, assembled in
764
+ // src/workerworld.ts. No config key picks the tmux route, and nothing births a
765
+ // `pr-<n>` tmux session any more; `sessions` serves paths, worktrees and the replay harness.
766
+ const agentDir = process.env["PI_CODING_AGENT_DIR"]?.trim() || join(homedir(), ".pi", "agent");
767
+ const workerWorld = buildWorkerWorld({
768
+ cfg,
769
+ root,
770
+ store,
771
+ adapter,
772
+ reader: adapter,
773
+ // The entrypoint names the tool uid (docker/entrypoint.sh): the model's
774
+ // shims then dial the socket as another uid of this group.
775
+ toolUid: toolUidSplit(),
776
+ sessions,
777
+ ...(parts.ovSessions ? { ovSessions: parts.ovSessions } : {}),
778
+ permits: lensPermits,
779
+ agentDir,
780
+ pump: () => daemon.pump(),
781
+ loginGate: {
782
+ paused: () => loginRefresh?.paused() ?? false,
783
+ whenResumed: (ms) => loginRefresh?.whenResumed(ms) ?? Promise.resolve(true),
784
+ },
785
+ log,
786
+ });
787
+ const botHost: SessionHooks = workerWorld.host;
788
+ const renderPrompt = (order: RoundOrder, memory: RoundMemory): string => {
789
+ assertOrderFitsHost(botHost, order);
790
+ return renderRoundPrompt(
791
+ readFileSync(join(root, "prompts", templateFileForOrder(order)), "utf8"),
792
+ order,
793
+ memory,
794
+ cfg.owner.name,
795
+ ecosystem,
796
+ identity,
797
+ );
798
+ };
799
+ // The recheck's own template — a nudge, not a round order.
800
+ const renderRecheck = (order: RecheckOrder): string =>
801
+ renderRecheckPrompt(
802
+ readFileSync(join(root, "prompts", RECHECK_TEMPLATE_FILE), "utf8"),
803
+ order,
804
+ identity,
805
+ );
806
+
807
+ let eod: EodTimer | null = null;
808
+ let pinWatch: ReturnType<typeof startEodPinWatch> | null = null;
809
+ let heartbeat: HeartbeatTimer | null = null;
810
+ let commands: CommandWatcher | null = null;
811
+ let operatorCommands: CommandWatcher | null = null;
812
+ let slack: SlackRuntime | null = null;
813
+ // onStart's own intervals (the overseer rotation, the agents day check),
814
+ // cleared at stop like every other timer.
815
+ const intervals: Array<ReturnType<typeof setInterval>> = [];
816
+ // The [feedback] engine, built in onStart (it needs the memory store). A
817
+ // signal arriving in the boot gap between the webhook binding and onStart
818
+ // is ledgered, never dropped: its row starts `pending`, the backfill pump
819
+ // writes its memory entry, and the agents rebuild once the engine exists so
820
+ // a same-day [feedback] comment still reaches the validator.
821
+ let feedback: FeedbackEngine | null = null;
822
+ let feedbackLedgeredEarly = false;
823
+ // The [feedback] ledger's backfill pump — writes what memory still owes
824
+ // while memory health reads healthy, and wakes `memory_write_backlog` when
825
+ // an owed signal ages past the threshold.
826
+ let feedbackPump: FeedbackMemoryPump | null = null;
827
+ // The finding-case writer — one document per posted finding a human
828
+ // answered or whose fate is known, into reviewer memory's `finding-cases/`,
829
+ // through the same store; backfills `[memory] cases_backfill_days`. Writes
830
+ // only while memory health reads healthy.
831
+ let caseWriter: CaseWriter | null = null;
832
+ // Is a review round reading memory right now? Dispatch opens a window
833
+ // around each round's seed (through to its lesson selection), the selector
834
+ // one around each prefetch, and the case writer starts no batch while any
835
+ // is open — every case is a multi-second embedding on the one-thread
836
+ // embedder those reads wait on.
837
+ const roundActivity = new RoundMemoryActivity();
838
+ // The learner — mines the human outcomes into lesson candidates at
839
+ // EOD − `lessons_mine_lead_min`, hands the EOD sweep its outcomes packet
840
+ // (`dayContext`), takes the `eod-curation-receipt`, checks every adoption
841
+ // against memory, and wakes `eod_curation_missing` /
842
+ // `eod_lessons_not_persisted`. Only where memory is switched on: with it off
843
+ // there is nowhere a lesson could land.
844
+ let learner: Learner | null = null;
845
+ // The closed-PR archiver — daemon bookkeeping on its own timer (never a
846
+ // reconcile step), started beside the case writer and the learner whatever
847
+ // memory does. It starts no work before the first healthy reconcile plus
848
+ // `[archive] start_delay_min`, and holds while draining, stalled, starved or
849
+ // short of disk. Every tick plans (status.json `archive` says what a pass
850
+ // would move); in mode `on` it also restores reopened PRs, moves the old
851
+ // reader-less event rows and webhook deliveries into archive/db/, moves
852
+ // closed PRs' files into archive/pr-<n>/ and seals finished months.
853
+ let archiver: Archiver | null = null;
854
+ // The lesson shadow ledger — every round's selection through OpenViking
855
+ // (bounded, `[memory] delivery`), the marks a delivered dont-raise lesson
856
+ // would have left on its posted findings, and the operator's kill switch.
857
+ // Built in onStart (it needs the state store); the selector only where
858
+ // memory is on. Under `shadow` nothing here changes a prompt; under `on`
859
+ // dispatch awaits `select` beside the seed and renders the round's lessons,
860
+ // and `repoFacts` reaches the recheck nudge and the ping-pong wake.
861
+ let lessonLedger: LessonLedger | null = null;
862
+ let lessonSelector: RoundLessonSelector | null = null;
863
+ const lateLessons: RoundLessonHook = {
864
+ start: (order, files) => lessonSelector?.start(order, files),
865
+ delivering: () => lessonSelector?.delivering() ?? false,
866
+ select: (order, files, opts) =>
867
+ lessonSelector?.select(order, files, opts) ?? Promise.resolve(null),
868
+ repoFacts: (q) => lessonSelector?.repoFacts(q) ?? Promise.resolve(""),
869
+ // A round discarded before its handoff: its selection record goes (with
870
+ // no selector nothing was ever recorded).
871
+ discard: (roundId) => lessonSelector?.discard(roundId),
872
+ };
873
+ /** The status headline, recomputed at most once a minute (the heartbeat
874
+ * runs far more often, and the report reads several tables). */
875
+ let lessonStatusCache: { at: number; value: LessonDeliveryStatus | null } | null = null;
876
+ // The OpenViking server, supervised as a child of this process. Started
877
+ // first in onStart so it warms up while the agents build — but never
878
+ // awaited: there is deliberately no boot ordering, and rounds run
879
+ // memory-less until `/ready` passes.
880
+ let openviking: OpenVikingSupervisor | null = null;
881
+ // The local English embedder the server embeds through — a second
882
+ // supervised child, started first; the server's first start waits (bounded)
883
+ // for it to serve. Off unless the rendered ov.conf routes to it.
884
+ let localEmbedder: LocalEmbedderSupervisor | null = null;
885
+ // The embedder migration's daemon half — resumes the re-embed of reviewer
886
+ // memory the entrypoint's `prepare` journalled, once /ready passes.
887
+ // Timer-armed, never awaited; its block rides inside the heartbeat's
888
+ // `openviking` entry.
889
+ let reembed: ReembedDriver | null = null;
890
+ // Worker liveness: the archiver, the startup orphan sweep and operator
891
+ // recovery read the supervisor's lease files, never a tmux listing.
892
+ const workerLive = createWorkerLiveness({ runDir: runDirOf(join(root, "state")) });
893
+ // The webhook-URL self-heal. Only ever constructed when [ingress]
894
+ // shape = "tunnel-file".
895
+ let webhookHeal: WebhookHealer | null = null;
896
+ // Reviewer-memory health — the authenticated probe of each credential in
897
+ // use, fed by the daemon's own requests and round seeds, and the one source
898
+ // of the status.json `memory` block every person-facing surface reads.
899
+ // Built in onStart (it needs the store and the server's config); until then
900
+ // the block reads `unknown` — never healthy, never off.
901
+ let memoryHealth: MemoryHealthMonitor | null = null;
902
+ /** Why memory is NOT monitored on this daemon; null while still deciding. */
903
+ let memoryUnmonitored: string | null = null;
904
+ if (parts.memoryRequests) parts.memoryRequests.sink = (r) => memoryHealth?.recordRequest(r);
905
+
906
+ // The origin store the Slack belt reads. Slack inbound turns are retired, so
907
+ // nothing writes an origin any more.
908
+ const origins = slackOriginStore(root);
909
+
910
+ // The companion inbox: everything that used to wake the overseer (anomalies,
911
+ // the heartbeat, authwatch's and the login refresher's alerts, the EOD sweep,
912
+ // the self-requeue notice, the Slack connection signal) is an item here.
913
+ const inbox = new CompanionInbox({ root, log });
914
+ const writeInbox = inbox.writer();
915
+
916
+ // The profile's lens-heuristics extension, loaded once here so a
917
+ // malformed heuristics.toml fails the assembly loudly (same contract as the
918
+ // addenda pack below) and every dispatch selects with the same predicates.
919
+ const profileHeuristics = loadProfileHeuristics(root, cfg.review.profile, homeProfileLookup());
920
+
921
+ // The gate's loud line (app-self-config executions) goes to the
922
+ // daemon log; the default console fallback covers any unwired gate.
923
+ gate.setLog(log);
924
+
925
+ const daemon = createDaemon({
926
+ cfg,
927
+ root,
928
+ store,
929
+ adapter,
930
+ ...(parts.runHost ? { runHost: parts.runHost } : {}),
931
+ postingDelivery: {
932
+ createReview: async (pr, payload) => {
933
+ // An approval queued by approve-pr.sh keeps that route's green-CI
934
+ // precondition. Do not silently turn an old approval verdict into a
935
+ // COMMENT that its session never marked approval_withheld.
936
+ if (payload.event === "APPROVE" && cfg.guards.block_approve_on_red_ci) {
937
+ try {
938
+ if (
939
+ ciBlocksApproval(classifyCi(await adapter.listChecks(payload.commit_id)), {
940
+ noChecksIsRed: cfg.guards.no_checks_is_red,
941
+ })
942
+ )
943
+ return { executed: false, blocked: "ci-not-green" };
944
+ } catch {
945
+ return { executed: false, blocked: "pr-unreadable" };
946
+ }
947
+ }
948
+ return adapter.createReview(pr, payload);
949
+ },
950
+ listReviewsFull: (pr) => adapter.listReviewsFull(pr),
951
+ // The deferred-posting retry: straight through the gate, whose guards
952
+ // bind as on any review (guard 4 softens a red-CI approval to a
953
+ // COMMENT there); only guard 5 is exempt, via `postingRetry`.
954
+ createDeferredReview: (pr, payload, postingKey) =>
955
+ adapter.createReview(pr, payload, { postingRetry: postingKey }),
956
+ },
957
+ hooks: botHost,
958
+ writeInbox,
959
+ renderPrompt,
960
+ ecosystem,
961
+ heuristics: profileHeuristics,
962
+ // Pinned head + one budgeted context block per round. Past the
963
+ // deadline the round dispatches memory-less and logs the gap — memory
964
+ // never blocks or delays a round. Memory switched off
965
+ // (`[openviking] enabled = false`) seeds nothing: no read, no
966
+ // "memory unavailable" note, nothing counted as degraded.
967
+ ...roundMemorySeed(cfg.openviking.enabled, {
968
+ assembler: lateAssembler,
969
+ maxTokens: cfg.memory.context_tokens,
970
+ timeoutMs: cfg.memory.context_timeout_ms,
971
+ // Every seed, after the round was built, feeds memory health — a seed
972
+ // that timed out reaches no client observer, so this is the only place
973
+ // it is seen.
974
+ onSeeded: (mem: RoundMemory) =>
975
+ memoryHealth?.recordRoundSeed({
976
+ degraded: mem.degraded,
977
+ reasons: mem.gaps.map((g) => `${g.part}: ${g.reason}`),
978
+ }),
979
+ // The PR session's own recall (vendored openviking-pi patch 0008)
980
+ // searches with the round's query and the round's areas, not the round
981
+ // order.
982
+ publishQuery: (pr: number, query: string) => {
983
+ writeRecallHint(
984
+ sessions.ovRecallHintPath(prSessionName(pr)),
985
+ { query, areas: ROUND_SEARCH_AREAS },
986
+ { log: (msg) => log(msg) },
987
+ );
988
+ },
989
+ // The round's memory window — the case writer starts no
990
+ // batch from the seed until the lesson selection has taken over.
991
+ activity: roundActivity,
992
+ }),
993
+ // The round's lesson selection (shadow ledger; awaited by dispatch only
994
+ // under `delivery = "on"`); and the matcher, told when a round's findings
995
+ // record lands.
996
+ lessons: lateLessons,
997
+ onFindingsRecorded: (target) =>
998
+ completeRecordedFindings(
999
+ {
1000
+ store: daemon.store,
1001
+ ledger: lessonLedger,
1002
+ adapter,
1003
+ sidecarPath: (n, k) => priorSidecarPath(join(root, "state"), n, k),
1004
+ log,
1005
+ },
1006
+ target,
1007
+ ),
1008
+ renderRecheckPrompt: renderRecheck,
1009
+ // Conflict resolutions are never trivial: git-level auto-merge comparison
1010
+ // in the base clone names the hand-resolved files.
1011
+ // A degraded merge-verify is logged with its reason here (the failed git
1012
+ // command and its stderr when one failed); the callers record the
1013
+ // degradation itself.
1014
+ resolvedFilesInMerges: async (from, to, pr) => {
1015
+ const res = await sessions.worktrees.resolvedFilesInMerges(from, to, pr);
1016
+ if (res.degraded)
1017
+ log(
1018
+ `merge-verify degraded${pr !== undefined ? ` #${pr}` : ""} ${from.slice(0, 7)}→${to.slice(0, 7)}: ${res.reason ?? "unknown"}`,
1019
+ );
1020
+ return res;
1021
+ },
1022
+ // Outcome capture: did the code at a posted finding's anchor
1023
+ // change? Read-only git in the same base clone, no fetch, memoised.
1024
+ outcomeGit: sessions.worktrees,
1025
+ // One network fetch per round, before the order is built: the PR head and
1026
+ // the branch it targets, together. Everything downstream (prompt, session,
1027
+ // passes) then works from the shas this returns.
1028
+ prepareWorktree: (pr, baseRef, expectedHead) =>
1029
+ sessions.worktrees.ensureWorktree(pr, { baseRef, expectedHead }),
1030
+ // Follow-up rounds: the PR-owned change since the last reviewed
1031
+ // head (the reviewed head replayed onto the round's merge base, read-only
1032
+ // git in the base clone), and the pin that keeps a reviewed head
1033
+ // replayable after a force-push.
1034
+ prOwnedDelta: (q) => sessions.worktrees.prOwnedDelta(q),
1035
+ pinReviewedHead: (pr, sha) => sessions.worktrees.pinReviewedHead(pr, sha),
1036
+ // Third-party sources at the PR's locked versions, unpacked into a cache
1037
+ // on the state volume for the passes to read (`[review]
1038
+ // dependency_sources`). Download and unpack only;
1039
+ // total — a dead registry yields an empty note.
1040
+ dependencySources: async (worktreePath, files) =>
1041
+ renderDependencyNote(
1042
+ await prepareDependencySources(worktreePath, files, {
1043
+ cacheDir: join(root, "state", "depcache"),
1044
+ log: (line) => log(`deps: ${line}`),
1045
+ }),
1046
+ ),
1047
+ // Migration-number pre-check input: the base branch's migrations
1048
+ // at the sha the round pinned. Local git in the just-fetched base clone.
1049
+ listBaseMigrations: (baseSha) => sessions.worktrees.listMigrationFiles(baseSha),
1050
+ // Escalation-mention pick: most recent human author of the
1051
+ // touched first-party directive files at the round's merge base, read from
1052
+ // the base clone; the daemon's own bot identity never qualifies.
1053
+ directiveAuthor: (sha, paths) =>
1054
+ sessions.worktrees.lastHumanAuthor(sha, paths, {
1055
+ botLogins: [cfg.github.bot_login, cfg.github.app_slug],
1056
+ }),
1057
+ log,
1058
+ // Lazy on purpose: the Slack subsystem is built in onStart, after the
1059
+ // store exists. Off ⇒ this resolves undefined forever and dispatch/verdicts
1060
+ // never see an announcer.
1061
+ announce: () => slack?.announcer,
1062
+ digestHeadline: () => latestDigestHeadline(join(root, "digest")),
1063
+ // Passive pi auth-failure watch: the JSONLs pi actually writes.
1064
+ piSessionsDir: piAuthWatchDirs(sessions),
1065
+ // The memory engine's heartbeat block — a cached, synchronous
1066
+ // read of the supervisor's last health + /proc sample, plus the reembed
1067
+ // journal (one small synchronous file read per heartbeat) and the local
1068
+ // embedder's status.
1069
+ openvikingStatus: () => {
1070
+ const status = openviking?.status();
1071
+ if (!status) return status;
1072
+ return {
1073
+ ...status,
1074
+ ...(reembed ? { reembed: reembed.status() } : {}),
1075
+ ...(localEmbedder ? { embedder: localEmbedder.status() } : {}),
1076
+ };
1077
+ },
1078
+ // The archiver's block — a cached synchronous read. Before
1079
+ // onStart builds it the mode is known and nothing else: `unknown`, a
1080
+ // null backlog, never 0.
1081
+ archiveStatus: () => (archiver ? archiver.status() : pendingArchiveStatus(cfg.archive.mode)),
1082
+ // The login refresher's block — a cached synchronous read; absent when the
1083
+ // refresher is not configured.
1084
+ loginRefreshStatus: () => loginRefresh?.status(),
1085
+ // The lens permit broker's use and queue.
1086
+ permitStatus: () => lensPermits.status(),
1087
+ // What hosts each PR's session, for status.json's per-PR `runtime`.
1088
+ runtimeOf: workerWorld.runtimeOf,
1089
+ // Whether memory works, beside whether the server is up — and what memory
1090
+ // still owes the [feedback] ledger.
1091
+ memoryStatus: (): MemoryStatus => {
1092
+ const base = memoryHealth
1093
+ ? memoryHealth.status()
1094
+ : parts.memoryBootFailure
1095
+ ? migrationMemoryStatus(parts.memoryBootFailure, memoryBootAt)
1096
+ : memoryUnmonitored !== null
1097
+ ? disabledMemoryStatus(memoryUnmonitored)
1098
+ : pendingMemoryStatus();
1099
+ // The finding-case ledger, when a case writer runs (the block is a
1100
+ // fresh object each call). Unread is unknown (null), never
1101
+ // "nothing owed".
1102
+ if (caseWriter) {
1103
+ let cases: CaseWritesStatus | null;
1104
+ try {
1105
+ cases = caseWriter.status();
1106
+ } catch (err) {
1107
+ log("memory status: could not read the finding-case ledger", err);
1108
+ cases = null;
1109
+ }
1110
+ base.case_writes = cases;
1111
+ }
1112
+ // The lesson shadow ledger's headline. Unread is unknown (null), never
1113
+ // "nothing happened".
1114
+ if (lessonLedger) {
1115
+ const nowMs = Date.now();
1116
+ if (!lessonStatusCache || nowMs - lessonStatusCache.at >= 60_000) {
1117
+ let value: LessonDeliveryStatus | null;
1118
+ try {
1119
+ value = lessonDeliveryStatus(lessonLedger, cfg.memory.delivery, {
1120
+ selecting: lessonSelector !== null,
1121
+ });
1122
+ } catch (err) {
1123
+ log("memory status: could not read the lesson ledger", err);
1124
+ value = null;
1125
+ }
1126
+ lessonStatusCache = { at: nowMs, value };
1127
+ }
1128
+ base.lessons = lessonStatusCache.value;
1129
+ }
1130
+ // The day's EOD curation and the lessons' balance. Unread is unknown
1131
+ // (null), never "all well".
1132
+ if (learner) {
1133
+ try {
1134
+ base.curation = learner.status();
1135
+ } catch (err) {
1136
+ log("memory status: could not read the learner's ledger", err);
1137
+ base.curation = null;
1138
+ }
1139
+ }
1140
+ try {
1141
+ const b = daemon.store.feedbackMemoryBacklog();
1142
+ return {
1143
+ ...base,
1144
+ feedback_writes: {
1145
+ pending: b.pending,
1146
+ failed: b.failed,
1147
+ oldest_pending_at: b.oldest_pending_at,
1148
+ },
1149
+ };
1150
+ } catch (err) {
1151
+ // Unread is unknown, never "nothing owed".
1152
+ log("memory status: could not read the [feedback] memory backlog", err);
1153
+ return { ...base, feedback_writes: null };
1154
+ }
1155
+ },
1156
+
1157
+ // The [feedback] tuning loop.
1158
+ onFeedback: async (e: FeedbackEvent) => {
1159
+ if (!feedback) {
1160
+ // Before the engine exists the signal is ledgered, never dropped: its
1161
+ // row waits `pending` for the backfill pump.
1162
+ const res = ledgerFeedbackSignal(daemon.store, e, {
1163
+ timezone: cfg.ops.timezone,
1164
+ memoryEnabled: cfg.openviking.enabled,
1165
+ });
1166
+ if (res.result === "recorded") feedbackLedgeredEarly = true;
1167
+ log(
1168
+ `feedback: signal before engine ready — ledgered (${res.result}); memory is written by the backfill`,
1169
+ e.kind,
1170
+ );
1171
+ return;
1172
+ }
1173
+ await feedback.ingest(e);
1174
+ },
1175
+
1176
+ async onStart(d) {
1177
+ // OpenViking first and unawaited: the server takes seconds to become
1178
+ // ready, so it starts warming while everything below builds. A missing
1179
+ // binary (any dev PC) is reported once and never retried; a crash is
1180
+ // respawned on a capped backoff ladder, forever.
1181
+ //
1182
+ // The local English embedder starts before it — the server's /ready
1183
+ // probes it — and the server's first start waits for it
1184
+ // to serve, bounded (EMBEDDER_GATE_MAX_WAIT_MS). Neither is awaited here.
1185
+ localEmbedder = new LocalEmbedderSupervisor({ cfg: cfg.openviking, repoRoot: root, log });
1186
+ localEmbedder.start();
1187
+ const embedderForGate = localEmbedder;
1188
+ openviking = new OpenVikingSupervisor({
1189
+ cfg: cfg.openviking,
1190
+ log,
1191
+ startGate: {
1192
+ open: () => embedderForGate.gateOpen(),
1193
+ maxWaitMs: EMBEDDER_GATE_MAX_WAIT_MS,
1194
+ what: "the local embedder",
1195
+ },
1196
+ // The reviewer's memory root does not exist on a fresh server until
1197
+ // something creates it. The TUI does this for the companion's sake;
1198
+ // the daemon does it here for its own, each time the server comes up,
1199
+ // so the round path's first sweep lists a real (empty) tree instead
1200
+ // of leaning on how the server spells a missing one.
1201
+ onReady: () => {
1202
+ // Memory health re-measures at once rather than at its next tick:
1203
+ // a server that just came up should read healthy (or say why not)
1204
+ // in seconds, not minutes.
1205
+ void memoryHealth?.probeNow();
1206
+ const store = memoryStore;
1207
+ if (!store?.ensureWritable) return;
1208
+ void store.ensureWritable().then(
1209
+ (res) =>
1210
+ log(
1211
+ `openviking ready: memory root ${res.ok ? "ready" : "NOT ready"} — ${res.message}`,
1212
+ ),
1213
+ (err) => log("openviking ready: memory root check failed", err),
1214
+ );
1215
+ },
1216
+ });
1217
+ openviking.start();
1218
+ const supervisor = openviking;
1219
+ // Under the memory sidecar (endpoint = remote) the store, its journal
1220
+ // and the re-embed migration live in the sidecar, which runs its own
1221
+ // driver at its boot: this daemon has neither the journal nor ov.conf.
1222
+ if (cfg.openviking.endpoint === "local") {
1223
+ reembed = new ReembedDriver({
1224
+ cfg: cfg.openviking,
1225
+ repoRoot: root,
1226
+ keyPath: join(cfg.github.secrets_dir, OV_REVIEWER_KEY_SECRET),
1227
+ ready: () => supervisor.status().ready,
1228
+ log,
1229
+ });
1230
+ reembed.start();
1231
+ }
1232
+
1233
+ // Lens agents: expand {{include:...}} + per-pass model overrides into
1234
+ // the build dir (state/agents) the native subagent extension discovers
1235
+ // through its "user" scope — the container entrypoint symlinks
1236
+ // ~/.pi/agent/agents at it.
1237
+ //
1238
+ // The build composes in the same-day feedback addendum when today
1239
+ // carries [feedback] comments — validator-scoped, demotion-only,
1240
+ // regenerated on every ingest and expired on ops-day change (the
1241
+ // 5-minute day check below), so a mid-day restart picks today's
1242
+ // feedback up and yesterday's never leaks into a new day.
1243
+ // The repo-profile addenda pack is loaded once at assembly so a
1244
+ // malformed pack fails the boot loudly (never a silent skip) and every
1245
+ // rebuild composes the same bytes. Composition order is fixed in the
1246
+ // `addenda` list below, with the same-day feedback addendum last.
1247
+ const profileAddenda = loadProfileAddenda(root, cfg.review.profile, homeProfileLookup());
1248
+ if (cfg.review.profile !== "") {
1249
+ log(`profile ${cfg.review.profile}: ${profileAddenda.length} addenda loaded`);
1250
+ const tuned = Object.keys(cfg.lenses ?? {});
1251
+ if (tuned.length) log(`profile ${cfg.review.profile}: lens tuning for ${tuned.join(", ")}`);
1252
+ }
1253
+ // The day is marked built only once the build LANDS (dayScopedRebuild):
1254
+ // a throw on the day-change tick is retried by the next tick instead of
1255
+ // leaving yesterday's feedback addendum in force all day.
1256
+ // The lesson ledger holds the operator's kill switch,
1257
+ // which decides (with `[memory] delivery`) whether the validator's
1258
+ // lesson addendum is composed — so it exists before the first build.
1259
+ lessonLedger = new LessonLedger(d.store);
1260
+ const agentsDay = dayScopedRebuild(
1261
+ () => opsDay(cfg.ops.timezone),
1262
+ (buildDay) => {
1263
+ const fb = feedbackDayAddendum(d.store.feedbackCommentsForDay(buildDay), buildDay);
1264
+ const lessonAdd = lessonAddendaFor(cfg.memory.delivery, lessonLedger);
1265
+ const built = buildAgents(join(root, "agents"), join(root, "state", "agents"), {
1266
+ root,
1267
+ tiers: cfg.models.tiers,
1268
+ riskPasses: cfg.models.risk_passes,
1269
+ validateTier: cfg.models.validate_tier,
1270
+ vars: identity,
1271
+ // Ecosystem-gated addenda follow the profile's selection.
1272
+ ecosystem: ecosystem.name,
1273
+ // A lens's profile `effort` rides the per-variant hook;
1274
+ // `[models.pass_tiers]` pins move a lens's tier on top of it.
1275
+ models: roundAgentModels(cfg),
1276
+ addenda: [
1277
+ ...PASS_ADDENDA,
1278
+ ...configAddenda(cfg.review),
1279
+ ...profileAddenda,
1280
+ // The profile's `emphasis` / `max_severity`, compiled.
1281
+ ...lensTuningAddenda(cfg.lenses),
1282
+ // The profile's `[posting]` policy, compiled for the validator.
1283
+ ...postingPolicyAddenda(cfg.posting),
1284
+ // The validator's lesson authority, beside the
1285
+ // same-day feedback addendum (never in place of it) — only
1286
+ // while `[memory] delivery = "on"` and the kill switch is on.
1287
+ ...lessonAdd,
1288
+ ...(fb ? [fb] : []),
1289
+ ],
1290
+ });
1291
+ log(
1292
+ `built ${built.length} pass agents into state/agents${lessonAdd.length ? " (+ lesson addendum)" : ""}${fb ? " (+ same-day feedback addendum)" : ""}`,
1293
+ );
1294
+ },
1295
+ );
1296
+ const rebuildAgents = agentsDay.rebuild;
1297
+ rebuildAgents();
1298
+ // Reviewer memory: the one daemon-side `MemoryStore`, over the
1299
+ // supervised OpenViking server on loopback. The reviewer user key is
1300
+ // read from the secrets mount lazily (`keyFromFile` re-checks on a TTL),
1301
+ // so a key that lands after boot is picked up without a restart — and an
1302
+ // absent key degrades exactly like a dead server, which is the correct
1303
+ // behaviour on a dev PC. Never an ADMIN or ROOT key.
1304
+ // One key source for the client, the health probe and the key-change
1305
+ // watcher: it re-stats the file every TTL and the client drops its
1306
+ // cache on a 401, so a rotated key is re-read rather than a dead one
1307
+ // held forever.
1308
+ const reviewerKey = keyFromFile(join(cfg.github.secrets_dir, OV_REVIEWER_KEY_SECRET));
1309
+ // The counted `openviking_gap`: every gap counted into the memory
1310
+ // block, and a quiet anomaly row at most once an hour per op:code that
1311
+ // carries the running count (src/openviking/gapsink.ts).
1312
+ const countedGap = countedGapSink({
1313
+ record: (gap) => memoryHealth?.recordGap(gap),
1314
+ logAnomaly: (kind, pr, roundNo, detail) => d.store.logAnomaly(kind, pr, roundNo, detail),
1315
+ log,
1316
+ });
1317
+ const memoryClient = new OpenVikingClient({
1318
+ ...ovConnection(cfg.openviking),
1319
+ apiKey: reviewerKey,
1320
+ log,
1321
+ onGap: countedGap,
1322
+ onRequest: (r) => memoryHealth?.recordRequest(r),
1323
+ });
1324
+ const memory = openVikingStore(memoryClient, {
1325
+ defaultCategory: cfg.openviking.default_category,
1326
+ cacheTtlMs: cfg.openviking.read_cache_ttl_ms,
1327
+ log,
1328
+ // The sessions' own recall (vendored openviking-pi patch 0007) excludes
1329
+ // retired entries by this list, published beside their ovcli.conf
1330
+ // whenever the store's sweep sees it change.
1331
+ onRetired: (uris) =>
1332
+ writeRetiredUris(sessions.ovRetiredPath(), uris, {
1333
+ log: (msg) => log(msg),
1334
+ }),
1335
+ // …starting from the list the last daemon published, so a first sweep
1336
+ // that cannot read everything (the server still warming up) adds to
1337
+ // the sessions' exclusions and never wipes them.
1338
+ initialRetired: readRetiredUrisFile(sessions.ovRetiredPath()),
1339
+ });
1340
+ feedback = new FeedbackEngine({
1341
+ store: d.store,
1342
+ timezone: cfg.ops.timezone,
1343
+ rebuildAgents,
1344
+ memory,
1345
+ // Memory switched off: signals are ledgered `skipped`, never owed.
1346
+ memoryEnabled: cfg.openviking.enabled,
1347
+ log,
1348
+ });
1349
+ // Same-day [feedback] comments ledgered in the boot gap reach the
1350
+ // validator's addendum now (the boot build above may predate them).
1351
+ if (feedbackLedgeredEarly) {
1352
+ try {
1353
+ rebuildAgents();
1354
+ } catch (err) {
1355
+ log("feedback: agents rebuild for boot-gap signals failed", err);
1356
+ }
1357
+ }
1358
+ // Memory switched off leaves the holders empty: the round seeds
1359
+ // nothing (above) and the EOD pinned-area watchdog is not built, so a
1360
+ // deliberate off never reads as an outage.
1361
+ if (cfg.openviking.enabled) {
1362
+ memoryStore = memory;
1363
+ memorySweep = memory;
1364
+ memoryAssembler = openVikingContextAssembler({
1365
+ client: memoryClient,
1366
+ store: memory,
1367
+ // Without quotas the server balances the block across every scope it
1368
+ // holds and a round gets about one memory entry beside two built-in
1369
+ // scope blurbs. The quotas name the corpus;
1370
+ // `context_tokens` stays the budget. See [memory] in config.toml.
1371
+ quotas: cfg.memory.context_quotas,
1372
+ searchTimeoutMs: cfg.openviking.search_timeout_ms,
1373
+ log,
1374
+ });
1375
+ }
1376
+
1377
+ // The lesson shadow ledger and the round's selection. The
1378
+ // ledger (and so the operator's kill switch) always; the selector only
1379
+ // where memory is on — with it off there is nothing to select from.
1380
+ // (Built before the first agents build — the kill switch decides the
1381
+ // validator's lesson addendum; this keeps that same ledger.)
1382
+ lessonLedger = lessonLedger ?? new LessonLedger(d.store);
1383
+ if (cfg.openviking.enabled && cfg.memory.delivery !== "off") {
1384
+ // Its own client — no health observer, no counted gap — so a find
1385
+ // the round's budget aborts never reads as memory failing (the
1386
+ // health window would otherwise call a slow-but-working server
1387
+ // degraded and pause the case writer, the pump and the learner).
1388
+ const lessonEngine = openVikingLessonEngine(
1389
+ lessonFindClient({
1390
+ ...ovConnection(cfg.openviking),
1391
+ apiKey: reviewerKey,
1392
+ log,
1393
+ }),
1394
+ memory,
1395
+ );
1396
+ lessonSelector = new RoundLessonSelector({
1397
+ ledger: lessonLedger,
1398
+ mode: () => cfg.memory.delivery,
1399
+ engine: () => lessonEngine,
1400
+ budgetMs: cfg.memory.delivery_budget_ms,
1401
+ // The budget follows the mode — `shadow` runs after the
1402
+ // handoff and blocks nothing, so it gets its own, looser one.
1403
+ shadowBudgetMs: cfg.memory.shadow_budget_ms,
1404
+ activity: roundActivity,
1405
+ repo: cfg.github.repo,
1406
+ log,
1407
+ });
1408
+ }
1409
+ log(
1410
+ `lessons: [memory] delivery = ${cfg.memory.delivery}` +
1411
+ (cfg.memory.delivery === "on"
1412
+ ? lessonSelector
1413
+ ? " — DELIVERING: each round awaits its selection beside the memory seed (≤ " +
1414
+ `${cfg.memory.delivery_budget_ms} ms); lessons reach the lens task files, the lessons files of the validator and the lenses a session adds, the main session's index and past-review cases, and the recheck/ping-pong repo facts; the validator carries the lesson-shape addendum` +
1415
+ (cfg.review.task_files
1416
+ ? ""
1417
+ : " — but `[review] task_files = false`: the planned lenses have no task file, so THEY get no lessons")
1418
+ : " — but memory is off ([openviking] enabled = false): nothing to select, nothing delivered"
1419
+ : cfg.memory.delivery === "shadow"
1420
+ ? ` — selection and the shadow ledger only (after each round's handoff, ≤ ${cfg.memory.shadow_budget_ms} ms); no prompt, task file or agent changes`
1421
+ : " — nothing selected, nothing recorded") +
1422
+ (lessonLedger.lessonsOff() ? "; the operator's kill switch is OFF" : ""),
1423
+ );
1424
+
1425
+ // Reviewer-memory health. "Not monitored" is reserved for memory
1426
+ // switched off — `[openviking] enabled = false`, which is what
1427
+ // declining memory in onboarding writes. Memory configured on whose
1428
+ // server cannot run (a binary missing after a rebuild, a venv path
1429
+ // drifted from `[openviking] bin`) is monitored: its probes go
1430
+ // unreachable and it reads `down` after `[memory] down_after_min`,
1431
+ // because that is an outage, not a choice, and must not be painted
1432
+ // like a deliberate off.
1433
+ if (!cfg.openviking.enabled) {
1434
+ memoryUnmonitored = parts.memoryOff ?? "[openviking] enabled = false";
1435
+ if (parts.memoryOff)
1436
+ log(
1437
+ `${parts.memoryOff}: the memory engine runs as the Docker sidecar outside the release image, and no Docker answers here — memory stays off for this boot`,
1438
+ );
1439
+ } else {
1440
+ // A rotated daemon key reaches the sessions too: their ovcli.conf is
1441
+ // rewritten the moment the key changes, not at the next session birth.
1442
+ const watchKey = keyChangeWatcher(() => {
1443
+ const res = sessions.provisionOvCredentials();
1444
+ log(
1445
+ `openviking: the reviewer key changed — sessions' ovcli.conf ${res.written ? "rewritten" : "NOT rewritten"}${res.keyed ? "" : ` (${res.reason || "no key"})`}`,
1446
+ );
1447
+ d.store.logEvent("openviking_key_changed", null, {
1448
+ ovcli_written: res.written,
1449
+ keyed: res.keyed,
1450
+ reason: res.reason,
1451
+ });
1452
+ // A failed rewrite stays owed: the next probe tick tries again.
1453
+ return res.written;
1454
+ });
1455
+ const probeBase = {
1456
+ ...ovConnection(cfg.openviking),
1457
+ };
1458
+ memoryHealth = new MemoryHealthMonitor({
1459
+ probes: {
1460
+ daemon: credentialProbe({
1461
+ ...probeBase,
1462
+ key: () => ({ state: "key", key: reviewerKey() ?? "" }),
1463
+ }),
1464
+ sessions: credentialProbe({
1465
+ ...probeBase,
1466
+ key: () => readOvcliKey(sessions.ovcliConfPath()),
1467
+ }),
1468
+ },
1469
+ beforeProbe: () => {
1470
+ watchKey(reviewerKey());
1471
+ },
1472
+ wake: (kind, detail, message) => d.store.logAnomaly(kind, null, null, detail, message),
1473
+ draining: () => d.store.drainState().draining,
1474
+ // Whether memory serves search — the server's /ready (it runs an
1475
+ // embedding probe) and the local embedder. The fs/ls probe needs no
1476
+ // embedding, so without this a dead embedder would read healthy
1477
+ // while every search failed. Cached, synchronous reads.
1478
+ serving: () =>
1479
+ memoryServing(
1480
+ cfg.openviking.embedding_provider,
1481
+ openviking?.status(),
1482
+ localEmbedder?.status(),
1483
+ ),
1484
+ incidents: {
1485
+ load: () => d.store.memoryIncident(),
1486
+ save: (incident) => d.store.recordMemoryIncident(incident),
1487
+ },
1488
+ where: {
1489
+ daemon: `secrets/${OV_REVIEWER_KEY_SECRET}`,
1490
+ sessions: "ovcli.conf in the pi HOME",
1491
+ },
1492
+ log,
1493
+ // Floors: a mistyped 0 must not become a tight probe loop or a
1494
+ // wake on every probe.
1495
+ intervalMs: Math.max(10, cfg.memory.probe_interval_s) * 1000,
1496
+ downAfterMs: Math.max(1, cfg.memory.down_after_min) * 60_000,
1497
+ bootGraceMs: Math.max(0, cfg.memory.boot_grace_min) * 60_000,
1498
+ rewakeMs: Math.max(1, cfg.memory.rewake_h) * 3_600_000,
1499
+ });
1500
+ // This boot's provisioning report (the entrypoint ran the script
1501
+ // before us): into the block, and one wake per report when it says
1502
+ // anything is wrong — keyed by the report's fingerprint, so a daemon
1503
+ // restart inside the same container does not repeat it.
1504
+ const prov = readProvisionReport(provisionReportPath(root));
1505
+ memoryHealth.setProvision(prov);
1506
+ raiseProvisionWake(d.store, prov);
1507
+ memoryHealth.start();
1508
+
1509
+ // The backfill pump. It writes owed [feedback] signals through the
1510
+ // same `note` op on the same store — a retry on the one route, never
1511
+ // a second channel — only while health reads healthy, and wakes
1512
+ // `memory_write_backlog` past the threshold. Legacy rows (older than the
1513
+ // store's legacy cutoff, written under random names) are checked against a complete sweep
1514
+ // first (an entry whose write timed out may have landed after all).
1515
+ const health = memoryHealth;
1516
+ feedbackPump = new FeedbackMemoryPump({
1517
+ store: d.store,
1518
+ memory,
1519
+ healthy: () => health.currentState() === "healthy",
1520
+ healthState: () => health.currentState(),
1521
+ // Retired entries count as "landed": a legacy signal whose entry was
1522
+ // retired since — by the operator, or by the
1523
+ // relayout — was written once and must not be written again into
1524
+ // events/. (A relayout MOVE leaves the copy live in events/, so it
1525
+ // is found among the live entries.)
1526
+ readEntries: async () => {
1527
+ const sweep = await memory.sweep();
1528
+ return {
1529
+ entries: [...sweep.entries, ...sweep.retired],
1530
+ complete: sweep.unread.length === 0,
1531
+ };
1532
+ },
1533
+ wake: (kind, detail, message) => d.store.logAnomaly(kind, null, null, detail, message),
1534
+ log,
1535
+ intervalMs: Math.max(10, cfg.memory.backfill_interval_s) * 1000,
1536
+ backlogAfterMs: Math.max(1, cfg.memory.write_backlog_after_min) * 60_000,
1537
+ rewakeMs: Math.max(1, cfg.memory.rewake_h) * 3_600_000,
1538
+ });
1539
+ feedbackPump.start();
1540
+
1541
+ // Finding cases: the learner's context. Rendered from state.db
1542
+ // (round_findings, the outcome capture's tables, the human replies,
1543
+ // the feedback ledger) and written through the same store's
1544
+ // `putDocuments` — one batch-write under `finding-cases/` a tick, every case
1545
+ // under its own precondition — only while memory reads healthy. Its
1546
+ // backfill reads a PR's inline comments through the adapter (a read:
1547
+ // never the gate) and runs the outcome capture for PRs nothing
1548
+ // captured. Never in a round's search scope; only the daemon's lesson
1549
+ // selection reads cases.
1550
+ caseWriter = new CaseWriter({
1551
+ store: d.store,
1552
+ outcomes: d.outcomes,
1553
+ memory,
1554
+ repo: cfg.github.repo,
1555
+ botLogin: cfg.github.bot_login,
1556
+ healthy: () => health.currentState() === "healthy",
1557
+ // No case batch starts while a review round reads memory (its seed,
1558
+ // its lesson selection); every batch sent reports its embedding
1559
+ // tail there, which a seed that starts inside it waits out (within
1560
+ // its own budget).
1561
+ rounds: roundActivity,
1562
+ embedTailMsPerCase: cfg.memory.case_embed_tail_ms,
1563
+ // While a review round runs or is queued to dispatch,
1564
+ // the backfill, the late activity and history cases wait.
1565
+ reviews: () => reviewsInFlight(d.store),
1566
+ read: adapter,
1567
+ log,
1568
+ intervalMs: Math.max(10, cfg.memory.cases_interval_s) * 1000,
1569
+ backfillDays: Math.max(0, cfg.memory.cases_backfill_days),
1570
+ });
1571
+ caseWriter.start();
1572
+
1573
+ // The learner: state.db and the base clone's git in,
1574
+ // candidates and the EOD packet out; it reads the lessons in force
1575
+ // through the same store (only while memory reads healthy) and never
1576
+ // writes memory — the curator does, with `viking_write`'s typed
1577
+ // `lesson` (vendored patch 0010).
1578
+ learner = new Learner({
1579
+ store: d.store,
1580
+ root,
1581
+ eodTime: cfg.eod.time,
1582
+ timeZone: cfg.eod.timezone,
1583
+ botLogin: cfg.github.bot_login,
1584
+ baseBranch: cfg.github.base_branch,
1585
+ config: learnerKnobsOf(cfg.memory),
1586
+ git: sessions.worktrees,
1587
+ lessons: memory,
1588
+ healthy: () => health.currentState() === "healthy",
1589
+ wake: (kind, detail, message) => d.store.logAnomaly(kind, null, null, detail, message),
1590
+ log,
1591
+ });
1592
+ learner.start();
1593
+ }
1594
+
1595
+ // The archiver. Its eligibility asks which workers run (leases the
1596
+ // process table confirms; unknown holds, never "none") and the session
1597
+ // manager which births are in flight; its holds read the daemon's own
1598
+ // load watch.
1599
+ archiver = new Archiver({
1600
+ store: d.store,
1601
+ cfg: cfg.archive,
1602
+ root,
1603
+ sessionsRoot: sessions.sessionsRoot(),
1604
+ firstHealthyReconcileAt: () => d.firstHealthyReconcileAt(),
1605
+ load: () => d.load.status(),
1606
+ starvedRatio: eventLoopPolicy(cfg.daemon).starvedRatio,
1607
+ liveSessions: async () => workerLive.liveSessions(),
1608
+ birthInFlight: (pr) => workerWorld.host.birthInFlight(pr),
1609
+ // The stores that belong to no PR. The overseer is gone, so its
1610
+ // transcripts have no current file to protect and no birth record
1611
+ // (unknown = nothing moves); pi's global store lies under the
1612
+ // container user's home.
1613
+ piGlobalDir: piGlobalSessionsDir(),
1614
+ log,
1615
+ });
1616
+ archiver.start();
1617
+ if (cfg.login_refresh.enabled) {
1618
+ loginRefresh = new LoginRefreshSupervisor({
1619
+ repoRoot: root,
1620
+ agentDir,
1621
+ marginMin: cfg.login_refresh.margin_min,
1622
+ log,
1623
+ // One anomaly per failure episode, through the one durable funnel.
1624
+ raiseAnomaly: (detail) => d.store.logAnomaly("login_refresh_failed", null, null, detail),
1625
+ });
1626
+ loginRefresh.start();
1627
+ }
1628
+ // A reopened PR's archive comes back before its session is born:
1629
+ // both birth paths await this, bounded, and never refuse the birth.
1630
+ const archiveRef = archiver;
1631
+ workerWorld.host.setBeforeBirth((pr) => archiveRef.beforeBirth(pr));
1632
+
1633
+ // The worker world comes up before anything asks which workers run: the socket,
1634
+ // then whatever a previous daemon left reaped and requeued (a worker's round is
1635
+ // resumed from its JSONL by a fresh one), then the timers. Nothing is adopted.
1636
+ await workerWorld.start();
1637
+
1638
+ // Sweep stale worktrees. No tmux session is adopted: a PR with no worker
1639
+ // is idle with its JSONL, and one a worker still runs for keeps its tree.
1640
+ const active = d.store
1641
+ .allPrs()
1642
+ .filter((p) => p.state !== "closed")
1643
+ .map((p) => p.number);
1644
+ const inFlight = inFlightPrs(d.store, workerLive);
1645
+ const pre = await sessions.preflight([...active, ...inFlight]);
1646
+ log(
1647
+ `preflight: workers=${inFlight.join(",") || "none"} swept-worktrees=${pre.sweptWorktrees.join(",") || "none"}`,
1648
+ );
1649
+
1650
+ // Rounds still marked running whose worker did not survive the restart
1651
+ // are phantoms: cancel them, requeue the ones whose work was truly lost.
1652
+ // A round whose finished review the outbox still owes GitHub is not one,
1653
+ // nor is one whose round command still waits for the next worker.
1654
+ const orphans = reconcileOrphanedRounds(d.store, inFlight, {
1655
+ log,
1656
+ postingOwed: orphanPostingOwed(
1657
+ join(root, "state", "posting-receipts"),
1658
+ cfg.dry_run,
1659
+ cfg.github.repo,
1660
+ ),
1661
+ });
1662
+ if (orphans.cancelled.length) {
1663
+ log(
1664
+ `startup: cancelled orphaned rounds on #${orphans.cancelled.join(", #")}; requeued ${orphans.requeued.length}`,
1665
+ );
1666
+ }
1667
+
1668
+ // Overseer command files → validated execution (whitelist + caps).
1669
+ const leverDeps: LeverDeps = {
1670
+ store: d.store,
1671
+ levers: cfg.levers,
1672
+ timezone: cfg.ops.timezone,
1673
+ };
1674
+ const commandDeps: CommandDeps = {
1675
+ async rerunRound(pr) {
1676
+ return recoverPrForOperator(
1677
+ { store: d.store, pump: () => d.pump(), workerRuns: (n) => workerLive.runs(n) },
1678
+ pr,
1679
+ "rerun-round",
1680
+ );
1681
+ },
1682
+ async restartSession(target, deliveryKey) {
1683
+ return recoverPrForOperator(
1684
+ {
1685
+ store: d.store,
1686
+ deliveryKey,
1687
+ pump: () => d.pump(),
1688
+ // Returns only once the worker is reaped; one that outlives the kill
1689
+ // throws, which leaves ownership unchanged.
1690
+ killSession: (pr) => workerWorld.host.killSession(pr),
1691
+ workerRuns: (pr) => workerLive.runs(pr),
1692
+ // A fresh worker resumes the newest JSONL: rotate it away first.
1693
+ rotateSession: (pr) => workerWorld.host.rotateSession(pr),
1694
+ },
1695
+ target,
1696
+ "restart-session",
1697
+ );
1698
+ },
1699
+ resolveTerminalFailure(payload) {
1700
+ return resolveTerminalFailure(
1701
+ {
1702
+ store: d.store,
1703
+ getPrDisposition: (pr) => adapter.getPrDisposition(pr),
1704
+ },
1705
+ payload,
1706
+ );
1707
+ },
1708
+ // The operator's round controls on a worker-hosted PR: steer, follow-up,
1709
+ // abort, takeover, debug-round, release and the idle-PR message.
1710
+ operatorControl: workerWorld.operatorControl,
1711
+ // The two bounded levers: who is asking comes from the
1712
+ // watcher's channel, and a session's hold is capped, budgeted and
1713
+ // expiring in src/levers.ts — an operator's is not.
1714
+ pausePr(pr, reason, issuer, forMin, deliveryKey) {
1715
+ return pauseLever({ ...leverDeps, deliveryKey }, pr, reason, issuer, forMin);
1716
+ },
1717
+ resumePr(pr, issuer) {
1718
+ return resumeLever(leverDeps, pr, issuer, () => d.pump());
1719
+ },
1720
+ flagOwner(message) {
1721
+ const flags = join(root, "state", "flags");
1722
+ mkdirSync(flags, { recursive: true });
1723
+ // owner.log; older entries live on in the legacy owner-legacy.log,
1724
+ // which nothing writes.
1725
+ appendFileSync(join(flags, "owner.log"), `${new Date().toISOString()} ${message}\n`);
1726
+ d.store.logEvent("flag_owner", null, { message });
1727
+ return { ok: true };
1728
+ },
1729
+ fileImprovementPr(payload, deliveryKey) {
1730
+ return fileImprovementPr(payload, {
1731
+ deliveryKey,
1732
+ cfg,
1733
+ adapter,
1734
+ gate,
1735
+ store: d.store,
1736
+ run: gitRun,
1737
+ // The reviewed repo's base clone — the one tree with `origin`
1738
+ // pointing at the reviewed repo and the session's branch in it.
1739
+ ensureRepoDir: () => sessions.worktrees.ensureBaseClone(),
1740
+ });
1741
+ },
1742
+ fileIssue(payload, deliveryKey) {
1743
+ return fileIssue(payload, { cfg, adapter, gate, store: d.store, deliveryKey });
1744
+ },
1745
+ retryThreadDecision(commentId) {
1746
+ return d.retryThreadDecision(commentId);
1747
+ },
1748
+ // The operator's lesson kill switch — reached only for a command the
1749
+ // sealed commands/operator/ channel delivered (commands.ts refuses
1750
+ // every other issuer, twice). State in state.db.
1751
+ lessonSwitch(change, file) {
1752
+ if (!lessonLedger)
1753
+ return { ok: false, reason: "the lesson ledger is not built on this daemon yet" };
1754
+ const r = lessonLedger.apply(change, file);
1755
+ lessonStatusCache = null;
1756
+ // The validator's lesson addendum is composed only
1757
+ // while the kill switch is on — so flipping it rebuilds the agents,
1758
+ // the way a [feedback] comment does for its addendum. Suppressing
1759
+ // one lesson needs no rebuild: the selector reads it every round.
1760
+ // A rebuild that throws leaves the build stale: the 5-minute agents
1761
+ // tick retries it, and the answer says so rather than reading as
1762
+ // a validator already rebuilt.
1763
+ let rebuildFailed = false;
1764
+ if (r.changed && (change.action === "off" || change.action === "on")) {
1765
+ try {
1766
+ rebuildAgents();
1767
+ } catch (err) {
1768
+ rebuildFailed = true;
1769
+ log(
1770
+ "lessons: agents rebuild after the kill switch flipped failed — the agents tick retries it",
1771
+ err,
1772
+ );
1773
+ }
1774
+ }
1775
+ return {
1776
+ ok: true,
1777
+ changed: r.changed,
1778
+ ...(rebuildFailed
1779
+ ? { agents_rebuild: "failed — the 5-minute agents tick retries it" }
1780
+ : {}),
1781
+ lessons_off: r.lessons_off,
1782
+ suppressed: r.suppressed.length,
1783
+ ...("uri" in change ? { uri: change.uri } : {}),
1784
+ };
1785
+ },
1786
+ // The operator's archive verbs — reached only for a
1787
+ // command the sealed commands/operator/ channel delivered (commands.ts
1788
+ // refuses every other issuer, twice). Each runs under the archiver's
1789
+ // strict mutex and answers within OPERATOR_WAIT_MS; `archiver` is read
1790
+ // at call time, never captured at wiring.
1791
+ archiveRun() {
1792
+ if (!archiver)
1793
+ return { ok: false, reason: "the archiver is not built on this daemon yet" };
1794
+ return archiver.operatorRun();
1795
+ },
1796
+ archiveRestore(pr) {
1797
+ if (!archiver)
1798
+ return { ok: false, reason: "the archiver is not built on this daemon yet" };
1799
+ return archiver.operatorRestore(pr);
1800
+ },
1801
+ // The EOD curator's receipt for the day's outcomes packet.
1802
+ // `learner` is read at call time, never captured at wiring.
1803
+ curationReceipt(receipt) {
1804
+ return routeCurationReceipt(learner, receipt);
1805
+ },
1806
+ // Deploy-window drain. On = the webhook answers 200 and drops, the
1807
+ // pump starts nothing, recheck/ping-pong/EOD hold — in-flight rounds
1808
+ // finish naturally. Off is the manual lift; the automatic one is the
1809
+ // next boot's first healthy reconcile pass (daemon.ts) — except for
1810
+ // the operator's HOLD, which only an explicit off lifts. Never touches
1811
+ // GitHub, never goes near the gate.
1812
+ drain(on, issuer, forMin, deliveryKey, hold) {
1813
+ return drainLever(
1814
+ { ...leverDeps, deliveryKey },
1815
+ on,
1816
+ issuer,
1817
+ forMin,
1818
+ async () => {
1819
+ d.wakeThreadDecisions();
1820
+ await d.pump();
1821
+ },
1822
+ hold === true,
1823
+ );
1824
+ },
1825
+ };
1826
+ // The Slack concierge. Off by default; when on it owns its own
1827
+ // tier-checked command dir, the socket, and every unprompted post. It is
1828
+ // started before the legacy watcher because that watcher borrows two
1829
+ // things from it: the reply handler and the origin lookup.
1830
+ slack =
1831
+ (await startSlack({
1832
+ cfg,
1833
+ root,
1834
+ store: d.store,
1835
+ commandDeps,
1836
+ writeInbox,
1837
+ origins,
1838
+ log,
1839
+ version: packageVersion(root),
1840
+ ...(previousStatus !== undefined ? { previousStatus } : {}),
1841
+ })) ?? null;
1842
+ if (slack) log("slack: concierge live (socket mode; commands/slack tier-checked)");
1843
+
1844
+ commands = buildLocalCommandWatcher({
1845
+ root,
1846
+ deps: commandDeps,
1847
+ store: d.store,
1848
+ slack,
1849
+ log: (l) => log(l),
1850
+ });
1851
+ await commands.start();
1852
+ operatorCommands = buildOperatorCommandWatcher({
1853
+ root,
1854
+ deps: commandDeps,
1855
+ store: d.store,
1856
+ slack,
1857
+ log: (l) => log(l),
1858
+ });
1859
+ await operatorCommands.start();
1860
+
1861
+ // Webhook-URL self-heal: only under the tunnel-file ingress shape. The
1862
+ // gated PATCH rides the app-self-config class, so it executes even in
1863
+ // dry-run (AGENTS.md invariant 6) — loudly.
1864
+ if (cfg.ingress.shape === "tunnel-file") {
1865
+ webhookHeal = new WebhookHealer({
1866
+ hostnameFile: ingressHostnameFile(cfg, root),
1867
+ readConfig: () => adapter.getAppWebhookConfig(),
1868
+ updateUrl: (newUrl, oldUrl) => adapter.updateOwnWebhookUrl(newUrl, oldUrl),
1869
+ logEvent: (kind, payload) => d.store.logEvent(kind, null, payload),
1870
+ anomaly: (detail) => d.store.logAnomaly("webhook_heal", null, null, detail, detail),
1871
+ log,
1872
+ debounceMs: cfg.ingress.debounce_ms,
1873
+ sweepIntervalMs: cfg.ingress.sweep_interval_s * 1000,
1874
+ });
1875
+ webhookHeal.start();
1876
+ log(`webhookheal: watching ${ingressHostnameFile(cfg, root)} (ingress shape tunnel-file)`);
1877
+ }
1878
+
1879
+ // Daemon-clock EOD ([eod]-timezone wall time; pi schedulers deliberately unused).
1880
+ // Curation must never touch the owner's pinned entries. The watchdog
1881
+ // fingerprints the pinned area around the sweep and raises an anomaly on
1882
+ // any change. It wraps the inject rather than hooking inside
1883
+ // the timer, so a broken watchdog can never skip the sweep.
1884
+ const sweepSource = memorySweep;
1885
+ pinWatch =
1886
+ cfg.eod.pinned_watchdog && sweepSource
1887
+ ? startEodPinWatch({
1888
+ readPinned: readPinnedFromSweep(sweepSource),
1889
+ logAnomaly: (kind, pr, reason, detail, injectMessage) =>
1890
+ d.store.logAnomaly(kind, pr, reason, detail, injectMessage),
1891
+ timeZone: cfg.eod.timezone,
1892
+ digestReady: (date) => digestWritten(date, join(root, "digest")),
1893
+ windowMin: cfg.eod.pinned_watchdog_window_min,
1894
+ log,
1895
+ })
1896
+ : null;
1897
+ // The sweep is one inbox item per day: the companion's TUI pulls it and
1898
+ // runs it, so with the companion off it runs late, never not at all.
1899
+ const injectEod = inbox.eodWriter();
1900
+ eod = startEodTimer({
1901
+ timeHHMM: cfg.eod.time,
1902
+ timeZone: cfg.eod.timezone,
1903
+ readPrompt: promptReader(join(root, "prompts", "eod-sweep.md")),
1904
+ identity,
1905
+ // The day's cancellation summary rides the sweep prompt so the
1906
+ // digest actually mentions cancellations (grouped, loud first, "none"
1907
+ // explicit) — and the learner's outcomes packet, built here, at the
1908
+ // fire (a mine first when the day's has not run) — whose pointer the
1909
+ // digest carries and whose curation instructions render after the
1910
+ // seed. A learner that throws costs the packet, never the sweep.
1911
+ dayContext: async (date) => {
1912
+ const cancelled = eodDigestContext(d.store, date, cfg.eod.timezone);
1913
+ if (!learner) return cancelled;
1914
+ try {
1915
+ const prep = await learner.prepareEod(date);
1916
+ return { lines: `${cancelled}\n${prep.lines}`, curation: prep.curation };
1917
+ } catch (err) {
1918
+ log(`learner: the outcomes packet for ${date} failed`, err);
1919
+ return cancelled;
1920
+ }
1921
+ },
1922
+ onContextMissing: (date, why) =>
1923
+ log(`EOD sweep for ${date}: its day context is missing — it goes out without it`, why),
1924
+ // The curator learns the memory layout here — the seed is frozen and
1925
+ // names only some areas.
1926
+ ...(cfg.openviking.enabled ? { memoryLayout: MEMORY_LAYOUT_NOTE } : {}),
1927
+ // The seed starts §1 from "Outcomes to curate" and closes it with the
1928
+ // receipt, so a sweep the packet did not reach says why in that
1929
+ // section instead of dropping it: no learner on this daemon, or a
1930
+ // packet that failed or missed the wait.
1931
+ noCuration: (date) => renderNoPacketSection(date, learner ? "unavailable" : "off"),
1932
+ // Deploy-window drain: the sweep does not fire into a board being
1933
+ // drained for a deploy — it is deferred: the day is persisted in
1934
+ // state.db and fires once the drain lifts, across a restart too.
1935
+ shouldHold: () => d.store.drainState().draining,
1936
+ // One sweep at a time: while the pinned-area watch window of the
1937
+ // last sweep is open, the next day (a deferred one above all — they
1938
+ // queue back to back after a drain) waits behind it, so each window
1939
+ // judges its own day's work under its own date.
1940
+ sweepInFlight: () => pinWatch?.isOpen() === true,
1941
+ deferred: {
1942
+ load: () => d.store.eodDeferredDates(),
1943
+ save: (dates) => d.store.recordEodDeferred(dates),
1944
+ },
1945
+ onDeferredStoreFailed: (op, dates, err) =>
1946
+ log(
1947
+ `EOD owed-day ${op} failed${dates ? ` (owed: ${dates.join(", ") || "none"})` : ""} — scheduling continues in memory`,
1948
+ err,
1949
+ ),
1950
+ onHeld: (date, why) => {
1951
+ d.store.logEvent(
1952
+ why === "draining" ? "eod_deferred_draining" : "eod_deferred_behind_sweep",
1953
+ null,
1954
+ { date },
1955
+ );
1956
+ log(
1957
+ why === "draining"
1958
+ ? `EOD sweep for ${date} deferred — draining; it fires once the drain lifts`
1959
+ : `EOD sweep for ${date} deferred — an earlier sweep is still in flight; it fires after`,
1960
+ );
1961
+ },
1962
+ onDeferredFire: (date) => {
1963
+ d.store.logEvent("eod_deferred_fired", null, { date });
1964
+ // An owed day — deferred behind a drain or an earlier sweep, or one
1965
+ // whose fire a restart interrupted — fires only once neither holds;
1966
+ // the timer does not say which owed it.
1967
+ log(
1968
+ `EOD sweep for ${date} firing — owed from a deferral or an interrupted fire; no drain or sweep holds it now`,
1969
+ );
1970
+ },
1971
+ writeInbox: pinWatch ? pinWatch.wrapInject(injectEod) : injectEod,
1972
+ onFire: (date) => {
1973
+ log(`EOD sweep written to the companion inbox for ${date}`);
1974
+ // The receipt for the day's packet is owed from now.
1975
+ learner?.eodFired(date);
1976
+ },
1977
+ onError: (err) => log("EOD inbox write failed (retried with backoff)", err),
1978
+ // Three retries failed too: the day's curation did not happen.
1979
+ onInjectFailed: (date, err, attempts) => {
1980
+ const why = err instanceof Error ? err.message : String(err);
1981
+ d.store.logAnomaly(
1982
+ "eod_inject_failed",
1983
+ null,
1984
+ null,
1985
+ `EOD sweep for ${date} not written to the inbox after ${attempts} attempts: ${why.slice(0, 200)}`,
1986
+ [
1987
+ `EOD INBOX WRITE FAILED — the EOD sweep for ${date} could not be written to the companion inbox (${attempts} attempts, backed off).`,
1988
+ `Last error: ${why.slice(0, 300)}`,
1989
+ "That day's curation did not run: no digest, no lessons. Check the companion inbox directory and the state volume's free space, then re-run the sweep by hand if the day matters.",
1990
+ ].join("\n"),
1991
+ );
1992
+ },
1993
+ });
1994
+ log(`EOD timer armed for ${eod.nextFire().toISOString()}`);
1995
+
1996
+ // Daily heartbeat inbox item — same wall-clock machinery,
1997
+ // morning slot. At most one boot catch-up if the daemon was down at the
1998
+ // hour; the fired-date lives in the store, so restarts never double-fire.
1999
+ heartbeat = startHeartbeat({
2000
+ timeHHMM: cfg.eod.heartbeat_time,
2001
+ timeZone: cfg.eod.timezone,
2002
+ lastFired: () => d.store.lastHeartbeatDate(),
2003
+ recordFired: (date) => d.store.recordHeartbeat(date),
2004
+ writeInbox: inbox.heartbeatWriter(),
2005
+ log,
2006
+ onError: (err) => log("heartbeat injection failed", err),
2007
+ });
2008
+ log(`heartbeat armed for ${heartbeat.nextFire().toISOString()}`);
2009
+
2010
+ // Expire a stale same-day feedback addendum promptly on the
2011
+ // ops-day change — a 5-minute string compare, so yesterday's items
2012
+ // bite at most minutes into a new day, not an hour — and retries a
2013
+ // rebuild that threw (a stale build) on the same day.
2014
+ const dayCheck = setInterval(() => {
2015
+ try {
2016
+ agentsDay.tick();
2017
+ } catch (err) {
2018
+ log("feedback: day-change agents rebuild failed", err);
2019
+ }
2020
+ }, 300_000);
2021
+ dayCheck.unref?.();
2022
+ intervals.push(dayCheck);
2023
+ },
2024
+
2025
+ async onStop(reason = "shutdown") {
2026
+ memoryHealth?.stop();
2027
+ feedbackPump?.stop();
2028
+ caseWriter?.stop();
2029
+ learner?.stop();
2030
+ archiver?.stop();
2031
+ // Workers end and their commands requeue; the socket closes.
2032
+ try {
2033
+ await workerWorld.stop();
2034
+ } catch (err) {
2035
+ log("workers: shutdown failed", err);
2036
+ }
2037
+ if (loginRefresh) {
2038
+ try {
2039
+ await loginRefresh.stop();
2040
+ } catch (err) {
2041
+ log("login refresh: shutdown failed", err);
2042
+ }
2043
+ loginRefresh = null;
2044
+ }
2045
+ // The lesson selections in flight. A shadow one is cut now and records
2046
+ // nothing (`stop`): its budget — `shadow_budget_ms`, up to 20 s — does
2047
+ // not fit docker's 10 s stop grace beside the server's own 5 s, and a
2048
+ // record cut short by our stop would read as the engine's. An awaited
2049
+ // one (`on`) is not cut: it settles within `delivery_budget_ms`
2050
+ // (≤ 2 s); let it record before the server and the store go. The wait
2051
+ // follows that budget, never the shadow one.
2052
+ if (lessonSelector) {
2053
+ const selector = lessonSelector;
2054
+ lessonSelector = null;
2055
+ selector.stop();
2056
+ await Promise.race([
2057
+ selector.idle(),
2058
+ new Promise<void>((r) => setTimeout(r, cfg.memory.delivery_budget_ms + 500).unref?.()),
2059
+ ]);
2060
+ }
2061
+ for (const t of intervals) clearInterval(t);
2062
+ intervals.length = 0;
2063
+ eod?.stop();
2064
+ // Its open window polls the pinned area; drop it before the server goes.
2065
+ pinWatch?.stop();
2066
+ heartbeat?.stop();
2067
+ commands?.stop();
2068
+ operatorCommands?.stop();
2069
+ webhookHeal?.stop();
2070
+ webhookHeal = null;
2071
+ reembed?.stop();
2072
+ reembed = null;
2073
+ // SIGTERM then SIGKILL after a bounded grace — a memory engine must not
2074
+ // be able to hold the daemon's exit open.
2075
+ if (openviking) {
2076
+ try {
2077
+ await openviking.stop();
2078
+ } catch (err) {
2079
+ log("openviking: shutdown failed", err);
2080
+ }
2081
+ openviking = null;
2082
+ }
2083
+ // After the server: it may still be vectorizing on its way down.
2084
+ if (localEmbedder) {
2085
+ try {
2086
+ await localEmbedder.stop();
2087
+ } catch (err) {
2088
+ log("openviking: local embedder shutdown failed", err);
2089
+ }
2090
+ localEmbedder = null;
2091
+ }
2092
+ // Runs before the store closes, so the goodbye is ledgered; bounded by
2093
+ // the announcer itself, so it can never delay the exit.
2094
+ if (slack) {
2095
+ try {
2096
+ await slack.stop(reason);
2097
+ } catch (err) {
2098
+ log("slack: shutdown failed", err);
2099
+ }
2100
+ slack = null;
2101
+ }
2102
+ },
2103
+ });
2104
+
2105
+ // Posting-time guards live on the gate, so every posting route in
2106
+ // the daemon — session review, approval path, migration pre-check comment —
2107
+ // passes through them. They need the store, which only exists once the
2108
+ // daemon has been built; hence the setter here rather than in buildParts().
2109
+ gate.setGuard(
2110
+ makePostingGuard({
2111
+ store: daemon.store,
2112
+ adapter,
2113
+ botLogin: cfg.github.bot_login,
2114
+ guards: cfg.guards,
2115
+ log,
2116
+ }),
2117
+ );
2118
+
2119
+ return daemon;
2120
+ }
2121
+
2122
+ async function main(): Promise<void> {
2123
+ // Where this daemon runs, asked once before anything is assembled: outside
2124
+ // the release image it warns, and with no Docker its memory is off.
2125
+ const root = sandboxRoot();
2126
+ const loaded = loadConfig();
2127
+ const daemon = createProductionDaemon(
2128
+ buildParts(root, loaded, hostFacts(root, loaded.openviking.enabled)),
2129
+ );
2130
+ const shutdown = (sig: string) => {
2131
+ void (async () => {
2132
+ console.log(`[quickreviewd] ${sig} — shutting down`);
2133
+ await daemon.stop(sig);
2134
+ process.exit(0);
2135
+ })();
2136
+ };
2137
+ process.on("SIGINT", () => shutdown("SIGINT"));
2138
+ process.on("SIGTERM", () => shutdown("SIGTERM"));
2139
+ try {
2140
+ await daemon.start();
2141
+ } catch (err) {
2142
+ // A failed boot (onStart is fatal — daemon.ts) exits non-zero after an
2143
+ // orderly stop, so the container's restart policy retries it rather than
2144
+ // leaving a half-assembled daemon serving reviews without its watchers.
2145
+ console.error("[quickreviewd] startup failed — exiting", err);
2146
+ await daemon.stop("startup failed").catch(() => {});
2147
+ process.exit(1);
2148
+ }
2149
+ }
2150
+
2151
+ if (process.argv[1]?.endsWith("wire.js")) {
2152
+ void main();
2153
+ }