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