@quickchatai/quickreview 0.0.0-stage → 1.1.0

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