@quickchatai/quickreview 0.0.0-stage → 1.2.0

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