@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,1425 @@
1
+ // `qcr tui [pr]` entry point. Wires the real ssh runner; the optional PR
2
+ // number opens the board with that PR selected.
3
+ import { join } from "node:path";
4
+ import { liveConfigPath, loadConfig, sandboxRoot } from "../config.js";
5
+ import { App } from "./app.js";
6
+ import { powerActionFor } from "./deploytab.js";
7
+ import { ensureSshMaster } from "./connect.js";
8
+ import { MOUSE_OFF, MOUSE_ON } from "./mouse.js";
9
+ import { isFreshHealth } from "./poll.js";
10
+ import { ALT_SCREEN_OFF, ALT_SCREEN_ON, CLEAR } from "./paint.js";
11
+ import { storeHealth } from "./memory.js";
12
+ import { entryKey, findMemoryEntry, memoryOrder, memoryRefUnread, setTimelineFilter, } from "./memtab.js";
13
+ import { refOf } from "../memory/store.js";
14
+ import { isTab } from "./tabs.js";
15
+ import { laptopCompanionConfig, laptopInstallEnv, laptopLifecycle } from "./companionplan.js";
16
+ import { TUI_COMPANION_ENV, TUI_COMPANION_TMUX_SOCKET, tmuxSocketRunner } from "./tmuxsocket.js";
17
+ import { APP_BUILD_INFO, BUILD_STAMP, CORE_DIST, HotReloader, parseResumeArg, readResumeState, realReexec, RELOAD_EXIT_CODE, resumeStatePath, } from "./reload.js";
18
+ import { killInFlightSsh, runnerFor, transportOf } from "./remote.js";
19
+ import { chainOnPoll } from "./poll-hooks.js";
20
+ import { deliverMessage } from "./msg.js";
21
+ export { chainOnPoll } from "./poll-hooks.js";
22
+ /**
23
+ * `qcr tui <pr>` opens the board with that PR selected. `overseer` and `main`
24
+ * are refused with the place to go instead: the TUI never attaches to a VM
25
+ * session.
26
+ */
27
+ export function parseTarget(arg) {
28
+ if (arg === undefined || arg === "")
29
+ return { kind: "none" };
30
+ if (/^\d+$/.test(arg))
31
+ return { kind: "pr", pr: Number(arg) };
32
+ if (arg === "overseer" || arg === "main") {
33
+ return {
34
+ kind: "bad",
35
+ reason: `'${arg}' is no longer a target — the TUI never attaches to the ${arg} session; open the board and ask the companion`,
36
+ };
37
+ }
38
+ return { kind: "bad", reason: `unknown target '${arg}' — expected a PR number` };
39
+ }
40
+ export const TUI_HELP = `qcr tui — live board and operator console for the review daemon (over ssh)
41
+
42
+ usage: qcr tui [<pr>] [--no-companion] [--no-reload]
43
+
44
+ qcr tui open the board
45
+ qcr tui 5012 open the board with PR 5012 selected (⏎ focuses its pi pane)
46
+
47
+ --no-companion do not run the local companion (container, ambient feed,
48
+ alert pipeline). The layout does not change: the column
49
+ is still on the right and says "companion off"
50
+ --no-reload do not re-exec when a new dist/ build lands
51
+
52
+ layout: ONE shape, always — the tab on the left, the companion column on the
53
+ right. Nothing goes full-screen and nothing collapses the column;
54
+ anything you would have attached to a VM session for, ask the
55
+ companion.
56
+
57
+ keys: 1…8 the eight surfaces — board · memory · digest · tickets · deploy ·
58
+ metrics · feedback · scan (docs/tui.md says what each is for; the bar at the
59
+ foot of the screen shows what the current frame answers to) ·
60
+ 2 memory: j/k select · [ ] older/newer day · e p x n edit/pin/retire/new ·
61
+ 3 digest: [ ] older/newer · 4 tickets: c actionable ⇄ all ·
62
+ 5 deploy: click ▶/■ buttons (D D deploy, S S stop/start) ·
63
+ 6 metrics: w/m/a window, # <pr> ⏎ for a PR timeline ·
64
+ 7 feedback: t focus · a archived · A/x accept/reject a proposal ·
65
+ R then y reverts a feedback-derived commit (local, never pushed) ·
66
+ 8 scan: [ ] unit · p promote · d dismiss · f/F filter · t tier · P plan (space
67
+ toggles a row, ⏎ approves, Esc back) · x stop · D delta re-scan ·
68
+ companion column on every tab: type any unclaimed key (or /) to talk to
69
+ it — every byte reaches pi · wheel over the column, or PgUp/PgDn while
70
+ focused, walks its scrollback ·
71
+ drag its left edge to resize · click it to take the keyboard ·
72
+ board: j/k select · ⏎ focus the PR's pi pane · m quick-message ·
73
+ g github · u unwedge · e events overlay · I set up the repo ·
74
+ Ctrl-q release focus · Ctrl-c ×3 hard-kill the TUI ·
75
+ s reconnect ssh (GCP passphrase, once) · r refresh ·
76
+ ? full key map · q quit
77
+
78
+ troubleshooting:
79
+ ✖ unreachable ssh to the VM failed. The board names WHICH of the
80
+ three usual causes it is and prints the exact command to
81
+ run — a key missing from the agent (ssh-add …), a wedged
82
+ shared master (ssh -O exit …), or a host that is simply
83
+ not answering. Nothing is run for you; s re-checks and
84
+ reports, and offers the one GCP passphrase prompt when a
85
+ key is what is missing
86
+ ▲ DATA STALE ssh works but status.json stopped updating — the
87
+ container is down or the daemon hung (qcr-side:
88
+ ssh <host> sudo docker ps / docker logs)
89
+ keys lost in pi the outer terminal must speak csi-u (kitty/ghostty/
90
+ wezterm/iterm2); running inside an outer tmux needs
91
+ extended-keys passthrough there too
92
+ `;
93
+ export function parseTuiArgs(args) {
94
+ const { stateFile, rest } = parseResumeArg(args);
95
+ const flags = new Set(rest.filter((a) => a.startsWith("-")));
96
+ return {
97
+ target: rest.find((a) => !a.startsWith("-")),
98
+ companion: !flags.has("--no-companion"),
99
+ reload: !flags.has("--no-reload"),
100
+ resumeState: stateFile,
101
+ help: flags.has("--help") || flags.has("-h"),
102
+ };
103
+ }
104
+ /**
105
+ * The companion action channel's executor, mapped onto the App's public
106
+ * surface plus the per-tab states the install hooks return. Exported so tests
107
+ * can drive a real App through `processTuiActions`; runTui wires it inside
108
+ * the companionUp block. Actions run instantly — no confirm gate (see
109
+ * src/tui/actions.ts header).
110
+ */
111
+ export function buildTuiDriver(app, learning, metricsView, deployState) {
112
+ // One keyboard owner at a time: an action must never type into a focused
113
+ // pi pane or companion column (same rule the mouse/tab handlers follow).
114
+ // A busy App (operator input box open, terminal lent to the `s` passphrase
115
+ // prompt) is refused: a synthesized keystroke would land in the operator's
116
+ // draft or nowhere.
117
+ const prep = () => {
118
+ if (app.piFocused())
119
+ app.blurPiPane();
120
+ app.sidebar?.blur();
121
+ if (app.isBusy())
122
+ throw new Error("operator input open / terminal in use — action refused");
123
+ };
124
+ /** Press a deploy-tab run key twice (arm + confirm) and answer whether a
125
+ * run started: the tab refuses a press with a result on screen, a drain
126
+ * still owned, or a target not yet loaded, and a refused printable would
127
+ * otherwise fall through to the companion column while the action read as
128
+ * done. */
129
+ const startDeployRun = (key, kind) => {
130
+ if (deployState.run)
131
+ throw new Error(`deploy_tab ${kind} refused — ${deployState.run.running ? "a run is in flight" : "the previous result is still on screen (x closes it)"}`);
132
+ // Begin this action's own arm/confirm pair. An operator's earlier arm
133
+ // would make the first press confirm and the second reach the companion.
134
+ deployState.armedAt = undefined;
135
+ deployState.armedKind = undefined;
136
+ app.pressKey(key);
137
+ app.pressKey(key);
138
+ const started = () => deployState.run?.kind; // read after the presses
139
+ if (started() !== kind)
140
+ throw new Error(`deploy_tab ${kind} refused by the tab${app.view.noticeLine ? ` — ${app.view.noticeLine}` : ""}`);
141
+ };
142
+ const driver = {
143
+ switchTab: (tab) => {
144
+ prep();
145
+ app.tab = tab; // one vocabulary: the verb's tab name IS the App's (src/tui/tabs.ts)
146
+ app.repaint();
147
+ },
148
+ selectPr: (pr) => {
149
+ prep();
150
+ // public App API — selects + refreshes detail; a refusal leaves the tab
151
+ if (!app.selectPr(pr))
152
+ throw new Error(`#${pr} is not on the board`);
153
+ app.tab = "board";
154
+ app.repaint();
155
+ },
156
+ metricsWindow: (w) => {
157
+ prep();
158
+ app.tab = "metrics";
159
+ metricsView.drill = undefined;
160
+ metricsView.window = w === "7d" ? "d7" : w === "30d" ? "d30" : "all";
161
+ metricsView.scroll = 0;
162
+ app.repaint();
163
+ },
164
+ metricsPr: (pr) => {
165
+ prep();
166
+ app.tab = "metrics";
167
+ app.repaint();
168
+ // Drive the tab's own PR-entry state machine so the timeline FETCH runs
169
+ // (drillTo is closure-private): # opens entry, digits accumulate, ⏎ drills.
170
+ // The sequence starts from the dashboard: a half-typed operator entry
171
+ // would swallow the "#" and prefix our digits, and an open timeline
172
+ // answers to Esc only, so its "#" would reach the companion column.
173
+ metricsView.prEntry = undefined;
174
+ metricsView.drill = undefined;
175
+ metricsView.scroll = 0;
176
+ app.pressKey("#");
177
+ if (metricsView.prEntry !== "")
178
+ throw new Error(`metrics pr ${pr} refused — the metrics tab did not open its PR entry`);
179
+ for (const d of String(pr))
180
+ app.pressKey(d);
181
+ app.pressKey("\r");
182
+ },
183
+ digestDay: (date) => {
184
+ prep();
185
+ app.tab = "digest";
186
+ app.repaint();
187
+ // Page to the day through the tab's own [ / ] keys so content loads.
188
+ // files are date-named ASCENDING (parseDigestList sorts); back counts
189
+ // BACK from the newest file. An unlisted digest is a failed result —
190
+ // including when the listing itself has not happened or failed.
191
+ const files = learning.store.digest.files ?? [];
192
+ const i = files.findIndex((f) => f.startsWith(date));
193
+ if (i < 0)
194
+ throw new Error(`no digest for ${date}`);
195
+ const target = files.length - 1 - i;
196
+ const delta = target - learning.store.digest.back;
197
+ for (let k = 0; k < Math.abs(delta); k++)
198
+ app.pressKey(delta > 0 ? "[" : "]");
199
+ },
200
+ memoryView: (opts) => {
201
+ prep();
202
+ app.tab = "memory";
203
+ const mem = learning.memory;
204
+ if (opts.tag !== undefined)
205
+ setTimelineFilter(mem, opts.tag);
206
+ if (opts.day !== undefined) {
207
+ // The day is a JUMP in the day-grouped timeline: land on its first
208
+ // entry. A day with no entry under the current filter is a failed
209
+ // result, not a silent no-op.
210
+ const ordered = memoryOrder(learning.store.entries, mem.filter);
211
+ const i = ordered.findIndex((e) => e.day === opts.day);
212
+ if (i < 0)
213
+ throw new Error(`no memory entries on ${opts.day}${mem.filter ? ` under #${mem.filter}` : ""}`);
214
+ mem.sel = i;
215
+ mem.selKey = entryKey(ordered[i]);
216
+ }
217
+ app.repaint();
218
+ },
219
+ memoryPin: async (ref, pin) => {
220
+ // The companion may pin or unpin an existing entry, and nothing else
221
+ // about memory rides this channel — the only op built here is `pin` or
222
+ // `unpin` (the companion boundary). A memory write, not a keystroke: no tab
223
+ // switch and no focus change (prep is for synthesized keys). It goes
224
+ // through the one store — the same verb the tab's `p` uses — against
225
+ // the entry as a new read finds it (the tab may never have been
226
+ // opened). That read lists every tree again (the TUI's store keeps no
227
+ // sweep cache; a file the listing shows unchanged may come from its
228
+ // per-file cache), and the write re-reads the file and refuses a stale
229
+ // hash — never applied blind.
230
+ const memory = learning.store.memory;
231
+ const entry = findMemoryEntry(await memory.read(), ref);
232
+ if (!entry) {
233
+ // Not found in a read that did not read everything is not "no such
234
+ // entry": answering that would publish a fact to the companion from
235
+ // no data.
236
+ const unread = memoryRefUnread(storeHealth(memory), ref);
237
+ if (unread)
238
+ throw new Error(`memory not fully readable — '${ref}' may be there; nothing was written: ${unread}`);
239
+ throw new Error(`no memory entry '${ref}' — its id or URI, as memory lists it`);
240
+ }
241
+ if (entry.pinned === pin)
242
+ return `${entry.id} is already ${pin ? "pinned" : "unpinned"}`;
243
+ const res = await memory.apply({
244
+ op: pin ? "pin" : "unpin",
245
+ ...refOf(entry),
246
+ });
247
+ learning.store.reloadMemory();
248
+ if (!res.ok)
249
+ throw new Error(res.message);
250
+ return res.message;
251
+ },
252
+ setBoardFilter: (f) => {
253
+ prep();
254
+ app.tab = "board";
255
+ app.setBoardFilter(f);
256
+ app.repaint();
257
+ },
258
+ setMemoryFilter: (tag) => {
259
+ prep();
260
+ app.tab = "memory";
261
+ setTimelineFilter(learning.memory, tag);
262
+ app.repaint();
263
+ },
264
+ msgSession: (session, text) => deliverMessage(app, session, text),
265
+ // The companion's tier (rule 8): the same operator route the viewer's input
266
+ // line uses. A refusal or a failed write rejects, so the result file says
267
+ // so; an unconfirmed send resolves, and its label says to check first.
268
+ operatorCommand: async (command) => {
269
+ const send = app.hooks.operatorCommand;
270
+ if (!send)
271
+ throw new Error("the operator command route is not wired in this TUI");
272
+ const out = await send(command, app);
273
+ if (out.state === "refused" || out.state === "failed")
274
+ throw new Error(out.label);
275
+ return out.label;
276
+ },
277
+ deployTab: (action) => {
278
+ prep();
279
+ app.tab = "deploy";
280
+ app.repaint();
281
+ if (action === "abort") {
282
+ if (!deployState.run)
283
+ throw new Error("deploy_tab abort refused — no run in flight");
284
+ app.pressKey("x"); // kills a running run (closes a finished log otherwise)
285
+ return;
286
+ }
287
+ if (action === "deploy") {
288
+ startDeployRun("D", "deploy"); // arm+confirm inside the window = instant
289
+ return;
290
+ }
291
+ // stop/start: the tab's S button computes its own kind from the observed
292
+ // container state; refuse a mismatched request rather than silently doing
293
+ // the opposite — and refuse outright when that state has not been read,
294
+ // so a stop request can never stop a live reviewer on data nobody
295
+ // fetched.
296
+ if (deployState.containerStatus === undefined) {
297
+ throw new Error(`deploy_tab ${action} refused — the container's state has not been read yet`);
298
+ }
299
+ const kind = powerActionFor(deployState.containerStatus);
300
+ if (kind !== action)
301
+ throw new Error(`deploy_tab ${action} refused — container is ${deployState.containerStatus}`);
302
+ startDeployRun("S", action);
303
+ },
304
+ };
305
+ return driver;
306
+ }
307
+ /** First slice of the quit-time wait for an in-flight bring-up. */
308
+ /** Startup steps that exist only for the companion (the memory forward also serves the Memory tab). */
309
+ const COMPANION_ONLY_STEPS = [
310
+ "memory-namespaces",
311
+ "container",
312
+ "rollover",
313
+ "companion-pi",
314
+ ];
315
+ const BRING_UP_QUIT_GRACE_MS = 250;
316
+ /** True if the promise settled inside `ms`. The timer is unref'd, so a slow
317
+ * bring-up can never be the thing that holds the process open. */
318
+ async function settledWithin(p, ms) {
319
+ let timer;
320
+ const timeout = new Promise((resolve) => {
321
+ timer = setTimeout(() => resolve(false), ms);
322
+ timer.unref?.();
323
+ });
324
+ const settled = await Promise.race([p.then(() => true), timeout]);
325
+ if (timer)
326
+ clearTimeout(timer);
327
+ return settled;
328
+ }
329
+ /**
330
+ * Clean quit stops the container (conversation + auth survive on volumes).
331
+ * The question is whether THIS start put a container up — true from the
332
+ * moment `ensureUp` returns, not only once the rollover and the pi spawn
333
+ * behind it finished: a quit whose bring-up grace ran out mid-rollover would
334
+ * otherwise leave a freshly started container running. A failed stop is
335
+ * swallowed — leaving it running is harmless, the next start adopts it.
336
+ */
337
+ export async function stopCompanionOnQuit(opts) {
338
+ if (!opts.containerStarted)
339
+ return;
340
+ opts.write("qcr tui: stopping the companion container…\n");
341
+ try {
342
+ await opts.stop();
343
+ }
344
+ catch {
345
+ /* leaving it running is harmless: the next start adopts */
346
+ }
347
+ }
348
+ /**
349
+ * `qcr tui` — the whole startup, in two phases.
350
+ *
351
+ * Phase 0 is local only: parse, config, wire, and paint. Nothing external —
352
+ * no ssh, no docker, no network, no directory scan — may run before the first
353
+ * frame, and the frame it paints is the real layout with every unfetched value
354
+ * marked unknown. The companion column's width comes from config, the only
355
+ * layout-affecting fact, so nothing jumps when the container later arrives.
356
+ *
357
+ * Phase 1 is the bring-up, streaming in behind that frame and reporting into
358
+ * the companion column: connection triage, memory forward, namespaces,
359
+ * container, day rollover, pi. It never blocks the board, and a step that
360
+ * fails is a degraded mode, never a reason the TUI does not open.
361
+ */
362
+ export async function runTui(argv = [], deps = {}) {
363
+ const args = parseTuiArgs(typeof argv === "string" ? [argv] : argv);
364
+ if (args.help) {
365
+ console.log(TUI_HELP);
366
+ return;
367
+ }
368
+ const target = parseTarget(args.target);
369
+ if (target.kind === "bad") {
370
+ console.error(`qcr tui: ${target.reason}`);
371
+ process.exitCode = 2;
372
+ return;
373
+ }
374
+ const out = deps.out ?? process.stdout;
375
+ const input = deps.input ?? process.stdin;
376
+ const exit = deps.exit ?? ((code) => process.exit(code));
377
+ if (!out.isTTY || !input.isTTY) {
378
+ console.error("qcr tui needs an interactive terminal");
379
+ process.exitCode = 2;
380
+ return;
381
+ }
382
+ // ---- phase 0: local only, ending in the first frame -----------------------
383
+ const loaded = deps.config ?? loadConfig();
384
+ // In the containerised TUI the companion is a container the host wrapper
385
+ // runs, reached through a shared tmux socket. Its channel dirs are the
386
+ // host's, named by the wrapper in env; nothing here can run docker.
387
+ const socketMode = process.env[TUI_COMPANION_ENV] === TUI_COMPANION_TMUX_SOCKET;
388
+ const full = socketMode
389
+ ? { ...loaded, companion: laptopCompanionConfig(process.env, loaded.companion) }
390
+ : loaded;
391
+ const cfg = full.tui;
392
+ // One transport decision for every remote op — ssh to the VM, or the same
393
+ // commands against local docker on the laptop topology.
394
+ const runner = runnerFor(cfg);
395
+ const app = new App(cfg, runner, out, input);
396
+ // The terminal must come back whatever kills us — raw mode + alt screen
397
+ // surviving a crash is exactly the "only closing the terminal works" trap.
398
+ const restoreTerminal = () => {
399
+ try {
400
+ if (input.isTTY)
401
+ input.setRawMode(false);
402
+ }
403
+ catch {
404
+ /* stdin already gone */
405
+ }
406
+ out.write(MOUSE_OFF + ALT_SCREEN_OFF);
407
+ };
408
+ // A reload under the supervisor exits with RELOAD_EXIT_CODE so the next
409
+ // build takes over this terminal state — the alt screen, mouse reporting
410
+ // and raw mode stay exactly as they are. Restoring here would flash the
411
+ // shell between builds. Every other exit restores.
412
+ process.on("exit", (code) => {
413
+ if (code === RELOAD_EXIT_CODE)
414
+ return;
415
+ restoreTerminal();
416
+ });
417
+ process.on("SIGINT", () => {
418
+ restoreTerminal();
419
+ exit(130);
420
+ });
421
+ process.on("SIGTERM", () => {
422
+ restoreTerminal();
423
+ exit(143);
424
+ });
425
+ process.on("uncaughtException", (err) => {
426
+ restoreTerminal();
427
+ console.error("qcr tui crashed:", err);
428
+ exit(1);
429
+ });
430
+ /** Suspend the TUI, run something on the real terminal, come back. */
431
+ const onRealTerminal = (fn) => {
432
+ // Quit can land mid-flight (the operator presses `s`, then Ctrl-C twice
433
+ // while the probes run). The terminal is already restored by then, so
434
+ // going back into the alt screen would leave a blank alternate screen with
435
+ // no TUI behind it. Run the work plainly and leave the terminal alone.
436
+ if (app.isStopped()) {
437
+ fn();
438
+ return;
439
+ }
440
+ app.suspend();
441
+ try {
442
+ if (input.isTTY)
443
+ input.setRawMode(false);
444
+ }
445
+ catch {
446
+ /* keep going — fn may still work */
447
+ }
448
+ out.write(MOUSE_OFF + ALT_SCREEN_OFF);
449
+ try {
450
+ fn();
451
+ }
452
+ finally {
453
+ // Deliberate, and NOT safe to "fix": this return also swallows a throw
454
+ // from fn(), which is what we want on the quit path — the alt screen is
455
+ // already gone, and restoring it to report an error would repaint over
456
+ // the shell the operator is back in. Restructuring so the return sits
457
+ // outside the finally would change that, and error propagation on quit
458
+ // is behaviour, not style.
459
+ // eslint-disable-next-line no-unsafe-finally
460
+ if (app.isStopped())
461
+ return; // quit happened while fn ran
462
+ if (input.isTTY)
463
+ input.setRawMode(true);
464
+ out.write(ALT_SCREEN_ON + CLEAR + MOUSE_ON);
465
+ app.resume();
466
+ }
467
+ };
468
+ const { DurableDeployProcess } = await import("./deploy-process.js");
469
+ const durableDeploy = new DurableDeployProcess(sandboxRoot());
470
+ app.hooks.deployRun = (_theApp, runId, version, onData, onExit) => durableDeploy.startOrAttach(runId, version, onData, onExit);
471
+ app.view.repo = full.github.repo;
472
+ const { spawn } = await import("node:child_process");
473
+ const { openUrlDetached } = await import("./openurl.js");
474
+ const { existsSync, mkdirSync, readFileSync, writeFileSync } = await import("node:fs");
475
+ app.hooks.openUrl = (url) => openUrlDetached(url, (line) => app.notice(line));
476
+ const marker = join(sandboxRoot(), "state", "tui-help-shown");
477
+ app.hooks.helpSeen = {
478
+ get: () => existsSync(marker),
479
+ set: () => {
480
+ mkdirSync(join(sandboxRoot(), "state"), { recursive: true });
481
+ writeFileSync(marker, new Date().toISOString());
482
+ },
483
+ };
484
+ // The companion column is layout: its width depends on nothing but the
485
+ // terminal, so wiring it here — before anything asks docker whether the
486
+ // container is running — is what makes the first frame the real one. The
487
+ // sidebar is only `available()` once the container answers, and the
488
+ // container fills a column that was already the right size.
489
+ const { CompanionSidebar, restorePreferredSidebarWidth } = await import("./sidebar.js");
490
+ const { CompanionLifecycle, realCompanionFs, realDockerRunner, tmuxSocketPath } = await import("./companion.js");
491
+ const { NativeCompanion, SelectingCompanion, nativeAgentDir, probeDocker, UNSANDBOXED_WARNING } = await import("./nativecompanion.js");
492
+ const { configHome: companionConfigHome } = await import("../install.js");
493
+ const { MEMORY_OFF_NO_DOCKER } = await import("../memory/health.js");
494
+ // Is Docker answering here? Asked once, in the bring-up — never before the
495
+ // first frame — and shared by the companion's choice and the memory forward.
496
+ let dockerProbe;
497
+ const dockerUp = () => (dockerProbe ??= probeDocker(realDockerRunner).catch(() => false));
498
+ // Docker answering → the container companion; else the native one on this
499
+ // host, with the same guards and an "unsandboxed" warning
500
+ // (nativecompanion.ts). The containerised TUI keeps its wrapper-run
501
+ // container: it has no Docker of its own to ask.
502
+ const containerLifecycle = new CompanionLifecycle(full.companion, realDockerRunner);
503
+ const selecting = socketMode
504
+ ? undefined
505
+ : new SelectingCompanion(containerLifecycle, realDockerRunner, {
506
+ dockerUp,
507
+ makeNative: () => new NativeCompanion(containerLifecycle, {
508
+ appRoot: sandboxRoot(),
509
+ agentDir: nativeAgentDir(),
510
+ configHome: companionConfigHome(),
511
+ }),
512
+ onNative: () => {
513
+ app.sidebar?.setHeader({ unsandboxed: true });
514
+ app.notice(`companion: ${UNSANDBOXED_WARNING}`);
515
+ },
516
+ });
517
+ // The one seam the lifecycle, the column and the rollover reach the companion
518
+ // through: docker exec, the native host's tmux, or tmux -S over the shared
519
+ // socket.
520
+ const companionDocker = selecting
521
+ ? selecting.runner
522
+ : tmuxSocketRunner(tmuxSocketPath(full.companion), full.companion.container_name);
523
+ const companion = selecting ?? laptopLifecycle(process.env, companionDocker, loaded.companion, realCompanionFs);
524
+ // The companion's read-only GitHub token, minted here from the App
525
+ // credential this host holds and handed over as an env file in a dir the
526
+ // container mounts read-only. Refreshed while the TUI runs (it lives an
527
+ // hour); constructed now, first minted in the bring-up — it is network.
528
+ const { CompanionGithubToken, tokenOutcomeLine } = await import("./ghread.js");
529
+ const { companionGithubDir } = await import("../install.js");
530
+ const ghToken = new CompanionGithubToken({
531
+ dir: companionGithubDir(socketMode ? laptopInstallEnv(process.env) : undefined),
532
+ repo: full.github.repo,
533
+ secretsDir: full.github.secrets_dir,
534
+ });
535
+ let configMirrorTimer;
536
+ const CONFIG_MIRROR_EVERY_MS = 30_000;
537
+ const companionOn = args.companion && full.companion.enabled;
538
+ let companionUp = false;
539
+ /** `ensureUp` returned: a container is running on this start's account,
540
+ * whether or not the rollover and pi spawn behind it ever finish. */
541
+ let companionContainerStarted = false;
542
+ let companionError;
543
+ // The column exists in every configuration: `--no-companion` and
544
+ // `[companion] enabled = false` decide what runs and what the column says,
545
+ // never whether the right half of the screen is there.
546
+ app.sidebar = new CompanionSidebar(full.companion, companionDocker, {
547
+ available: () => companionUp,
548
+ onUpdate: () => app.repaint(),
549
+ // pi's `/copy` inside the container lands in a tmux paste buffer; the
550
+ // column forwards it here, to the host clipboard.
551
+ onCopy: (text) => app.copyText(text, "the companion"),
552
+ });
553
+ if (!companionOn)
554
+ app.sidebar.setOff(args.companion ? "[companion] enabled = false" : "--no-companion");
555
+ // Bring-up progress goes in that column: it is empty at this moment and
556
+ // already the right shape, so it costs no new surface — and the lines are
557
+ // never written to the normal screen, where the alt screen's clear would
558
+ // erase them (a visible flicker). With the companion off the same column
559
+ // carries the skipped steps, and stays as the place that says why it is off.
560
+ const { StartupProgress } = await import("./startup.js");
561
+ const progress = new StartupProgress(undefined, {
562
+ onUpdate: (p) => {
563
+ // Kept after the bring-up settles on purpose: a `✗ companion container ·
564
+ // docker is not running` is the most useful thing the column can show,
565
+ // and clearing it would delete the diagnosis a second after it appeared.
566
+ // The hand-off is the sidebar's `mirrored` latch — the checklist yields
567
+ // to pi's first real capture and never comes back.
568
+ app.sidebar?.setStartup(p.lines());
569
+ app.repaint();
570
+ },
571
+ });
572
+ app.sidebar.setStartup(progress.lines());
573
+ // No attach hooks: the TUI never attaches to a VM session — see AppHooks.
574
+ const { installMessageHooks } = await import("./msg.js");
575
+ const { LiveControlSender, installLiveControlHooks } = await import("./livecontrol.js");
576
+ // One control sender for the viewer's input line and for `m` on a worker PR.
577
+ const control = new LiveControlSender(app.cfg, app.ssh);
578
+ installLiveControlHooks(app, control);
579
+ installMessageHooks(app, undefined, control);
580
+ const { installUnwedgeHooks } = await import("./unwedge.js");
581
+ installUnwedgeHooks(app);
582
+ // `I` asks the companion to run its setup skill — one inject into
583
+ // its own column (drained now, or at its next start) — and hands it the
584
+ // keyboard. The same ask `qcr setup` makes from a shell.
585
+ const { requestSetup, commitRequestedProfiles, commitLine } = await import("./setupentry.js");
586
+ const { companionInjectDir } = await import("./companion.js");
587
+ app.hooks.setup = (theApp) => {
588
+ try {
589
+ requestSetup({ injectDir: companionInjectDir(full.companion) });
590
+ }
591
+ catch (err) {
592
+ theApp.notice(`setup: the request could not be written — ${String(err)}`);
593
+ return;
594
+ }
595
+ theApp.notice(companionUp
596
+ ? "setup: asked the companion — it takes it from here"
597
+ : "setup: asked — the companion picks it up when it starts");
598
+ theApp.openCompanion();
599
+ };
600
+ // The profile commits setup asked for, carried out here, on the config
601
+ // mirror's timer — the container cannot see the config home's .git (the
602
+ // home holds secrets/, so it is never mounted whole).
603
+ const commitProfiles = () => {
604
+ for (const r of commitRequestedProfiles())
605
+ app.notice(commitLine(r));
606
+ };
607
+ // The four learning surfaces — [2] memory, [3] digest, [4] tickets,
608
+ // [7] feedback — over one store. Accept-verdict git actions run against
609
+ // this working copy (the canonical repo); the [sink] config decides where
610
+ // the defect auto-filer writes (wayfinder tracker, gated github command
611
+ // channel, or none) and which effort the ticket projection reads.
612
+ const { installLearningTabs } = await import("./learning.js");
613
+ const { execFile } = await import("node:child_process");
614
+ const gitRun = (cmd, args2, stdin) => new Promise((res, rej) => {
615
+ const child = execFile(cmd, args2, { cwd: sandboxRoot() }, (err, stdout, stderr) => {
616
+ if (err)
617
+ rej(new Error(stderr.trim() || err.message));
618
+ else
619
+ res(stdout);
620
+ });
621
+ if (stdin !== undefined)
622
+ child.stdin?.end(stdin);
623
+ else
624
+ child.stdin?.end();
625
+ });
626
+ // One memory store for the whole TUI: OpenViking on the VM, reached over
627
+ // the memory forward's loopback listener, holding both user keys. Every
628
+ // memory surface in the TUI shares this instance — a second one would mean
629
+ // a second sweep and a second health opinion.
630
+ const { tuiMemoryStore, tuiMemoryBaseUrl } = await import("./memory.js");
631
+ // The companion namespace's retired URIs, published into the state home
632
+ // the companion mounts — its recall excludes them.
633
+ const { MEMORY: COMPANION_MEMORY } = await import("./companion.js");
634
+ const { stateHome: memStateHome } = await import("../install.js");
635
+ const memStore = tuiMemoryStore({
636
+ retiredFiles: { companion: join(memStateHome(), COMPANION_MEMORY.retiredFile) },
637
+ });
638
+ // The TUI brings the memory forward up itself, so the Memory tab works
639
+ // without the operator installing the systemd unit first. It adopts a
640
+ // forward that is already listening (the unit, or another qcr tui) and stops
641
+ // only one it started — see memforward.ts for why that split matters.
642
+ // Constructed here, started in the bring-up: `ensure()` probes over HTTP.
643
+ const { MemoryForward, forwardUsable, tuiMemoryContainer } = await import("./memforward.js");
644
+ const memForward = new MemoryForward({
645
+ baseUrl: tuiMemoryBaseUrl(),
646
+ repoRoot: sandboxRoot(),
647
+ // The forward follows the same [tui] target knob — local mode reaches the
648
+ // daemon container's loopback engine via plain docker exec.
649
+ local: transportOf(cfg) === "local",
650
+ sudoDocker: cfg.sudo_docker,
651
+ sshHost: cfg.ssh_host,
652
+ // OpenViking's own container: the memory sidecar once the VM flips
653
+ // `[openviking] endpoint = "remote"`, else the daemon container.
654
+ container: tuiMemoryContainer(cfg),
655
+ // A local engine runs in Docker; with none here, memory is off — said
656
+ // once as the forward's state, never retried as a failure.
657
+ ...(!socketMode && transportOf(cfg) === "local"
658
+ ? {
659
+ offReason: async () => ((await dockerUp()) ? undefined : MEMORY_OFF_NO_DOCKER),
660
+ }
661
+ : {}),
662
+ log: (line) => app.notice(line),
663
+ });
664
+ const { buildIssueSink, ticketProjection } = await import("./issuesink.js");
665
+ const { readEffortTickets } = await import("./trackertickets.js");
666
+ const projection = ticketProjection(full);
667
+ const { RealDeployFetcher, installDeployHooks, powerSpawnArgv, realLocalRunner } = await import("./deploytab.js");
668
+ const { FileDeployCeremonyStore } = await import("./deploy-ceremony.js");
669
+ const localRunner = realLocalRunner(sandboxRoot());
670
+ // The change register — every feedback-derived commit (identified by its
671
+ // `Ratified-in-session:` trailer), newest first, with a one-key revert on
672
+ // the feedback tab. It reads and writes the local checkout only, through
673
+ // the same read-only local-git seam the deploy tab uses; nothing here
674
+ // touches the VM, GitHub or the gate. Persisted under state/ so the list is
675
+ // on screen before git has been asked anything.
676
+ const { ChangeLog, ChangeRegisterStore } = await import("./changelog-store.js");
677
+ const { stateHome } = await import("../install.js");
678
+ const { PIN_PROPOSALS_FILE, parsePinProposals } = await import("../setup/pins.js");
679
+ const learning = installLearningTabs(app, {
680
+ memory: memStore,
681
+ // The entries setup proposed to pin, marked ☆ on the memory tab.
682
+ readPinProposals: () => {
683
+ try {
684
+ return parsePinProposals(readFileSync(join(stateHome(), PIN_PROPOSALS_FILE), "utf8"));
685
+ }
686
+ catch (err) {
687
+ return err.code === "ENOENT"
688
+ ? { state: "absent" }
689
+ : { state: "unreadable", error: String(err) };
690
+ }
691
+ },
692
+ forward: memForward,
693
+ gitRun,
694
+ repoRoot: sandboxRoot(),
695
+ configPath: liveConfigPath(),
696
+ sink: buildIssueSink(full, app.ssh, memStore),
697
+ issueLabel: full.github.issue_label,
698
+ eod: { time: full.eod.time, timezone: full.eod.timezone },
699
+ changes: new ChangeLog(new ChangeRegisterStore(join(sandboxRoot(), "state", "changes")), localRunner),
700
+ ...(projection
701
+ ? {
702
+ readTickets: () => readEffortTickets(projection.root, projection.effort),
703
+ ticketsEffort: projection.effort,
704
+ }
705
+ : {}),
706
+ });
707
+ app.hooks.powerRun = (_theApp, action, onData, onExit) => {
708
+ // docker stop/start on the VM (or local docker in local mode), streamed
709
+ // into the tab like a deploy.
710
+ const cmd = powerSpawnArgv(cfg, action);
711
+ const child = spawn(cmd[0], cmd.slice(1), { stdio: ["ignore", "pipe", "pipe"] });
712
+ onData(`$ ${cmd.join(" ")}\n`);
713
+ child.stdout.on("data", (b) => onData(b.toString("utf8")));
714
+ child.stderr.on("data", (b) => onData(b.toString("utf8")));
715
+ child.on("close", (code) => onExit(code));
716
+ child.on("error", (err) => {
717
+ onData(`\nspawn failed: ${String(err)}\n`);
718
+ onExit(1);
719
+ });
720
+ return {
721
+ kill: () => {
722
+ try {
723
+ child.kill("SIGTERM");
724
+ }
725
+ catch {
726
+ /* already gone */
727
+ }
728
+ },
729
+ };
730
+ };
731
+ const deployState = installDeployHooks(app, new RealDeployFetcher(cfg, runner, localRunner), undefined, undefined, new FileDeployCeremonyStore(join(sandboxRoot(), "state", "tui-deploy-ceremony.json")));
732
+ const { installMetricsHooks, localHeadProbe } = await import("./metricstab.js");
733
+ // The metrics OPERATIONS card answers "deploy needed?" through the same
734
+ // read-only local-git seam the deploy tab uses.
735
+ const metricsView = installMetricsHooks(app, undefined, localHeadProbe(localRunner));
736
+ // `[8] scan` — the scan report, read from the state home the
737
+ // companion writes. Its two request files (the stage-2 start, the stop)
738
+ // are answered by the companion's scan belt; promote, dismiss and delta go
739
+ // to the companion as the operator's asks, through the setup entry's inject
740
+ // channel.
741
+ const { installScanTab, ScanReportStore } = await import("./scantab.js");
742
+ const { writeCompanionAsk } = await import("./setupentry.js");
743
+ installScanTab(app, {
744
+ store: new ScanReportStore(stateHome()),
745
+ ask: (text, verb) => {
746
+ try {
747
+ writeCompanionAsk({
748
+ injectDir: companionInjectDir(full.companion),
749
+ suffix: `scan-${verb}`,
750
+ text,
751
+ });
752
+ }
753
+ catch (err) {
754
+ return { ok: false, line: `✖ scan ${verb}: the ask could not be written — ${String(err)}` };
755
+ }
756
+ return {
757
+ ok: true,
758
+ line: companionUp
759
+ ? `scan ${verb}: asked the companion — the report updates when it has acted`
760
+ : `scan ${verb}: asked — the companion picks it up when it starts`,
761
+ };
762
+ },
763
+ companionUp: () => !companionOn || companionError !== undefined ? false : companionUp ? true : undefined,
764
+ });
765
+ // Finished-round evidence, copied down from the VM into a local store the
766
+ // companion reads read-only. Its dir is created in the bring-up,
767
+ // before the container starts — the mount source must exist first, or docker
768
+ // invents it root-owned and the copier can never write into it.
769
+ const { EvidenceCopier, EvidenceStore, SWEEP_PROGRESS_FILE, SweepProgressFile, evidenceWhyIntervalMs, } = await import("./evidence.js");
770
+ const evidence = new EvidenceStore(full.companion.evidence_dir);
771
+ // Ambient feed + alert pipeline + startup healing. All of it hangs
772
+ // off companionOn, so --no-companion really means "none of this runs" —
773
+ // the column itself is not part of "this": it is layout, and it is always
774
+ // there.
775
+ const { companionStarter, installCompanionTabHooks } = await import("./companiontab.js");
776
+ // The companion sidebar is the only companion surface. Mirror + send go
777
+ // through the same local docker seam the lifecycle uses; refresh piggybacks
778
+ // the app poll (the sidebar's own 500ms timer only runs focused/after-send).
779
+ chainOnPoll(app, (a) => void a.sidebar?.refresh());
780
+ // The header carries what the sidebar cannot know for itself: whose day it
781
+ // is (the companion rolls over on the ops-timezone boundary, so a stale day
782
+ // in the header is the first visible sign a rollover was missed) and whether
783
+ // memory is actually reachable — a companion talking with no memory behind
784
+ // it looks identical to a healthy one until you ask it something.
785
+ const { todayDate } = await import("./rollover.js");
786
+ chainOnPoll(app, (a) => {
787
+ const fwd = memForward.view();
788
+ // A native companion has no memory wiring: no Docker, no sidecar.
789
+ const noDocker = selecting?.kind === "native" || (fwd.state === "off" && fwd.detail === MEMORY_OFF_NO_DOCKER);
790
+ a.sidebar?.setHeader({
791
+ day: todayDate(new Date(), process.env, full.ops.timezone),
792
+ memory: noDocker
793
+ ? "off"
794
+ : fwd.state === "failed"
795
+ ? "degraded"
796
+ : fwd.state === "off"
797
+ ? "off"
798
+ : "ok",
799
+ memoryNote: noDocker ? MEMORY_OFF_NO_DOCKER : undefined,
800
+ });
801
+ });
802
+ // The evidence copier rides the existing poll — no second scheduler,
803
+ // and deliberately not awaited. `sweep()` is re-entrant-safe (a sweep in
804
+ // flight makes the next call a no-op) and every remote read is killed at
805
+ // `evidence_timeout_s`, so a wedged ssh control master stalls the copier and
806
+ // nothing else: the poll keeps polling and the board keeps painting. Runs
807
+ // whether or not the companion is up — the record is worth keeping either
808
+ // way, and it is what the companion will read when it next starts.
809
+ //
810
+ // Its per-PR progress lives beside the TUI's other state files,
811
+ // so a re-exec (every local build) resumes instead of re-running `qcr why`
812
+ // for every PR, and its `why` execs are paced by `evidence_why_interval_s`
813
+ // and held while the board's last status shows a drain or a stale heartbeat.
814
+ const evidenceCopier = new EvidenceCopier(cfg, evidence, runner, {
815
+ sessionDir: full.pi.session_dir,
816
+ timeoutMs: Math.max(5, cfg.evidence_timeout_s) * 1000,
817
+ whyIntervalMs: evidenceWhyIntervalMs(cfg),
818
+ progress: new SweepProgressFile(join(sandboxRoot(), "state", SWEEP_PROGRESS_FILE)),
819
+ onNotice: (text) => app.notice(text),
820
+ });
821
+ chainOnPoll(app, (a) => {
822
+ void evidenceCopier.sweep(a.view.snapshot).catch(() => {
823
+ /* best-effort — a failed copy is retried on the next poll, never fatal */
824
+ });
825
+ });
826
+ // The durable candidate list behind the `survey` skill. Derived from what
827
+ // is already on this disk — the poll snapshot plus the evidence store's
828
+ // round manifests — so it costs no remote reads and rides the same poll. It lands
829
+ // in the evidence dir, which is already mounted `/evidence:ro`, so the
830
+ // companion reads `/evidence/candidates.json` over an existing channel and
831
+ // cannot write to it.
832
+ //
833
+ // Judged-ness comes back the other way, out of the `#adjudication` memory
834
+ // entries `adjudicate` already mandates — one record, one direction, and no
835
+ // second write path from the companion. It is read at a slow cadence because
836
+ // memory is an HTTP hop and the list only needs it to stop re-offering
837
+ // something settled.
838
+ const { CandidateStore, CandidateSweeper, adjudicationsFrom } = await import("./candidates.js");
839
+ const candidates = new CandidateStore(full.companion.evidence_dir);
840
+ let adjudicationsAt = 0;
841
+ let adjudicationsCache = new Map();
842
+ const candidateSweeper = new CandidateSweeper(candidates, {
843
+ evidenceDir: evidence.dir,
844
+ onNotice: (text) => app.notice(text),
845
+ adjudications: async () => {
846
+ // 5 minutes: an adjudication is a human-scale event, and a stale map only
847
+ // re-offers something already judged for one more cycle.
848
+ if (Date.now() - adjudicationsAt < 300_000)
849
+ return adjudicationsCache;
850
+ adjudicationsAt = Date.now();
851
+ adjudicationsCache = adjudicationsFrom(await memStore.read());
852
+ return adjudicationsCache;
853
+ },
854
+ });
855
+ chainOnPoll(app, (a) => {
856
+ void candidateSweeper.sweep(a.view.snapshot).catch(() => {
857
+ /* best-effort — the survey is rebuilt from scratch on the next poll */
858
+ });
859
+ });
860
+ // A reviewer-memory incident older than
861
+ // `[memory] incident_ticket_after_min` becomes one issue-tracker ticket
862
+ // (actor qcr-tui, deduped by the incident's start), read from status.json —
863
+ // never from memory, which is the thing that is down (the `#bug` filer reads
864
+ // memory, so it could never report this). Independent of the companion:
865
+ // the board banner and this ticket reach a person with the column off.
866
+ const { maybeFileCurationIncident, maybeFileMemoryIncident, newIncidentFilerMemo } = await import("./memincident.js");
867
+ const incidentMemo = newIncidentFilerMemo();
868
+ const incidentTracker = full.sink.kind === "wayfinder" && full.sink.root && full.sink.effort && full.sink.parent
869
+ ? { root: full.sink.root, effort: full.sink.effort, parent: full.sink.parent }
870
+ : undefined;
871
+ /** One incident filer's notices: a filing, a missing tracker, a failure —
872
+ * each distinct line once, so a tracker that keeps failing does not repaint
873
+ * the notice every poll. */
874
+ const incidentNotifier = (what) => {
875
+ let last = "";
876
+ return (a, out) => {
877
+ const line = out.kind === "filed"
878
+ ? `${what} filed as ${out.ticketId} (${out.file})`
879
+ : out.kind === "no-tracker" || out.kind === "failed"
880
+ ? out.line
881
+ : "";
882
+ if (line !== "" && line !== last) {
883
+ last = line;
884
+ a.notice(line);
885
+ }
886
+ };
887
+ };
888
+ const incidentNotice = incidentNotifier("memory incident");
889
+ chainOnPoll(app, (a) => {
890
+ const out = maybeFileMemoryIncident(incidentTracker, a.view.snapshot?.memory, {
891
+ fresh: isFreshHealth(a.view.health),
892
+ now: new Date(),
893
+ afterMs: full.memory.incident_ticket_after_min * 60_000,
894
+ memo: incidentMemo,
895
+ });
896
+ incidentNotice(a, out);
897
+ });
898
+ // The learner's EOD curation that did not reach memory (no receipt, or
899
+ // lessons memory does not hold) — the same person path: one
900
+ // ticket per curation incident (its oldest unresolved failure) older than
901
+ // the same threshold, from status.json `memory.curation`.
902
+ const curationMemo = newIncidentFilerMemo();
903
+ const curationNotice = incidentNotifier("EOD curation incident");
904
+ chainOnPoll(app, (a) => {
905
+ const out = maybeFileCurationIncident(incidentTracker, a.view.snapshot?.memory, {
906
+ fresh: isFreshHealth(a.view.health),
907
+ now: new Date(),
908
+ afterMs: full.memory.incident_ticket_after_min * 60_000,
909
+ memo: curationMemo,
910
+ });
911
+ curationNotice(a, out);
912
+ });
913
+ // ---- the connection triage ------------------------------------------------
914
+ //
915
+ // The TUI does the three-way ssh triage behind the first frame:
916
+ // `ensureSshMaster` before the alt screen would be both a network call
917
+ // gating the first paint and, on a bad day, an interactive prompt. Nothing
918
+ // here is self-healing by design; the TUI reports the cause and the exact
919
+ // command, and `s` re-runs the check.
920
+ const { diagnosisLine, triageConnection } = await import("./sshtriage.js");
921
+ const applyDiagnosis = (d) => {
922
+ app.view.connection = d;
923
+ if (d.kind === "ok" || d.kind === "local")
924
+ progress.done("ssh", d.cause);
925
+ else
926
+ progress.fail("ssh", diagnosisLine(d));
927
+ app.notice(d.kind === "ok" || d.kind === "local" ? `✓ ${d.cause}` : `✖ ${diagnosisLine(d)}`);
928
+ app.repaint();
929
+ };
930
+ // `s` starts its own async chain, independent of the bring-up. Tracked for
931
+ // the same reason: quit must not tear the terminal down underneath it.
932
+ let pendingReconnect = Promise.resolve();
933
+ app.hooks.reconnect = () => {
934
+ progress.begin("ssh", "re-checking");
935
+ app.notice("checking the connection to the VM…");
936
+ pendingReconnect = (async () => {
937
+ let d = await triageConnection(cfg);
938
+ // A missing key is the one class the operator can clear from here: `s`
939
+ // is them asking for the single native passphrase prompt. A wedged
940
+ // master or a dead host is reported with its command instead — prompting
941
+ // against either would just hang.
942
+ if (d.kind === "no-agent-key" || d.kind === "unknown") {
943
+ onRealTerminal(() => {
944
+ ensureSshMaster(cfg);
945
+ });
946
+ d = await triageConnection(cfg);
947
+ }
948
+ applyDiagnosis(d);
949
+ // Awaited, not fired and forgotten: it is part of "re-run the check and
950
+ // report the new outcome", and quit waits for this chain.
951
+ await app.poller.tick();
952
+ })().catch((err) => app.notice(`✖ ssh check failed: ${String(err instanceof Error ? err.message : err)}`));
953
+ };
954
+ // ---- the bring-up: everything external, behind the first frame ------------
955
+ const startCompanion = async () => {
956
+ try {
957
+ // Before the container, never after: the companion's pi extension picks
958
+ // the user namespace it writes into by listing `viking://user/`, and
959
+ // caches that choice for the life of the process. A namespace whose
960
+ // directory does not exist yet is invisible to it, so it falls back to
961
+ // `user/default` and every write is refused, reported only as "access
962
+ // denied". Needs the forward up, so it is awaited here.
963
+ progress.begin("memory-forward");
964
+ const fwd = await memForward.ensure();
965
+ if (fwd.state === "failed")
966
+ progress.fail("memory-forward", fwd.detail);
967
+ else
968
+ progress.done("memory-forward", fwd.state === "off" ? fwd.detail : fwd.state);
969
+ if (forwardUsable(fwd)) {
970
+ progress.begin("memory-namespaces");
971
+ const notes = await memStore.ensureNamespaces();
972
+ for (const note of notes)
973
+ app.notice(`memory: ${note}`);
974
+ progress.done("memory-namespaces", notes.length > 0 ? `${notes.length} note${notes.length === 1 ? "" : "s"}` : "ready");
975
+ }
976
+ else {
977
+ progress.skip("memory-namespaces", fwd.state === "off" ? fwd.detail : "forward down");
978
+ }
979
+ progress.begin("container");
980
+ // Before the container, so a cold start finds the token already there.
981
+ // Total: no credential, or GitHub down, is a note — never a failed start.
982
+ const gh = await ghToken.refresh();
983
+ progress.note("container", tokenOutcomeLine(gh));
984
+ app.notice(`companion: ${tokenOutcomeLine(gh)}`);
985
+ ghToken.start((o) => {
986
+ if (o.kind === "failed")
987
+ app.notice(`companion: ${tokenOutcomeLine(o)}`);
988
+ });
989
+ // The companion's read-only mirror of the live config — a copy, not a
990
+ // file bind, so a config.toml replaced by rename is still seen (see
991
+ // CONTAINER.configLive). Kept in step on the same timer as the token.
992
+ const syncConfig = () => {
993
+ const m = companion.syncConfigMirror();
994
+ if (typeof m === "object")
995
+ app.notice(`companion: config mirror NOT refreshed — ${m.failed}`);
996
+ commitProfiles();
997
+ };
998
+ syncConfig();
999
+ configMirrorTimer ??= setInterval(syncConfig, CONFIG_MIRROR_EVERY_MS);
1000
+ configMirrorTimer.unref();
1001
+ await companion.ensureUp((line) => {
1002
+ progress.note("container", line);
1003
+ app.notice(`companion: ${line}`);
1004
+ });
1005
+ companionContainerStarted = true;
1006
+ progress.done("container");
1007
+ // The container's memory network exists now: a forward we started
1008
+ // before it (a cold host) binds the bridge address it could not.
1009
+ await memForward.rebindMissing();
1010
+ // Daily rollover — strictly between "container is up" and
1011
+ // "pi is spawned": on the first start of a new ops day it archives
1012
+ // yesterday, kills the session and queues the opening context, so the
1013
+ // spawnPi below births the fresh conversation that reads it.
1014
+ progress.begin("rollover");
1015
+ const { CompanionRollover } = await import("./rollover.js");
1016
+ const { latestDigestHeadline } = await import("../digest.js");
1017
+ const rollover = new CompanionRollover(full.companion, companionDocker, {
1018
+ statePath: join(companionStateDir, "rollover.json"),
1019
+ policyStatePath,
1020
+ templatePath: join(sandboxRoot(), "prompts", "companion-day-open.md"),
1021
+ injectDir: companion.hostInjectDir(),
1022
+ localDigestHeadline: () => latestDigestHeadline(),
1023
+ ownerName: full.owner.name,
1024
+ timeZone: full.ops.timezone,
1025
+ // The first-run interview — the marker path is the setup wizard's
1026
+ // contract; the template makes it work without the wizard too.
1027
+ firstRunTemplatePath: join(sandboxRoot(), "prompts", "companion-first-run.md"),
1028
+ firstRunMarkerPath: join(companionStateDir, "first-run.json"),
1029
+ });
1030
+ const report = await rollover.run();
1031
+ if (report.deferred) {
1032
+ // The container could not be read, so the day is deliberately not
1033
+ // recorded and yesterday's conversation continues. Loud, because a
1034
+ // rollover that silently did not happen reads as one that did.
1035
+ app.notice(`companion: day rollover DEFERRED — ${report.deferred}`);
1036
+ }
1037
+ else if (report.stoodDown) {
1038
+ app.notice(`companion: ${report.stoodDown}`);
1039
+ }
1040
+ else if (report.verdict.kind === "rollover") {
1041
+ app.notice(`companion: new day ${report.verdict.today} — fresh conversation (${report.steps.length} steps)`);
1042
+ }
1043
+ for (const warning of report.warnings ?? [])
1044
+ app.notice(`companion: ${warning}`);
1045
+ if (report.interviewFile) {
1046
+ app.notice("companion: first start on this installation — greeting + calibration interview queued");
1047
+ }
1048
+ progress.done("rollover", report.deferred
1049
+ ? `deferred — ${report.deferred}`
1050
+ : report.stoodDown
1051
+ ? "rolled over by another start"
1052
+ : report.verdict.kind === "rollover"
1053
+ ? `new day ${report.verdict.today}`
1054
+ : "same day");
1055
+ progress.begin("companion-pi");
1056
+ await companion.spawnPi();
1057
+ progress.done("companion-pi");
1058
+ companionUp = true;
1059
+ companionError = undefined;
1060
+ app.repaint();
1061
+ }
1062
+ catch (err) {
1063
+ companionError = String(err instanceof Error ? err.message : err);
1064
+ for (const key of ["memory-forward", ...COMPANION_ONLY_STEPS]) {
1065
+ const step = progress.steps().find((s) => s.key === key);
1066
+ if (step?.state === "running")
1067
+ progress.fail(key, companionError);
1068
+ else if (step?.state === "pending")
1069
+ progress.skip(key, "companion unavailable");
1070
+ }
1071
+ app.notice(`companion unavailable: ${companionError}`);
1072
+ }
1073
+ };
1074
+ const companionStateDir = join(sandboxRoot(), "state", "companion");
1075
+ const policyStatePath = join(companionStateDir, "alert-policy.json");
1076
+ // Boot and retry share ONE start: the channels below are wired after the
1077
+ // first start that leaves the companion up, whichever path that was.
1078
+ const startAndWire = companionStarter({
1079
+ start: startCompanion,
1080
+ up: () => companionUp,
1081
+ wire: () => wireCompanionChannels(),
1082
+ });
1083
+ installCompanionTabHooks(app, {
1084
+ available: () => companionUp,
1085
+ failure: () => companionError,
1086
+ retry: companionOn ? startAndWire : undefined,
1087
+ });
1088
+ /** Everything the companion feed / alert pipeline needs, once it is up. */
1089
+ const wireCompanionChannels = async () => {
1090
+ const { FeedWriter, buildTuiView } = await import("./feed.js");
1091
+ const feed = new FeedWriter(full.companion.feed_dir);
1092
+ // Publish the operator's view: the last painted frame plus the derived
1093
+ // state behind it. Two triggers, one throttle: the poll below (so it
1094
+ // keeps up with the daemon) and every repaint (so it keeps up with the
1095
+ // operator's keystrokes — "what is on their screen" is only true if it
1096
+ // tracks the screen). Files only: this never injects a companion turn,
1097
+ // and the companion reads it on demand through the `tui_*` tools.
1098
+ const publishView = () => {
1099
+ try {
1100
+ // One read of the frame for both: the view's screen metadata (rows,
1101
+ // chars, truncation) describes the text written beside it.
1102
+ const screen = app.screenText();
1103
+ feed.writeView(buildTuiView({
1104
+ now: new Date(),
1105
+ screen,
1106
+ tab: app.tab,
1107
+ snapshot: app.view.snapshot,
1108
+ health: app.view.health,
1109
+ selectedPr: app.currentPr(),
1110
+ panePr: app.view.piPr,
1111
+ paneFocused: app.view.piFocus === true,
1112
+ notice: app.view.noticeLine,
1113
+ deploy: deployState,
1114
+ tuiMemoryRequests: memStore.requestCounts(),
1115
+ }), screen);
1116
+ }
1117
+ catch {
1118
+ // Publishing the view is a side effect of painting it — a failure here
1119
+ // must never surface on the notice line, let alone break the poll.
1120
+ }
1121
+ };
1122
+ app.afterRepaint = publishView;
1123
+ chainOnPoll(app, () => publishView());
1124
+ chainOnPoll(app, (a) => {
1125
+ const snap = a.view.snapshot;
1126
+ if (!snap)
1127
+ return;
1128
+ try {
1129
+ feed.write(snap, a.rawEvents, a.view.health.kind);
1130
+ }
1131
+ catch (err) {
1132
+ a.notice(`✖ feed write failed: ${String(err instanceof Error ? err.message : err)}`);
1133
+ }
1134
+ });
1135
+ const { AlertConsumer, AlertPolicy, filePolicyStore, notifySendRunner } = await import("./alerts.js");
1136
+ // Persisted per-cause memory: "already told you" survives a TUI restart
1137
+ // inside the same ops day, and resets with the day.
1138
+ const policy = new AlertPolicy(full.companion.cooldown_min, () => new Date(), filePolicyStore(policyStatePath), full.ops.timezone);
1139
+ // No overseer inject: the overseer is retired (rule 8). An alert is the
1140
+ // status line, the badge and the desktop notification.
1141
+ const consumer = new AlertConsumer(full.companion.alerts_dir, policy, {
1142
+ notice: (text) => app.notice(text),
1143
+ badge: (badge) => app.setAlertBadge(badge),
1144
+ notify: notifySendRunner,
1145
+ });
1146
+ consumer.start(Math.max(1, cfg.poll_interval_s) * 1000);
1147
+ alerts = consumer;
1148
+ // The bot's companion inbox (anomalies, heartbeat, login alerts, the EOD
1149
+ // sweep): pulled over the TUI's one runner into the companion's inject dir
1150
+ // and the alerts feed above, then acked back. Rule 8, src/tui/inboxpull.ts.
1151
+ const { InboxPuller } = await import("./inboxpull.js");
1152
+ const { companionInjectDir: injectDirOf } = await import("./companion.js");
1153
+ const inboxPuller = new InboxPuller({
1154
+ cfg,
1155
+ ssh: runner,
1156
+ injectDir: injectDirOf(full.companion),
1157
+ alertsDir: full.companion.alerts_dir,
1158
+ }, (line) => app.notice(line));
1159
+ chainOnPoll(app, () => {
1160
+ void inboxPuller.tick().catch(() => {
1161
+ /* best-effort — never break the poll loop */
1162
+ });
1163
+ });
1164
+ // Daemon-detected pi auth failures (status.json `pi_auth_expired`) ride
1165
+ // the same pipeline: on each poll, bridge a fresh flag into one alert
1166
+ // file; the consumer above does badge + notify + cooldown.
1167
+ const { maybeWritePiAuthAlert, maybeWriteMemoryAlert, maybeWriteCurationAlert, maybeWriteWorkerCrashAlerts, } = await import("./alerts.js");
1168
+ const piAuthMem = { lastWritten: "" };
1169
+ chainOnPoll(app, (a) => {
1170
+ try {
1171
+ maybeWritePiAuthAlert(full.companion.alerts_dir, a.view.snapshot?.pi_auth_expired, piAuthMem);
1172
+ }
1173
+ catch {
1174
+ // alerting is best-effort; never break the poll loop
1175
+ }
1176
+ });
1177
+ // Reviewer memory refusing a key or down — one desktop notification per
1178
+ // transition, from status.json `memory` (never from OpenViking), and only
1179
+ // from a fresh snapshot.
1180
+ const memoryAlertMem = { lastWritten: "" };
1181
+ chainOnPoll(app, (a) => {
1182
+ try {
1183
+ maybeWriteMemoryAlert(full.companion.alerts_dir, a.view.snapshot?.memory, isFreshHealth(a.view.health), memoryAlertMem);
1184
+ }
1185
+ catch {
1186
+ // alerting is best-effort; never break the poll loop
1187
+ }
1188
+ });
1189
+ // The day's EOD curation did not reach memory (no receipt,
1190
+ // or a lesson refused / not found) — one notification per (state, day),
1191
+ // from status.json `memory.curation`, fresh snapshots only.
1192
+ const curationAlertMem = { lastWritten: "" };
1193
+ chainOnPoll(app, (a) => {
1194
+ try {
1195
+ maybeWriteCurationAlert(full.companion.alerts_dir, a.view.snapshot?.memory, isFreshHealth(a.view.health), curationAlertMem);
1196
+ }
1197
+ catch {
1198
+ // alerting is best-effort; never break the poll loop
1199
+ }
1200
+ });
1201
+ // A PR parked by the worker crash-loop breaker, or the bot drained by it
1202
+ // — one notification per park / per drain, from a fresh snapshot.
1203
+ const workerCrashMem = { written: new Set() };
1204
+ chainOnPoll(app, (a) => {
1205
+ try {
1206
+ maybeWriteWorkerCrashAlerts(full.companion.alerts_dir, a.view.snapshot, isFreshHealth(a.view.health), workerCrashMem);
1207
+ }
1208
+ catch {
1209
+ // alerting is best-effort; never break the poll loop
1210
+ }
1211
+ });
1212
+ // No memory request-file channel here, and no memory feed: the companion
1213
+ // reaches OpenViking directly, over a host-owned forward, holding both
1214
+ // user keys. A second write path is exactly what must not exist — do not
1215
+ // add one as an "offline fallback" (the companion boundary).
1216
+ // The companion → TUI action channel. Sweep discipline is the usual one
1217
+ // (poll cadence, busy flag so a slow sweep never stacks);
1218
+ // processTuiActions itself archives-before-execute and writes the result
1219
+ // files. Each executed action's trace lands on the notice line — actions
1220
+ // execute instantly, with no confirm gate.
1221
+ const { processTuiActions, parseCompanionCommand } = await import("./actions.js");
1222
+ const tuiActionsDir = join(full.companion.alerts_dir, "tui-actions");
1223
+ // The companion's commands/ (rule 8): under the state home it mounts rw,
1224
+ // where the seeded EOD sweep writes its receipt. Each file is one command
1225
+ // of the companion's tier, forwarded once down the operator route.
1226
+ const { COMPANION_COMMANDS_DIR } = await import("../install.js");
1227
+ const companionCommandsDir = join(companion.hostHomes().state, COMPANION_COMMANDS_DIR);
1228
+ const driver = buildTuiDriver(app, learning, metricsView, deployState);
1229
+ let tuiActBusy = false;
1230
+ chainOnPoll(app, (a) => {
1231
+ if (tuiActBusy)
1232
+ return;
1233
+ tuiActBusy = true;
1234
+ void processTuiActions(tuiActionsDir, driver)
1235
+ .then(async (done) => {
1236
+ for (const r of done)
1237
+ a.notice(r.trace ?? `companion tui-action ${r.file}: ✖ ${r.message}`);
1238
+ for (const r of await processTuiActions(companionCommandsDir, driver, parseCompanionCommand))
1239
+ a.notice(r.trace ?? `companion command ${r.file}: ✖ ${r.message}`);
1240
+ })
1241
+ .catch(() => {
1242
+ /* best-effort — never break the poll loop */
1243
+ })
1244
+ .finally(() => {
1245
+ tuiActBusy = false;
1246
+ });
1247
+ });
1248
+ };
1249
+ let alerts;
1250
+ /**
1251
+ * PHASE 1. Every external thing the TUI does at startup, in one place,
1252
+ * behind the first frame and reported into the companion column.
1253
+ */
1254
+ const bringUp = async () => {
1255
+ // The tracker scan is a filesystem walk, so it waits for the frame too.
1256
+ learning.store.reloadTickets(); // total: an unreadable tracker is an UNKNOWN pane, never a failed start
1257
+ // The triage runs CONCURRENTLY with the companion: a wedged ssh master can
1258
+ // cost its probe seconds, and the companion has nothing to do with ssh.
1259
+ //
1260
+ // `begin` first, and it is not decoration: the probe chain is the slowest
1261
+ // thing here (a blackholed host costs its ConnectTimeout), so this is the
1262
+ // step most likely to need the overdue warning — and a step left `pending`
1263
+ // says "nobody has reached this yet", which would be a lie the moment the
1264
+ // probes start.
1265
+ progress.begin("ssh");
1266
+ const triaged = triageConnection(cfg)
1267
+ .then(applyDiagnosis)
1268
+ .catch((err) => progress.fail("ssh", `probe failed: ${String(err instanceof Error ? err.message : err)}`));
1269
+ // The evidence store's dir must exist before the container mounts
1270
+ // it (docker would invent it root-owned), and the copier writes into it
1271
+ // whether or not the companion is up.
1272
+ try {
1273
+ evidence.ensure();
1274
+ }
1275
+ catch (err) {
1276
+ app.notice(`✖ evidence store ${evidence.dir}: ${String(err instanceof Error ? err.message : err)}`);
1277
+ }
1278
+ if (companionOn) {
1279
+ await startAndWire();
1280
+ }
1281
+ else {
1282
+ for (const key of COMPANION_ONLY_STEPS) {
1283
+ progress.skip(key, "--no-companion");
1284
+ }
1285
+ // The Memory tab still needs the forward even with the companion off.
1286
+ progress.begin("memory-forward");
1287
+ const fwd = await memForward.ensure();
1288
+ if (fwd.state === "failed")
1289
+ progress.fail("memory-forward", fwd.detail);
1290
+ else
1291
+ progress.done("memory-forward", fwd.state === "off" ? fwd.detail : fwd.state);
1292
+ }
1293
+ await triaged;
1294
+ };
1295
+ // The first poll is a step like any other: it begins when the poller does
1296
+ // (inside `run()` below) and completes on the first snapshot. Until then the
1297
+ // board says PRS (?) — an honest unknown, never a count of zero.
1298
+ progress.begin("poll");
1299
+ chainOnPoll(app, (a) => {
1300
+ const step = progress.steps().find((s) => s.key === "poll");
1301
+ if (a.view.snapshot) {
1302
+ if (step?.state !== "done")
1303
+ progress.done("poll", `${a.view.snapshot.prs.length} PRs`);
1304
+ return;
1305
+ }
1306
+ if (step?.state === "running" && a.poller.state.consecutiveFailures >= 2) {
1307
+ progress.fail("poll", app.view.connection ? diagnosisLine(app.view.connection) : "status.json unreadable");
1308
+ }
1309
+ });
1310
+ // Restore a hot-reload snapshot before run(), so the first paint is already
1311
+ // on the right tab with the right selection.
1312
+ const resumed = args.resumeState ? readResumeState(args.resumeState) : undefined;
1313
+ if (resumed) {
1314
+ // A tab name this build does not know (e.g. from an older build) lands on
1315
+ // the board.
1316
+ if (isTab(resumed.tab))
1317
+ app.tab = resumed.tab;
1318
+ app.view.eventsScroll = resumed.eventsScroll;
1319
+ app.view.eventsFocus = resumed.eventsFocus;
1320
+ if (app.sidebar && resumed.sidebarWidth !== undefined) {
1321
+ app.sidebar.preferredWidth = restorePreferredSidebarWidth(resumed.sidebarWidth);
1322
+ }
1323
+ }
1324
+ // Hot-reload: watch our own dist/, the build stamp and (without acting) the
1325
+ // core's dist/, and re-exec when a whole build lands (reload.ts says why a
1326
+ // core write alone never reloads).
1327
+ const reloadState = resumeStatePath(sandboxRoot());
1328
+ const reloader = args.reload
1329
+ ? new HotReloader({
1330
+ distDir: join(sandboxRoot(), "dist"),
1331
+ buildStamp: join(sandboxRoot(), BUILD_STAMP),
1332
+ // A core-only change built without the stamp (a raw `tsc -b`, an
1333
+ // IDE): the core's dist/ only marks it, the app's clean build info
1334
+ // reloads (reload.ts).
1335
+ coreDistDir: join(sandboxRoot(), CORE_DIST),
1336
+ appBuildInfo: join(sandboxRoot(), APP_BUILD_INFO),
1337
+ stateFile: reloadState,
1338
+ snapshot: () => ({
1339
+ tab: app.tab,
1340
+ pr: app.currentPr(),
1341
+ eventsScroll: app.view.eventsScroll,
1342
+ eventsFocus: app.view.eventsFocus,
1343
+ sidebarWidth: app.sidebar?.preferredWidth,
1344
+ saved_at: "",
1345
+ }),
1346
+ // The forward must be released here, not in the cleanup after
1347
+ // `await running`. `realReexec` calls this teardown and then
1348
+ // spawnSync's the new build synchronously — `app.quit()` merely
1349
+ // resolves a promise, so that cleanup is a microtask which cannot run
1350
+ // until spawnSync returns, i.e. not until the child has exited. Left
1351
+ // to it, the old process would sit inside spawnSync holding :1933
1352
+ // with a FROZEN event loop: the new TUI finds the port taken, cannot
1353
+ // bind, and memory is dead for the rest of the session.
1354
+ // Same reasoning covers in-flight ssh: the per-call SIGKILL in
1355
+ // `realSshRunner` is a timer on the loop that spawnSync is about to
1356
+ // freeze, so a capture/poll/why still running at reload time is
1357
+ // orphaned forever. Against a wedged ControlMaster (an ssh config
1358
+ // block with no ServerAliveInterval lets a network blip hang the
1359
+ // master and every slave riding it) those children never exit on
1360
+ // their own either, and they stack up one generation per reload.
1361
+ // Kill them here, while the loop still turns.
1362
+ reexec: (file) => realReexec(file, () => {
1363
+ killInFlightSsh();
1364
+ memForward.stop();
1365
+ app.quit();
1366
+ }),
1367
+ })
1368
+ : undefined;
1369
+ // ---- the first frame ------------------------------------------------------
1370
+ //
1371
+ // Deep link: `qcr tui <pr>` is the board with that PR selected — nothing
1372
+ // opens over it.
1373
+ const initial = target.kind === "pr" ? target.pr : resumed?.pr;
1374
+ const running = app.run(initial);
1375
+ // Only now: a recursive dist watcher walks the tree, and the bring-up talks
1376
+ // to ssh, docker and OpenViking. Neither may precede the frame above.
1377
+ reloader?.start();
1378
+ // Kept, not dropped on the floor: quitting during the bring-up must not
1379
+ // leave it running against the teardown below, or `spawnPi()` could land
1380
+ // after `gracefulStop()` (a clean quit that leaves a freshly spawned
1381
+ // companion behind) and `memForward.stop()` could close a forward the
1382
+ // bring-up was still about to use.
1383
+ const bringUpDone = bringUp().catch(() => {
1384
+ /* every step already reports itself; a bring-up cannot fail the quit */
1385
+ });
1386
+ await running;
1387
+ // Let the bring-up finish before tearing down what it is still touching.
1388
+ // Bounded, and announced: this runs AFTER the terminal is restored, so a
1389
+ // silent wait here is a terminal that looks dead — the same reasoning the
1390
+ // companion stop below is written under.
1391
+ const inFlight = Promise.allSettled([bringUpDone, pendingReconnect]);
1392
+ if (!(await settledWithin(inFlight, BRING_UP_QUIT_GRACE_MS))) {
1393
+ out.write("qcr tui: waiting for startup to finish before shutting down…\n");
1394
+ await settledWithin(inFlight, BRING_UP_QUIT_GRACE_MS * 3);
1395
+ }
1396
+ reloader?.stop();
1397
+ progress.stop();
1398
+ alerts?.stop();
1399
+ ghToken.stop();
1400
+ if (configMirrorTimer)
1401
+ clearInterval(configMirrorTimer);
1402
+ // Closes only a forward THIS process bound; an adopted one (systemd unit,
1403
+ // another qcr tui) is left running on purpose — see memforward.ts.
1404
+ memForward.stop();
1405
+ // Clean quit stops the container (conversation + auth survive on volumes).
1406
+ // A crash deliberately leaves it running — the next start adopts it.
1407
+ //
1408
+ // This runs after the terminal is restored, so every millisecond it takes is
1409
+ // a terminal that looks dead: board gone, prompt not back, nothing
1410
+ // responding. Say what is happening: a slow docker, or a container whose
1411
+ // PID 1 ignores SIGTERM (so `docker stop` runs to its full timeout), must
1412
+ // not look like a hang.
1413
+ await stopCompanionOnQuit({
1414
+ // The wrapper that ran the container stops it: nothing here can.
1415
+ containerStarted: !socketMode && (companionContainerStarted || companionUp),
1416
+ stop: () => companion.gracefulStop(),
1417
+ write: (line) => out.write(line),
1418
+ });
1419
+ // Exit explicitly. Anything still holding the event loop — a docker exec
1420
+ // that never returned, an ssh master, a socket — would otherwise keep the
1421
+ // process alive with the terminal already restored, which is exactly the
1422
+ // "TUI is gone but the shell never comes back" state.
1423
+ exit(0);
1424
+ }
1425
+ //# sourceMappingURL=index.js.map