@quickchatai/quickreview 0.0.0-stage → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1443) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +385 -2
  3. package/THIRD-PARTY-NOTICES.md +134 -0
  4. package/agents/pass-abstraction.md +64 -0
  5. package/agents/pass-ask-human.md +134 -0
  6. package/agents/pass-backcompat.md +67 -0
  7. package/agents/pass-baseline.md +61 -0
  8. package/agents/pass-behaviour-delta.md +64 -0
  9. package/agents/pass-bounds.md +36 -0
  10. package/agents/pass-claims.md +31 -0
  11. package/agents/pass-concurrency.md +168 -0
  12. package/agents/pass-consistency.md +57 -0
  13. package/agents/pass-contracts.md +30 -0
  14. package/agents/pass-data-safety.md +54 -0
  15. package/agents/pass-deferred.md +35 -0
  16. package/agents/pass-delivery.md +37 -0
  17. package/agents/pass-duplication.md +60 -0
  18. package/agents/pass-intent.md +63 -0
  19. package/agents/pass-lint-conformance.md +46 -0
  20. package/agents/pass-maintainability.md +67 -0
  21. package/agents/pass-placement.md +68 -0
  22. package/agents/pass-prod-risk.md +24 -0
  23. package/agents/pass-rollout.md +32 -0
  24. package/agents/pass-roundtrip.md +46 -0
  25. package/agents/pass-security.md +31 -0
  26. package/agents/pass-simplicity.md +68 -0
  27. package/agents/pass-statelessness.md +27 -0
  28. package/agents/pass-style.md +37 -0
  29. package/agents/pass-sweep.md +57 -0
  30. package/agents/pass-tests.md +58 -0
  31. package/agents/pass-ui.md +23 -0
  32. package/agents/pass-validate.md +50 -0
  33. package/agents/pass-widened.md +46 -0
  34. package/agents/scan/pass-scan.md +86 -0
  35. package/bin/qcr.js +11 -0
  36. package/config.example.toml +1067 -0
  37. package/deploy/ansible/README.md +356 -0
  38. package/deploy/ansible/group_vars/all.yml +212 -0
  39. package/deploy/ansible/inventory.example.ini +25 -0
  40. package/deploy/ansible/playbook.yml +360 -0
  41. package/deploy/collect-secrets.sh +289 -0
  42. package/deploy/collect-slack-secrets.mjs +1190 -0
  43. package/deploy/github-app-manifest.json +31 -0
  44. package/deploy/manifest.schema.json +75 -0
  45. package/deploy/qcr-update.sh +803 -0
  46. package/deploy/reviewer/AGENTS.override.md +12 -0
  47. package/dist/extensions/cli/bash-guard-ambient.js +42 -0
  48. package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
  49. package/dist/extensions/cli/bash-guard.js +5 -0
  50. package/dist/extensions/cli/bash-guard.js.map +1 -0
  51. package/dist/extensions/cli/companion-guard.js +5 -0
  52. package/dist/extensions/cli/companion-guard.js.map +1 -0
  53. package/dist/extensions/cli/debug-round.js +8 -0
  54. package/dist/extensions/cli/debug-round.js.map +1 -0
  55. package/dist/extensions/cli/inject-bridge.js +5 -0
  56. package/dist/extensions/cli/inject-bridge.js.map +1 -0
  57. package/dist/extensions/cli/ledger-pin.js +5 -0
  58. package/dist/extensions/cli/ledger-pin.js.map +1 -0
  59. package/dist/extensions/cli/lens.js +13 -0
  60. package/dist/extensions/cli/lens.js.map +1 -0
  61. package/dist/extensions/cli/muted-theme.js +5 -0
  62. package/dist/extensions/cli/muted-theme.js.map +1 -0
  63. package/dist/extensions/cli/onboarding.js +5 -0
  64. package/dist/extensions/cli/onboarding.js.map +1 -0
  65. package/dist/extensions/cli/setup.js +5 -0
  66. package/dist/extensions/cli/setup.js.map +1 -0
  67. package/dist/extensions/cli/tui-view.js +5 -0
  68. package/dist/extensions/cli/tui-view.js.map +1 -0
  69. package/dist/extensions/companion-guard.js +361 -0
  70. package/dist/extensions/companion-guard.js.map +1 -0
  71. package/dist/extensions/debug-round.js +204 -0
  72. package/dist/extensions/debug-round.js.map +1 -0
  73. package/dist/extensions/inject-bridge.js +603 -0
  74. package/dist/extensions/inject-bridge.js.map +1 -0
  75. package/dist/extensions/muted-theme.js +12 -0
  76. package/dist/extensions/muted-theme.js.map +1 -0
  77. package/dist/extensions/onboarding.js +175 -0
  78. package/dist/extensions/onboarding.js.map +1 -0
  79. package/dist/extensions/setup-replay.js +269 -0
  80. package/dist/extensions/setup-replay.js.map +1 -0
  81. package/dist/extensions/setup-scan.js +998 -0
  82. package/dist/extensions/setup-scan.js.map +1 -0
  83. package/dist/extensions/setup-triage.js +175 -0
  84. package/dist/extensions/setup-triage.js.map +1 -0
  85. package/dist/extensions/setup.js +1160 -0
  86. package/dist/extensions/setup.js.map +1 -0
  87. package/dist/extensions/tui-view.js +389 -0
  88. package/dist/extensions/tui-view.js.map +1 -0
  89. package/dist/extensions/vendor/openviking-pi/client.js +376 -0
  90. package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
  91. package/dist/extensions/vendor/openviking-pi/config.js +189 -0
  92. package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
  93. package/dist/extensions/vendor/openviking-pi/index.js +421 -0
  94. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
  95. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
  96. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
  97. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
  98. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
  99. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
  100. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
  101. package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
  102. package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
  103. package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
  104. package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
  105. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
  106. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
  107. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
  108. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
  109. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
  110. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
  111. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
  112. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
  113. package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
  114. package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
  115. package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
  116. package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
  117. package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
  118. package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
  119. package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
  120. package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
  121. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
  122. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
  123. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
  124. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
  125. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
  126. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
  127. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
  128. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
  129. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
  130. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
  131. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
  132. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
  133. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
  134. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
  135. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
  136. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
  137. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
  138. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
  139. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  140. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
  141. package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
  142. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
  143. package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
  144. package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
  145. package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
  146. package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
  147. package/dist/src/anomalies.js +483 -0
  148. package/dist/src/anomalies.js.map +1 -0
  149. package/dist/src/answerworker.js +180 -0
  150. package/dist/src/answerworker.js.map +1 -0
  151. package/dist/src/appstamp.js +68 -0
  152. package/dist/src/appstamp.js.map +1 -0
  153. package/dist/src/archive/admission.js +3 -0
  154. package/dist/src/archive/admission.js.map +1 -0
  155. package/dist/src/archive/archivedb.js +474 -0
  156. package/dist/src/archive/archivedb.js.map +1 -0
  157. package/dist/src/archive/archiver.js +1770 -0
  158. package/dist/src/archive/archiver.js.map +1 -0
  159. package/dist/src/archive/archivewrite.js +198 -0
  160. package/dist/src/archive/archivewrite.js.map +1 -0
  161. package/dist/src/archive/bundle.js +266 -0
  162. package/dist/src/archive/bundle.js.map +1 -0
  163. package/dist/src/archive/dbmove.js +638 -0
  164. package/dist/src/archive/dbmove.js.map +1 -0
  165. package/dist/src/archive/duplicatecounts.js +80 -0
  166. package/dist/src/archive/duplicatecounts.js.map +1 -0
  167. package/dist/src/archive/eligibility.js +113 -0
  168. package/dist/src/archive/eligibility.js.map +1 -0
  169. package/dist/src/archive/extract.js +288 -0
  170. package/dist/src/archive/extract.js.map +1 -0
  171. package/dist/src/archive/fsutil.js +330 -0
  172. package/dist/src/archive/fsutil.js.map +1 -0
  173. package/dist/src/archive/gzfile.js +277 -0
  174. package/dist/src/archive/gzfile.js.map +1 -0
  175. package/dist/src/archive/kinds.js +22 -0
  176. package/dist/src/archive/kinds.js.map +1 -0
  177. package/dist/src/archive/livecheckpoint.js +178 -0
  178. package/dist/src/archive/livecheckpoint.js.map +1 -0
  179. package/dist/src/archive/liveretention.js +145 -0
  180. package/dist/src/archive/liveretention.js.map +1 -0
  181. package/dist/src/archive/manifest.js +284 -0
  182. package/dist/src/archive/manifest.js.map +1 -0
  183. package/dist/src/archive/overseer.js +283 -0
  184. package/dist/src/archive/overseer.js.map +1 -0
  185. package/dist/src/archive/piglobal.js +129 -0
  186. package/dist/src/archive/piglobal.js.map +1 -0
  187. package/dist/src/archive/plan.js +398 -0
  188. package/dist/src/archive/plan.js.map +1 -0
  189. package/dist/src/archive/prarchive.js +831 -0
  190. package/dist/src/archive/prarchive.js.map +1 -0
  191. package/dist/src/archive/prfiles.js +303 -0
  192. package/dist/src/archive/prfiles.js.map +1 -0
  193. package/dist/src/archive/report.js +169 -0
  194. package/dist/src/archive/report.js.map +1 -0
  195. package/dist/src/archive/restore.js +276 -0
  196. package/dist/src/archive/restore.js.map +1 -0
  197. package/dist/src/archive/rows.js +106 -0
  198. package/dist/src/archive/rows.js.map +1 -0
  199. package/dist/src/archive/seal.js +480 -0
  200. package/dist/src/archive/seal.js.map +1 -0
  201. package/dist/src/archive/status.js +528 -0
  202. package/dist/src/archive/status.js.map +1 -0
  203. package/dist/src/archive/storepass.js +409 -0
  204. package/dist/src/archive/storepass.js.map +1 -0
  205. package/dist/src/backfill.js +217 -0
  206. package/dist/src/backfill.js.map +1 -0
  207. package/dist/src/bench/cost.js +87 -0
  208. package/dist/src/bench/exec.js +76 -0
  209. package/dist/src/bench/values.js +14 -0
  210. package/dist/src/botcontrol.js +127 -0
  211. package/dist/src/botcontrol.js.map +1 -0
  212. package/dist/src/botworker.js +805 -0
  213. package/dist/src/botworker.js.map +1 -0
  214. package/dist/src/capture.js +234 -0
  215. package/dist/src/capture.js.map +1 -0
  216. package/dist/src/cases.js +1491 -0
  217. package/dist/src/cases.js.map +1 -0
  218. package/dist/src/channelfs.js +27 -0
  219. package/dist/src/channelfs.js.map +1 -0
  220. package/dist/src/checklist.js +312 -0
  221. package/dist/src/checklist.js.map +1 -0
  222. package/dist/src/ciretry.js +91 -0
  223. package/dist/src/ciretry.js.map +1 -0
  224. package/dist/src/cli.js +1916 -0
  225. package/dist/src/cli.js.map +1 -0
  226. package/dist/src/cliops.js +333 -0
  227. package/dist/src/cliops.js.map +1 -0
  228. package/dist/src/clonedir.js +15 -0
  229. package/dist/src/clonedir.js.map +1 -0
  230. package/dist/src/commandeffects.js +43 -0
  231. package/dist/src/commandeffects.js.map +1 -0
  232. package/dist/src/commands.js +1312 -0
  233. package/dist/src/commands.js.map +1 -0
  234. package/dist/src/companioninbox.js +203 -0
  235. package/dist/src/companioninbox.js.map +1 -0
  236. package/dist/src/compare.js +673 -0
  237. package/dist/src/compare.js.map +1 -0
  238. package/dist/src/config.js +1027 -0
  239. package/dist/src/config.js.map +1 -0
  240. package/dist/src/configcheck.js +111 -0
  241. package/dist/src/configcheck.js.map +1 -0
  242. package/dist/src/coreext.js +43 -0
  243. package/dist/src/coreext.js.map +1 -0
  244. package/dist/src/daemon.js +929 -0
  245. package/dist/src/daemon.js.map +1 -0
  246. package/dist/src/debuground.js +731 -0
  247. package/dist/src/debuground.js.map +1 -0
  248. package/dist/src/debugroundpi.js +219 -0
  249. package/dist/src/debugroundpi.js.map +1 -0
  250. package/dist/src/diagnostics.js +14 -0
  251. package/dist/src/diagnostics.js.map +1 -0
  252. package/dist/src/digest.js +139 -0
  253. package/dist/src/digest.js.map +1 -0
  254. package/dist/src/dispatch.js +1726 -0
  255. package/dist/src/dispatch.js.map +1 -0
  256. package/dist/src/engine/answerguard.js +18 -0
  257. package/dist/src/engine/answerguard.js.map +1 -0
  258. package/dist/src/engine/botsdk.js +262 -0
  259. package/dist/src/engine/botsdk.js.map +1 -0
  260. package/dist/src/engine/caserunner-sdk.js +332 -0
  261. package/dist/src/engine/caserunner-sdk.js.map +1 -0
  262. package/dist/src/engine/debugpi.js +194 -0
  263. package/dist/src/engine/debugpi.js.map +1 -0
  264. package/dist/src/eod.js +473 -0
  265. package/dist/src/eod.js.map +1 -0
  266. package/dist/src/eodpinwatch.js +391 -0
  267. package/dist/src/eodpinwatch.js.map +1 -0
  268. package/dist/src/escalation.js +1009 -0
  269. package/dist/src/escalation.js.map +1 -0
  270. package/dist/src/eventloop.js +779 -0
  271. package/dist/src/eventloop.js.map +1 -0
  272. package/dist/src/execboundary.js +430 -0
  273. package/dist/src/execboundary.js.map +1 -0
  274. package/dist/src/feedback.js +915 -0
  275. package/dist/src/feedback.js.map +1 -0
  276. package/dist/src/findingscheck.js +200 -0
  277. package/dist/src/findingscheck.js.map +1 -0
  278. package/dist/src/fsdenied.js +26 -0
  279. package/dist/src/fsdenied.js.map +1 -0
  280. package/dist/src/ghread.js +181 -0
  281. package/dist/src/ghread.js.map +1 -0
  282. package/dist/src/github.js +1063 -0
  283. package/dist/src/github.js.map +1 -0
  284. package/dist/src/homes.js +103 -0
  285. package/dist/src/homes.js.map +1 -0
  286. package/dist/src/hostroute.js +24 -0
  287. package/dist/src/hostroute.js.map +1 -0
  288. package/dist/src/improvements.js +625 -0
  289. package/dist/src/improvements.js.map +1 -0
  290. package/dist/src/install.js +946 -0
  291. package/dist/src/install.js.map +1 -0
  292. package/dist/src/installkind.js +40 -0
  293. package/dist/src/installkind.js.map +1 -0
  294. package/dist/src/ipc/capability.js +286 -0
  295. package/dist/src/ipc/capability.js.map +1 -0
  296. package/dist/src/ipc/client.js +226 -0
  297. package/dist/src/ipc/client.js.map +1 -0
  298. package/dist/src/ipc/control.js +70 -0
  299. package/dist/src/ipc/control.js.map +1 -0
  300. package/dist/src/ipc/ghreadshim.js +77 -0
  301. package/dist/src/ipc/ghreadshim.js.map +1 -0
  302. package/dist/src/ipc/launcher.js +65 -0
  303. package/dist/src/ipc/launcher.js.map +1 -0
  304. package/dist/src/ipc/permits.js +293 -0
  305. package/dist/src/ipc/permits.js.map +1 -0
  306. package/dist/src/ipc/postshim.js +137 -0
  307. package/dist/src/ipc/postshim.js.map +1 -0
  308. package/dist/src/ipc/protocol.js +239 -0
  309. package/dist/src/ipc/protocol.js.map +1 -0
  310. package/dist/src/ipc/server.js +312 -0
  311. package/dist/src/ipc/server.js.map +1 -0
  312. package/dist/src/ipc/workerspec.js +148 -0
  313. package/dist/src/ipc/workerspec.js.map +1 -0
  314. package/dist/src/learner/learner.js +1272 -0
  315. package/dist/src/learner/learner.js.map +1 -0
  316. package/dist/src/learner/miner.js +948 -0
  317. package/dist/src/learner/miner.js.map +1 -0
  318. package/dist/src/learner/packet.js +216 -0
  319. package/dist/src/learner/packet.js.map +1 -0
  320. package/dist/src/learner/receipt.js +135 -0
  321. package/dist/src/learner/receipt.js.map +1 -0
  322. package/dist/src/learner/status.js +38 -0
  323. package/dist/src/learner/status.js.map +1 -0
  324. package/dist/src/ledgercheck.js +120 -0
  325. package/dist/src/ledgercheck.js.map +1 -0
  326. package/dist/src/lessonledger.js +451 -0
  327. package/dist/src/lessonledger.js.map +1 -0
  328. package/dist/src/lessonselect.js +639 -0
  329. package/dist/src/lessonselect.js.map +1 -0
  330. package/dist/src/lessonshadow.js +540 -0
  331. package/dist/src/lessonshadow.js.map +1 -0
  332. package/dist/src/levers.js +302 -0
  333. package/dist/src/levers.js.map +1 -0
  334. package/dist/src/live/projection.js +450 -0
  335. package/dist/src/live/projection.js.map +1 -0
  336. package/dist/src/live/publish.js +83 -0
  337. package/dist/src/live/publish.js.map +1 -0
  338. package/dist/src/live/read.js +60 -0
  339. package/dist/src/live/read.js.map +1 -0
  340. package/dist/src/livecli.js +288 -0
  341. package/dist/src/livecli.js.map +1 -0
  342. package/dist/src/loginhint.js +42 -0
  343. package/dist/src/loginhint.js.map +1 -0
  344. package/dist/src/loginrefresh/atomic.js +66 -0
  345. package/dist/src/loginrefresh/atomic.js.map +1 -0
  346. package/dist/src/loginrefresh/lock.js +93 -0
  347. package/dist/src/loginrefresh/lock.js.map +1 -0
  348. package/dist/src/loginrefresh/main.js +58 -0
  349. package/dist/src/loginrefresh/main.js.map +1 -0
  350. package/dist/src/loginrefresh/pirefresh.js +49 -0
  351. package/dist/src/loginrefresh/pirefresh.js.map +1 -0
  352. package/dist/src/loginrefresh/refresher.js +286 -0
  353. package/dist/src/loginrefresh/refresher.js.map +1 -0
  354. package/dist/src/loginrefresh/supervisor.js +253 -0
  355. package/dist/src/loginrefresh/supervisor.js.map +1 -0
  356. package/dist/src/loginrefresh/types.js +4 -0
  357. package/dist/src/loginrefresh/types.js.map +1 -0
  358. package/dist/src/memory/context.js +251 -0
  359. package/dist/src/memory/context.js.map +1 -0
  360. package/dist/src/memory/datafence.js +5 -0
  361. package/dist/src/memory/datafence.js.map +1 -0
  362. package/dist/src/memory/delivery.js +131 -0
  363. package/dist/src/memory/delivery.js.map +1 -0
  364. package/dist/src/memory/eventtags.js +81 -0
  365. package/dist/src/memory/eventtags.js.map +1 -0
  366. package/dist/src/memory/health.js +713 -0
  367. package/dist/src/memory/health.js.map +1 -0
  368. package/dist/src/memory/import.js +504 -0
  369. package/dist/src/memory/import.js.map +1 -0
  370. package/dist/src/memory/layout.js +252 -0
  371. package/dist/src/memory/layout.js.map +1 -0
  372. package/dist/src/memory/legacy-file.js +100 -0
  373. package/dist/src/memory/legacy-file.js.map +1 -0
  374. package/dist/src/memory/lessons.js +370 -0
  375. package/dist/src/memory/lessons.js.map +1 -0
  376. package/dist/src/memory/ovsession.js +449 -0
  377. package/dist/src/memory/ovsession.js.map +1 -0
  378. package/dist/src/memory/relayout.js +259 -0
  379. package/dist/src/memory/relayout.js.map +1 -0
  380. package/dist/src/memory/roundactivity.js +180 -0
  381. package/dist/src/memory/roundactivity.js.map +1 -0
  382. package/dist/src/memory/selection.js +690 -0
  383. package/dist/src/memory/selection.js.map +1 -0
  384. package/dist/src/memory/shadowstatus.js +69 -0
  385. package/dist/src/memory/shadowstatus.js.map +1 -0
  386. package/dist/src/memory/store.js +83 -0
  387. package/dist/src/memory/store.js.map +1 -0
  388. package/dist/src/memory/textidentity.js +8 -0
  389. package/dist/src/memory/textidentity.js.map +1 -0
  390. package/dist/src/metrics.js +773 -0
  391. package/dist/src/metrics.js.map +1 -0
  392. package/dist/src/missingconfig.js +8 -0
  393. package/dist/src/missingconfig.js.map +1 -0
  394. package/dist/src/npmverbs.js +226 -0
  395. package/dist/src/npmverbs.js.map +1 -0
  396. package/dist/src/onboard/choices.js +191 -0
  397. package/dist/src/onboard/choices.js.map +1 -0
  398. package/dist/src/onboard/engine.js +344 -0
  399. package/dist/src/onboard/engine.js.map +1 -0
  400. package/dist/src/onboard/launch.js +83 -0
  401. package/dist/src/onboard/launch.js.map +1 -0
  402. package/dist/src/onboard/steps/appslug.js +211 -0
  403. package/dist/src/onboard/steps/appslug.js.map +1 -0
  404. package/dist/src/onboard/steps/bringup.js +408 -0
  405. package/dist/src/onboard/steps/bringup.js.map +1 -0
  406. package/dist/src/onboard/steps/compose.js +343 -0
  407. package/dist/src/onboard/steps/compose.js.map +1 -0
  408. package/dist/src/onboard/steps/configdelivery.js +50 -0
  409. package/dist/src/onboard/steps/configdelivery.js.map +1 -0
  410. package/dist/src/onboard/steps/configfill.js +1148 -0
  411. package/dist/src/onboard/steps/configfill.js.map +1 -0
  412. package/dist/src/onboard/steps/deploysh.js +80 -0
  413. package/dist/src/onboard/steps/deploysh.js.map +1 -0
  414. package/dist/src/onboard/steps/embeddings.js +472 -0
  415. package/dist/src/onboard/steps/embeddings.js.map +1 -0
  416. package/dist/src/onboard/steps/escalation.js +571 -0
  417. package/dist/src/onboard/steps/escalation.js.map +1 -0
  418. package/dist/src/onboard/steps/githubapp.js +1092 -0
  419. package/dist/src/onboard/steps/githubapp.js.map +1 -0
  420. package/dist/src/onboard/steps/golive.js +506 -0
  421. package/dist/src/onboard/steps/golive.js.map +1 -0
  422. package/dist/src/onboard/steps/index.js +67 -0
  423. package/dist/src/onboard/steps/index.js.map +1 -0
  424. package/dist/src/onboard/steps/ingress.js +762 -0
  425. package/dist/src/onboard/steps/ingress.js.map +1 -0
  426. package/dist/src/onboard/steps/inventory.js +420 -0
  427. package/dist/src/onboard/steps/inventory.js.map +1 -0
  428. package/dist/src/onboard/steps/llmauth.js +893 -0
  429. package/dist/src/onboard/steps/llmauth.js.map +1 -0
  430. package/dist/src/onboard/steps/memory.js +1060 -0
  431. package/dist/src/onboard/steps/memory.js.map +1 -0
  432. package/dist/src/onboard/steps/memorykeys.js +43 -0
  433. package/dist/src/onboard/steps/memorykeys.js.map +1 -0
  434. package/dist/src/onboard/steps/preflight.js +172 -0
  435. package/dist/src/onboard/steps/preflight.js.map +1 -0
  436. package/dist/src/onboard/steps/proof.js +502 -0
  437. package/dist/src/onboard/steps/proof.js.map +1 -0
  438. package/dist/src/onboard/steps/ssh.js +300 -0
  439. package/dist/src/onboard/steps/ssh.js.map +1 -0
  440. package/dist/src/onboard/steps/topology.js +291 -0
  441. package/dist/src/onboard/steps/topology.js.map +1 -0
  442. package/dist/src/onboard/steps/transport.js +511 -0
  443. package/dist/src/onboard/steps/transport.js.map +1 -0
  444. package/dist/src/onboard/steps/verifyup.js +362 -0
  445. package/dist/src/onboard/steps/verifyup.js.map +1 -0
  446. package/dist/src/onboard/steps/vmdeploy.js +368 -0
  447. package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
  448. package/dist/src/onboard/toml.js +53 -0
  449. package/dist/src/onboard/toml.js.map +1 -0
  450. package/dist/src/opencli.js +508 -0
  451. package/dist/src/opencli.js.map +1 -0
  452. package/dist/src/openpr.js +457 -0
  453. package/dist/src/openpr.js.map +1 -0
  454. package/dist/src/openviking/client.js +821 -0
  455. package/dist/src/openviking/client.js.map +1 -0
  456. package/dist/src/openviking/context.js +218 -0
  457. package/dist/src/openviking/context.js.map +1 -0
  458. package/dist/src/openviking/embedder.js +457 -0
  459. package/dist/src/openviking/embedder.js.map +1 -0
  460. package/dist/src/openviking/gapsink.js +38 -0
  461. package/dist/src/openviking/gapsink.js.map +1 -0
  462. package/dist/src/openviking/health.js +69 -0
  463. package/dist/src/openviking/health.js.map +1 -0
  464. package/dist/src/openviking/index.js +24 -0
  465. package/dist/src/openviking/index.js.map +1 -0
  466. package/dist/src/openviking/lessonfind.js +54 -0
  467. package/dist/src/openviking/lessonfind.js.map +1 -0
  468. package/dist/src/openviking/probe.js +89 -0
  469. package/dist/src/openviking/probe.js.map +1 -0
  470. package/dist/src/openviking/proc.js +95 -0
  471. package/dist/src/openviking/proc.js.map +1 -0
  472. package/dist/src/openviking/provisionreport.js +144 -0
  473. package/dist/src/openviking/provisionreport.js.map +1 -0
  474. package/dist/src/openviking/reembed.js +317 -0
  475. package/dist/src/openviking/reembed.js.map +1 -0
  476. package/dist/src/openviking/relayout.js +153 -0
  477. package/dist/src/openviking/relayout.js.map +1 -0
  478. package/dist/src/openviking/sidecar-main.js +39 -0
  479. package/dist/src/openviking/sidecar-main.js.map +1 -0
  480. package/dist/src/openviking/sidecar.js +79 -0
  481. package/dist/src/openviking/sidecar.js.map +1 -0
  482. package/dist/src/openviking/store.js +1758 -0
  483. package/dist/src/openviking/store.js.map +1 -0
  484. package/dist/src/openviking/supervisor.js +422 -0
  485. package/dist/src/openviking/supervisor.js.map +1 -0
  486. package/dist/src/openviking/types.js +5 -0
  487. package/dist/src/openviking/types.js.map +1 -0
  488. package/dist/src/operator-recovery.js +172 -0
  489. package/dist/src/operator-recovery.js.map +1 -0
  490. package/dist/src/operatormessage.js +44 -0
  491. package/dist/src/operatormessage.js.map +1 -0
  492. package/dist/src/outcomes.js +1361 -0
  493. package/dist/src/outcomes.js.map +1 -0
  494. package/dist/src/piauthcopy.js +5 -0
  495. package/dist/src/piauthcopy.js.map +1 -0
  496. package/dist/src/picli.js +45 -0
  497. package/dist/src/picli.js.map +1 -0
  498. package/dist/src/picwd.js +25 -0
  499. package/dist/src/picwd.js.map +1 -0
  500. package/dist/src/pilogin.js +35 -0
  501. package/dist/src/pilogin.js.map +1 -0
  502. package/dist/src/pingpong.js +1028 -0
  503. package/dist/src/pingpong.js.map +1 -0
  504. package/dist/src/posting-journal.js +441 -0
  505. package/dist/src/posting-journal.js.map +1 -0
  506. package/dist/src/posting-outbox.js +662 -0
  507. package/dist/src/posting-outbox.js.map +1 -0
  508. package/dist/src/posting-receipt.js +203 -0
  509. package/dist/src/posting-receipt.js.map +1 -0
  510. package/dist/src/posting.js +348 -0
  511. package/dist/src/posting.js.map +1 -0
  512. package/dist/src/postops.js +637 -0
  513. package/dist/src/postops.js.map +1 -0
  514. package/dist/src/pr-activity.js +11 -0
  515. package/dist/src/pr-activity.js.map +1 -0
  516. package/dist/src/pr-author.js +5 -0
  517. package/dist/src/pr-author.js.map +1 -0
  518. package/dist/src/pr-conversation.js +355 -0
  519. package/dist/src/pr-conversation.js.map +1 -0
  520. package/dist/src/pr-session-lifecycle.js +542 -0
  521. package/dist/src/pr-session-lifecycle.js.map +1 -0
  522. package/dist/src/preflight.js +341 -0
  523. package/dist/src/preflight.js.map +1 -0
  524. package/dist/src/priorresolve.js +275 -0
  525. package/dist/src/priorresolve.js.map +1 -0
  526. package/dist/src/qcrbin.js +180 -0
  527. package/dist/src/qcrbin.js.map +1 -0
  528. package/dist/src/recheck.js +1398 -0
  529. package/dist/src/recheck.js.map +1 -0
  530. package/dist/src/reconcile.js +820 -0
  531. package/dist/src/reconcile.js.map +1 -0
  532. package/dist/src/recordedfindings.js +15 -0
  533. package/dist/src/recordedfindings.js.map +1 -0
  534. package/dist/src/regression.js +546 -0
  535. package/dist/src/regression.js.map +1 -0
  536. package/dist/src/replay.js +598 -0
  537. package/dist/src/replay.js.map +1 -0
  538. package/dist/src/requiredpasses.js +113 -0
  539. package/dist/src/requiredpasses.js.map +1 -0
  540. package/dist/src/resumecheck.js +168 -0
  541. package/dist/src/resumecheck.js.map +1 -0
  542. package/dist/src/reviewcli.js +671 -0
  543. package/dist/src/reviewcli.js.map +1 -0
  544. package/dist/src/reviewcontext.js +22 -0
  545. package/dist/src/reviewcontext.js.map +1 -0
  546. package/dist/src/reviewdetach.js +574 -0
  547. package/dist/src/reviewdetach.js.map +1 -0
  548. package/dist/src/reviewhistory.js +284 -0
  549. package/dist/src/reviewhistory.js.map +1 -0
  550. package/dist/src/reviewhome.js +26 -0
  551. package/dist/src/reviewhome.js.map +1 -0
  552. package/dist/src/reviewmemory.js +110 -0
  553. package/dist/src/reviewmemory.js.map +1 -0
  554. package/dist/src/reviewrendercli.js +104 -0
  555. package/dist/src/reviewrendercli.js.map +1 -0
  556. package/dist/src/round-work.js +20 -0
  557. package/dist/src/round-work.js.map +1 -0
  558. package/dist/src/runhost.js +72 -0
  559. package/dist/src/runhost.js.map +1 -0
  560. package/dist/src/scanauth.js +25 -0
  561. package/dist/src/scanauth.js.map +1 -0
  562. package/dist/src/scanbox.js +846 -0
  563. package/dist/src/scanbox.js.map +1 -0
  564. package/dist/src/scancli.js +1074 -0
  565. package/dist/src/scancli.js.map +1 -0
  566. package/dist/src/scandriverlog.js +30 -0
  567. package/dist/src/scandriverlog.js.map +1 -0
  568. package/dist/src/session-bridge.js +75 -0
  569. package/dist/src/session-bridge.js.map +1 -0
  570. package/dist/src/session-command-payload.js +29 -0
  571. package/dist/src/session-command-payload.js.map +1 -0
  572. package/dist/src/session-command-supervisor.js +939 -0
  573. package/dist/src/session-command-supervisor.js.map +1 -0
  574. package/dist/src/sessionjsonl.js +85 -0
  575. package/dist/src/sessionjsonl.js.map +1 -0
  576. package/dist/src/sessions.js +541 -0
  577. package/dist/src/sessions.js.map +1 -0
  578. package/dist/src/setup/adapters.js +330 -0
  579. package/dist/src/setup/adapters.js.map +1 -0
  580. package/dist/src/setup/baselines.js +64 -0
  581. package/dist/src/setup/baselines.js.map +1 -0
  582. package/dist/src/setup/caserunner.js +554 -0
  583. package/dist/src/setup/caserunner.js.map +1 -0
  584. package/dist/src/setup/configcommit.js +358 -0
  585. package/dist/src/setup/configcommit.js.map +1 -0
  586. package/dist/src/setup/engine.js +672 -0
  587. package/dist/src/setup/engine.js.map +1 -0
  588. package/dist/src/setup/facts.js +593 -0
  589. package/dist/src/setup/facts.js.map +1 -0
  590. package/dist/src/setup/findings.js +948 -0
  591. package/dist/src/setup/findings.js.map +1 -0
  592. package/dist/src/setup/history.js +338 -0
  593. package/dist/src/setup/history.js.map +1 -0
  594. package/dist/src/setup/interview.js +185 -0
  595. package/dist/src/setup/interview.js.map +1 -0
  596. package/dist/src/setup/ledger.js +207 -0
  597. package/dist/src/setup/ledger.js.map +1 -0
  598. package/dist/src/setup/pins.js +43 -0
  599. package/dist/src/setup/pins.js.map +1 -0
  600. package/dist/src/setup/portrait.js +432 -0
  601. package/dist/src/setup/portrait.js.map +1 -0
  602. package/dist/src/setup/presentation.js +9 -0
  603. package/dist/src/setup/presentation.js.map +1 -0
  604. package/dist/src/setup/profile.js +417 -0
  605. package/dist/src/setup/profile.js.map +1 -0
  606. package/dist/src/setup/replay.js +240 -0
  607. package/dist/src/setup/replay.js.map +1 -0
  608. package/dist/src/setup/replaybuild.js +311 -0
  609. package/dist/src/setup/replaybuild.js.map +1 -0
  610. package/dist/src/setup/replayrun.js +806 -0
  611. package/dist/src/setup/replayrun.js.map +1 -0
  612. package/dist/src/setup/replaysource.js +169 -0
  613. package/dist/src/setup/replaysource.js.map +1 -0
  614. package/dist/src/setup/retune.js +333 -0
  615. package/dist/src/setup/retune.js.map +1 -0
  616. package/dist/src/setup/retunelog.js +51 -0
  617. package/dist/src/setup/retunelog.js.map +1 -0
  618. package/dist/src/setup/scan.js +725 -0
  619. package/dist/src/setup/scan.js.map +1 -0
  620. package/dist/src/setup/scanbuild.js +133 -0
  621. package/dist/src/setup/scanbuild.js.map +1 -0
  622. package/dist/src/setup/scanfacts.js +96 -0
  623. package/dist/src/setup/scanfacts.js.map +1 -0
  624. package/dist/src/setup/scanlease.js +251 -0
  625. package/dist/src/setup/scanlease.js.map +1 -0
  626. package/dist/src/setup/scanreport.js +289 -0
  627. package/dist/src/setup/scanreport.js.map +1 -0
  628. package/dist/src/setup/scanrun.js +1733 -0
  629. package/dist/src/setup/scanrun.js.map +1 -0
  630. package/dist/src/setup/scantree.js +257 -0
  631. package/dist/src/setup/scantree.js.map +1 -0
  632. package/dist/src/setup/stage2.js +617 -0
  633. package/dist/src/setup/stage2.js.map +1 -0
  634. package/dist/src/setup/stage2request.js +259 -0
  635. package/dist/src/setup/stage2request.js.map +1 -0
  636. package/dist/src/setup/survey.js +307 -0
  637. package/dist/src/setup/survey.js.map +1 -0
  638. package/dist/src/setup/surveyrun.js +549 -0
  639. package/dist/src/setup/surveyrun.js.map +1 -0
  640. package/dist/src/setup/textsniff.js +89 -0
  641. package/dist/src/setup/textsniff.js.map +1 -0
  642. package/dist/src/setup/trackersink.js +395 -0
  643. package/dist/src/setup/trackersink.js.map +1 -0
  644. package/dist/src/setup/triage.js +636 -0
  645. package/dist/src/setup/triage.js.map +1 -0
  646. package/dist/src/setup/tuning.js +585 -0
  647. package/dist/src/setup/tuning.js.map +1 -0
  648. package/dist/src/setup/unitcut.js +437 -0
  649. package/dist/src/setup/unitcut.js.map +1 -0
  650. package/dist/src/singleflight.js +72 -0
  651. package/dist/src/singleflight.js.map +1 -0
  652. package/dist/src/slack/announce.js +725 -0
  653. package/dist/src/slack/announce.js.map +1 -0
  654. package/dist/src/slack/api.js +227 -0
  655. package/dist/src/slack/api.js.map +1 -0
  656. package/dist/src/slack/messages.js +158 -0
  657. package/dist/src/slack/messages.js.map +1 -0
  658. package/dist/src/slack/origin.js +550 -0
  659. package/dist/src/slack/origin.js.map +1 -0
  660. package/dist/src/slack/policy.js +225 -0
  661. package/dist/src/slack/policy.js.map +1 -0
  662. package/dist/src/slack/reply.js +153 -0
  663. package/dist/src/slack/reply.js.map +1 -0
  664. package/dist/src/slack/router.js +189 -0
  665. package/dist/src/slack/router.js.map +1 -0
  666. package/dist/src/slack/socket.js +323 -0
  667. package/dist/src/slack/socket.js.map +1 -0
  668. package/dist/src/slack/types.js +11 -0
  669. package/dist/src/slack/types.js.map +1 -0
  670. package/dist/src/sqlitehint.js +30 -0
  671. package/dist/src/sqlitehint.js.map +1 -0
  672. package/dist/src/ssh-observation.js +25 -0
  673. package/dist/src/ssh-observation.js.map +1 -0
  674. package/dist/src/state.js +5869 -0
  675. package/dist/src/state.js.map +1 -0
  676. package/dist/src/statemachine.js +220 -0
  677. package/dist/src/statemachine.js.map +1 -0
  678. package/dist/src/stubs.js +52 -0
  679. package/dist/src/stubs.js.map +1 -0
  680. package/dist/src/takeover.js +506 -0
  681. package/dist/src/takeover.js.map +1 -0
  682. package/dist/src/takeoverpi.js +249 -0
  683. package/dist/src/takeoverpi.js.map +1 -0
  684. package/dist/src/terminal-failure.js +26 -0
  685. package/dist/src/terminal-failure.js.map +1 -0
  686. package/dist/src/thread-resolution-refusal.js +4 -0
  687. package/dist/src/thread-resolution-refusal.js.map +1 -0
  688. package/dist/src/tmux.js +126 -0
  689. package/dist/src/tmux.js.map +1 -0
  690. package/dist/src/trackerid.js +43 -0
  691. package/dist/src/trackerid.js.map +1 -0
  692. package/dist/src/transcriptindex.js +512 -0
  693. package/dist/src/transcriptindex.js.map +1 -0
  694. package/dist/src/tui/actions.js +404 -0
  695. package/dist/src/tui/actions.js.map +1 -0
  696. package/dist/src/tui/alerts.js +523 -0
  697. package/dist/src/tui/alerts.js.map +1 -0
  698. package/dist/src/tui/app.js +1835 -0
  699. package/dist/src/tui/app.js.map +1 -0
  700. package/dist/src/tui/archiveops.js +164 -0
  701. package/dist/src/tui/archiveops.js.map +1 -0
  702. package/dist/src/tui/autofile.js +191 -0
  703. package/dist/src/tui/autofile.js.map +1 -0
  704. package/dist/src/tui/board.js +1143 -0
  705. package/dist/src/tui/board.js.map +1 -0
  706. package/dist/src/tui/candidates.js +699 -0
  707. package/dist/src/tui/candidates.js.map +1 -0
  708. package/dist/src/tui/cellwidth.js +151 -0
  709. package/dist/src/tui/cellwidth.js.map +1 -0
  710. package/dist/src/tui/changelog-store.js +404 -0
  711. package/dist/src/tui/changelog-store.js.map +1 -0
  712. package/dist/src/tui/chord.js +26 -0
  713. package/dist/src/tui/chord.js.map +1 -0
  714. package/dist/src/tui/clipboard.js +81 -0
  715. package/dist/src/tui/clipboard.js.map +1 -0
  716. package/dist/src/tui/cmdbar.js +253 -0
  717. package/dist/src/tui/cmdbar.js.map +1 -0
  718. package/dist/src/tui/companion.js +1275 -0
  719. package/dist/src/tui/companion.js.map +1 -0
  720. package/dist/src/tui/companionplan.js +200 -0
  721. package/dist/src/tui/companionplan.js.map +1 -0
  722. package/dist/src/tui/companiontab.js +61 -0
  723. package/dist/src/tui/companiontab.js.map +1 -0
  724. package/dist/src/tui/confighome.js +105 -0
  725. package/dist/src/tui/confighome.js.map +1 -0
  726. package/dist/src/tui/connect.js +44 -0
  727. package/dist/src/tui/connect.js.map +1 -0
  728. package/dist/src/tui/deploy-ceremony.js +75 -0
  729. package/dist/src/tui/deploy-ceremony.js.map +1 -0
  730. package/dist/src/tui/deploy-drain.js +93 -0
  731. package/dist/src/tui/deploy-drain.js.map +1 -0
  732. package/dist/src/tui/deploy-process.js +234 -0
  733. package/dist/src/tui/deploy-process.js.map +1 -0
  734. package/dist/src/tui/deploytab.js +1263 -0
  735. package/dist/src/tui/deploytab.js.map +1 -0
  736. package/dist/src/tui/design.js +111 -0
  737. package/dist/src/tui/design.js.map +1 -0
  738. package/dist/src/tui/digesttab.js +148 -0
  739. package/dist/src/tui/digesttab.js.map +1 -0
  740. package/dist/src/tui/emptystate.js +32 -0
  741. package/dist/src/tui/emptystate.js.map +1 -0
  742. package/dist/src/tui/evidence.js +790 -0
  743. package/dist/src/tui/evidence.js.map +1 -0
  744. package/dist/src/tui/feed.js +415 -0
  745. package/dist/src/tui/feed.js.map +1 -0
  746. package/dist/src/tui/feedbacktab.js +907 -0
  747. package/dist/src/tui/feedbacktab.js.map +1 -0
  748. package/dist/src/tui/filters.js +72 -0
  749. package/dist/src/tui/filters.js.map +1 -0
  750. package/dist/src/tui/ghread.js +272 -0
  751. package/dist/src/tui/ghread.js.map +1 -0
  752. package/dist/src/tui/historypull.js +61 -0
  753. package/dist/src/tui/historypull.js.map +1 -0
  754. package/dist/src/tui/inboxpull.js +197 -0
  755. package/dist/src/tui/inboxpull.js.map +1 -0
  756. package/dist/src/tui/index.js +1425 -0
  757. package/dist/src/tui/index.js.map +1 -0
  758. package/dist/src/tui/issuesink.js +206 -0
  759. package/dist/src/tui/issuesink.js.map +1 -0
  760. package/dist/src/tui/jsonfile.js +32 -0
  761. package/dist/src/tui/jsonfile.js.map +1 -0
  762. package/dist/src/tui/keys.js +97 -0
  763. package/dist/src/tui/keys.js.map +1 -0
  764. package/dist/src/tui/layout.js +70 -0
  765. package/dist/src/tui/layout.js.map +1 -0
  766. package/dist/src/tui/learning.js +469 -0
  767. package/dist/src/tui/learning.js.map +1 -0
  768. package/dist/src/tui/lessonswitch.js +103 -0
  769. package/dist/src/tui/lessonswitch.js.map +1 -0
  770. package/dist/src/tui/livecontrol.js +167 -0
  771. package/dist/src/tui/livecontrol.js.map +1 -0
  772. package/dist/src/tui/memforward.js +310 -0
  773. package/dist/src/tui/memforward.js.map +1 -0
  774. package/dist/src/tui/memincident.js +287 -0
  775. package/dist/src/tui/memincident.js.map +1 -0
  776. package/dist/src/tui/memory.js +485 -0
  777. package/dist/src/tui/memory.js.map +1 -0
  778. package/dist/src/tui/memtab.js +513 -0
  779. package/dist/src/tui/memtab.js.map +1 -0
  780. package/dist/src/tui/metricstab.js +979 -0
  781. package/dist/src/tui/metricstab.js.map +1 -0
  782. package/dist/src/tui/model.js +104 -0
  783. package/dist/src/tui/model.js.map +1 -0
  784. package/dist/src/tui/mouse.js +83 -0
  785. package/dist/src/tui/mouse.js.map +1 -0
  786. package/dist/src/tui/msg.js +196 -0
  787. package/dist/src/tui/msg.js.map +1 -0
  788. package/dist/src/tui/nativecompanion.js +321 -0
  789. package/dist/src/tui/nativecompanion.js.map +1 -0
  790. package/dist/src/tui/openurl.js +17 -0
  791. package/dist/src/tui/openurl.js.map +1 -0
  792. package/dist/src/tui/paint.js +501 -0
  793. package/dist/src/tui/paint.js.map +1 -0
  794. package/dist/src/tui/panes.js +74 -0
  795. package/dist/src/tui/panes.js.map +1 -0
  796. package/dist/src/tui/pipane.js +166 -0
  797. package/dist/src/tui/pipane.js.map +1 -0
  798. package/dist/src/tui/poll-hooks.js +11 -0
  799. package/dist/src/tui/poll-hooks.js.map +1 -0
  800. package/dist/src/tui/poll.js +113 -0
  801. package/dist/src/tui/poll.js.map +1 -0
  802. package/dist/src/tui/proposals.js +715 -0
  803. package/dist/src/tui/proposals.js.map +1 -0
  804. package/dist/src/tui/prpause.js +132 -0
  805. package/dist/src/tui/prpause.js.map +1 -0
  806. package/dist/src/tui/reload.js +351 -0
  807. package/dist/src/tui/reload.js.map +1 -0
  808. package/dist/src/tui/remote.js +312 -0
  809. package/dist/src/tui/remote.js.map +1 -0
  810. package/dist/src/tui/rollover.js +767 -0
  811. package/dist/src/tui/rollover.js.map +1 -0
  812. package/dist/src/tui/scantab.js +1053 -0
  813. package/dist/src/tui/scantab.js.map +1 -0
  814. package/dist/src/tui/setupentry.js +81 -0
  815. package/dist/src/tui/setupentry.js.map +1 -0
  816. package/dist/src/tui/sidebar.js +602 -0
  817. package/dist/src/tui/sidebar.js.map +1 -0
  818. package/dist/src/tui/sshtriage.js +393 -0
  819. package/dist/src/tui/sshtriage.js.map +1 -0
  820. package/dist/src/tui/startup.js +172 -0
  821. package/dist/src/tui/startup.js.map +1 -0
  822. package/dist/src/tui/tabs.js +41 -0
  823. package/dist/src/tui/tabs.js.map +1 -0
  824. package/dist/src/tui/termmirror.js +217 -0
  825. package/dist/src/tui/termmirror.js.map +1 -0
  826. package/dist/src/tui/theme.js +95 -0
  827. package/dist/src/tui/theme.js.map +1 -0
  828. package/dist/src/tui/ticketstab.js +224 -0
  829. package/dist/src/tui/ticketstab.js.map +1 -0
  830. package/dist/src/tui/tmuxsocket.js +101 -0
  831. package/dist/src/tui/tmuxsocket.js.map +1 -0
  832. package/dist/src/tui/trackertickets.js +77 -0
  833. package/dist/src/tui/trackertickets.js.map +1 -0
  834. package/dist/src/tui/unwedge.js +245 -0
  835. package/dist/src/tui/unwedge.js.map +1 -0
  836. package/dist/src/tui/viewer.js +479 -0
  837. package/dist/src/tui/viewer.js.map +1 -0
  838. package/dist/src/tui/viewerinput.js +125 -0
  839. package/dist/src/tui/viewerinput.js.map +1 -0
  840. package/dist/src/updatenotice.js +143 -0
  841. package/dist/src/updatenotice.js.map +1 -0
  842. package/dist/src/usage.js +1007 -0
  843. package/dist/src/usage.js.map +1 -0
  844. package/dist/src/verdicts.js +3848 -0
  845. package/dist/src/verdicts.js.map +1 -0
  846. package/dist/src/version.js +58 -0
  847. package/dist/src/version.js.map +1 -0
  848. package/dist/src/wakes.js +411 -0
  849. package/dist/src/wakes.js.map +1 -0
  850. package/dist/src/webhook.js +725 -0
  851. package/dist/src/webhook.js.map +1 -0
  852. package/dist/src/webhookheal.js +238 -0
  853. package/dist/src/webhookheal.js.map +1 -0
  854. package/dist/src/wire.js +1801 -0
  855. package/dist/src/wire.js.map +1 -0
  856. package/dist/src/worker-session-host.js +531 -0
  857. package/dist/src/worker-session-host.js.map +1 -0
  858. package/dist/src/workerbreaker.js +112 -0
  859. package/dist/src/workerbreaker.js.map +1 -0
  860. package/dist/src/workercontrol.js +49 -0
  861. package/dist/src/workercontrol.js.map +1 -0
  862. package/dist/src/workerlease.js +351 -0
  863. package/dist/src/workerlease.js.map +1 -0
  864. package/dist/src/workerliveness.js +84 -0
  865. package/dist/src/workerliveness.js.map +1 -0
  866. package/dist/src/workerlog.js +65 -0
  867. package/dist/src/workerlog.js.map +1 -0
  868. package/dist/src/workerruntime.js +10 -0
  869. package/dist/src/workerruntime.js.map +1 -0
  870. package/dist/src/workersupervisor.js +1206 -0
  871. package/dist/src/workersupervisor.js.map +1 -0
  872. package/dist/src/workerusage.js +23 -0
  873. package/dist/src/workerusage.js.map +1 -0
  874. package/dist/src/workerworld.js +332 -0
  875. package/dist/src/workerworld.js.map +1 -0
  876. package/dist/src/worktrees.js +984 -0
  877. package/dist/src/worktrees.js.map +1 -0
  878. package/docker/Dockerfile +284 -0
  879. package/docker/Dockerfile.dockerignore +35 -0
  880. package/docker/README.md +221 -0
  881. package/docker/companion/Dockerfile +139 -0
  882. package/docker/companion/agents/evidence.md +34 -0
  883. package/docker/companion/agents/explore.md +26 -0
  884. package/docker/companion/agents/pass-survey.md +49 -0
  885. package/docker/companion/agents/study.md +26 -0
  886. package/docker/companion/entrypoint.sh +343 -0
  887. package/docker/companion/memory-health.mjs +269 -0
  888. package/docker/entrypoint.sh +782 -0
  889. package/docker/memory/Dockerfile +191 -0
  890. package/docker/memory/Dockerfile.dockerignore +20 -0
  891. package/docker/memory/README.md +60 -0
  892. package/docker/memory/entrypoint.sh +163 -0
  893. package/docker/setid/qcr-setid.c +109 -0
  894. package/docker/tunnel/Dockerfile.cloudflared +51 -0
  895. package/docker/tunnel/Dockerfile.ngrok +40 -0
  896. package/docker/tunnel/sidecar.mjs +411 -0
  897. package/docker/vm/tmux.conf +26 -0
  898. package/docker-compose.yml +348 -0
  899. package/extensions/cli/bash-guard-ambient.ts +71 -0
  900. package/extensions/cli/bash-guard.ts +12 -0
  901. package/extensions/cli/companion-guard.ts +8 -0
  902. package/extensions/cli/debug-round.ts +9 -0
  903. package/extensions/cli/inject-bridge.ts +8 -0
  904. package/extensions/cli/ledger-pin.ts +13 -0
  905. package/extensions/cli/lens.ts +22 -0
  906. package/extensions/cli/muted-theme.ts +9 -0
  907. package/extensions/cli/onboarding.ts +8 -0
  908. package/extensions/cli/setup.ts +8 -0
  909. package/extensions/cli/tui-view.ts +8 -0
  910. package/extensions/companion-guard.ts +411 -0
  911. package/extensions/debug-round.ts +281 -0
  912. package/extensions/inject-bridge.ts +630 -0
  913. package/extensions/inline-bash.ts +102 -0
  914. package/extensions/muted-theme.json +68 -0
  915. package/extensions/muted-theme.ts +23 -0
  916. package/extensions/onboarding.ts +233 -0
  917. package/extensions/setup-replay.ts +336 -0
  918. package/extensions/setup-scan.ts +1236 -0
  919. package/extensions/setup-triage.ts +190 -0
  920. package/extensions/setup.ts +1428 -0
  921. package/extensions/tui-view.ts +489 -0
  922. package/extensions/vendor/openviking-pi/LICENSE +201 -0
  923. package/extensions/vendor/openviking-pi/NOTICE +68 -0
  924. package/extensions/vendor/openviking-pi/README.md +550 -0
  925. package/extensions/vendor/openviking-pi/client.ts +588 -0
  926. package/extensions/vendor/openviking-pi/config.json +28 -0
  927. package/extensions/vendor/openviking-pi/config.ts +271 -0
  928. package/extensions/vendor/openviking-pi/index.ts +523 -0
  929. package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
  930. package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
  931. package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
  932. package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
  933. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
  934. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
  935. package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
  936. package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
  937. package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
  938. package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
  939. package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
  940. package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
  941. package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
  942. package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
  943. package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
  944. package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
  945. package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
  946. package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
  947. package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
  948. package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
  949. package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
  950. package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
  951. package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
  952. package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
  953. package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
  954. package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
  955. package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
  956. package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
  957. package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
  958. package/extensions/vendor/openviking-pi/recall.ts +149 -0
  959. package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
  960. package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
  961. package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
  962. package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
  963. package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
  964. package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
  965. package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
  966. package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
  967. package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
  968. package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
  969. package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
  970. package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
  971. package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
  972. package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
  973. package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
  974. package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
  975. package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  976. package/extensions/vendor/openviking-pi/sync.ts +163 -0
  977. package/extensions/vendor/openviking-pi/takeover.ts +36 -0
  978. package/extensions/vendor/openviking-pi/tools.ts +479 -0
  979. package/extensions/vendor/openviking-pi/upstream.json +48 -0
  980. package/package.json +138 -4
  981. package/profiles/default/README.md +21 -0
  982. package/profiles/default/addenda/.gitkeep +0 -0
  983. package/profiles/default/profile.toml +17 -0
  984. package/profiles/example-django/README.md +21 -0
  985. package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  986. package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  987. package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  988. package/profiles/example-django/addenda/40-orm-usage.md +19 -0
  989. package/profiles/example-django/addenda/50-declared-shape.md +10 -0
  990. package/profiles/example-django/addenda/60-config-placement.md +14 -0
  991. package/profiles/example-django/heuristics.toml +24 -0
  992. package/profiles/example-django/profile.toml +9 -0
  993. package/profiles/fast/README.md +24 -0
  994. package/profiles/fast/profile.toml +59 -0
  995. package/profiles/minimal/README.md +17 -0
  996. package/profiles/minimal/addenda/.gitkeep +0 -0
  997. package/profiles/minimal/profile.toml +8 -0
  998. package/profiles/snappy/README.md +24 -0
  999. package/profiles/snappy/addenda/10-race-as-question.md +6 -0
  1000. package/profiles/snappy/profile.toml +46 -0
  1001. package/prompts/companion-day-open.md +46 -0
  1002. package/prompts/companion-first-run.md +104 -0
  1003. package/prompts/companion.md +821 -0
  1004. package/prompts/conversation-system.md +117 -0
  1005. package/prompts/eod-sweep.md +120 -0
  1006. package/prompts/onboarding.md +74 -0
  1007. package/prompts/pass-common.md +52 -0
  1008. package/prompts/pass-quality-common.md +22 -0
  1009. package/prompts/round-first.md +874 -0
  1010. package/prompts/round-follow-up.md +991 -0
  1011. package/prompts/round-re-review.md +889 -0
  1012. package/prompts/round-recheck.md +196 -0
  1013. package/prompts/round-staleness.md +856 -0
  1014. package/schemas/review-result.v1.json +391 -0
  1015. package/schemas/scan-result.v1.json +421 -0
  1016. package/schemas/version.v1.json +57 -0
  1017. package/scripts/approve-pr.sh +16 -0
  1018. package/scripts/build-stamp.mjs +67 -0
  1019. package/scripts/commit-verdict.sh +155 -0
  1020. package/scripts/companion-e2e.sh +250 -0
  1021. package/scripts/companion-memory-forward.mjs +1966 -0
  1022. package/scripts/config-fingerprint.sh +28 -0
  1023. package/scripts/deploy-manifest.mjs +141 -0
  1024. package/scripts/deploy.sh +1435 -0
  1025. package/scripts/directory-lock.mjs +193 -0
  1026. package/scripts/heartbeat-fresh.mjs +54 -0
  1027. package/scripts/install-locator.sh +328 -0
  1028. package/scripts/install-wrapper.sh +76 -0
  1029. package/scripts/memory-search-check.mjs +98 -0
  1030. package/scripts/mint-token.sh +212 -0
  1031. package/scripts/openviking-embedder.py +392 -0
  1032. package/scripts/openviking-ov-conf.mjs +161 -0
  1033. package/scripts/openviking-provision.mjs +1122 -0
  1034. package/scripts/openviking-reembed.mjs +1154 -0
  1035. package/scripts/post-review.sh +16 -0
  1036. package/scripts/pr-checks.sh +256 -0
  1037. package/scripts/pr-context.py +174 -0
  1038. package/scripts/pr-context.sh +63 -0
  1039. package/scripts/pr-self.sh +144 -0
  1040. package/scripts/pr-threads.sh +145 -0
  1041. package/scripts/qcr +15 -0
  1042. package/scripts/qcr-host.sh +769 -0
  1043. package/scripts/render-review.sh +24 -0
  1044. package/scripts/reply-thread.sh +16 -0
  1045. package/scripts/required-passes.py +112 -0
  1046. package/scripts/smoke-companion.sh +211 -0
  1047. package/scripts/tui-deploy-runner.mjs +61 -0
  1048. package/scripts/verify-dist.mjs +201 -0
  1049. package/scripts/vm-ghcr-login.sh +188 -0
  1050. package/seeds/agents/pass-baseline.md +61 -0
  1051. package/seeds/agents/pass-contracts.md +30 -0
  1052. package/seeds/agents/pass-data-safety.md +54 -0
  1053. package/seeds/agents/pass-delivery.md +37 -0
  1054. package/seeds/agents/pass-prod-risk.md +24 -0
  1055. package/seeds/agents/pass-security.md +31 -0
  1056. package/seeds/agents/pass-statelessness.md +27 -0
  1057. package/seeds/agents/pass-ui.md +23 -0
  1058. package/seeds/agents/pass-validate.md +50 -0
  1059. package/seeds/prompts/conversation-system.md +117 -0
  1060. package/seeds/prompts/eod-sweep.md +120 -0
  1061. package/seeds/prompts/pass-common.md +52 -0
  1062. package/skills/companion/adjudicate/SKILL.md +247 -0
  1063. package/skills/companion/evidence/SKILL.md +150 -0
  1064. package/skills/companion/land/SKILL.md +200 -0
  1065. package/skills/companion/replay/SKILL.md +138 -0
  1066. package/skills/companion/setup/SKILL.md +167 -0
  1067. package/skills/companion/survey/SKILL.md +160 -0
  1068. package/skills/selfimprove/README.md +34 -0
  1069. package/skills/selfimprove/SKILL.md +127 -0
  1070. package/skills/selfimprove/agents/openai.yaml +2 -0
  1071. package/skills/selfimprove/commands/selfimprove.md +6 -0
  1072. package/skills/selfimprove/references/fix-loop.md +49 -0
  1073. package/skills/selfimprove/references/ledger.md +20 -0
  1074. package/skills/selfimprove/references/setup.md +40 -0
  1075. package/src/anomalies.ts +566 -0
  1076. package/src/answerworker.ts +251 -0
  1077. package/src/appstamp.ts +65 -0
  1078. package/src/archive/admission.ts +3 -0
  1079. package/src/archive/archivedb.ts +548 -0
  1080. package/src/archive/archiver.ts +1963 -0
  1081. package/src/archive/archivewrite.ts +279 -0
  1082. package/src/archive/bundle.ts +333 -0
  1083. package/src/archive/dbmove.ts +911 -0
  1084. package/src/archive/duplicatecounts.ts +98 -0
  1085. package/src/archive/eligibility.ts +181 -0
  1086. package/src/archive/extract.ts +342 -0
  1087. package/src/archive/fsutil.ts +415 -0
  1088. package/src/archive/gzfile.ts +370 -0
  1089. package/src/archive/kinds.ts +21 -0
  1090. package/src/archive/livecheckpoint.ts +217 -0
  1091. package/src/archive/liveretention.ts +180 -0
  1092. package/src/archive/manifest.ts +519 -0
  1093. package/src/archive/overseer.ts +336 -0
  1094. package/src/archive/piglobal.ts +174 -0
  1095. package/src/archive/plan.ts +612 -0
  1096. package/src/archive/prarchive.ts +984 -0
  1097. package/src/archive/prfiles.ts +406 -0
  1098. package/src/archive/report.ts +252 -0
  1099. package/src/archive/restore.ts +330 -0
  1100. package/src/archive/rows.ts +187 -0
  1101. package/src/archive/seal.ts +630 -0
  1102. package/src/archive/status.ts +719 -0
  1103. package/src/archive/storepass.ts +581 -0
  1104. package/src/backfill.ts +362 -0
  1105. package/src/bench/cost.ts +113 -0
  1106. package/src/bench/exec.ts +90 -0
  1107. package/src/bench/values.ts +13 -0
  1108. package/src/botcontrol.ts +176 -0
  1109. package/src/botworker.ts +953 -0
  1110. package/src/capture.ts +321 -0
  1111. package/src/cases.ts +1789 -0
  1112. package/src/channelfs.ts +26 -0
  1113. package/src/checklist.ts +506 -0
  1114. package/src/ciretry.ts +141 -0
  1115. package/src/cli.ts +2271 -0
  1116. package/src/cliops.ts +384 -0
  1117. package/src/clonedir.ts +15 -0
  1118. package/src/commandeffects.ts +57 -0
  1119. package/src/commands.ts +1678 -0
  1120. package/src/companioninbox.ts +239 -0
  1121. package/src/compare.ts +957 -0
  1122. package/src/config.ts +2050 -0
  1123. package/src/configcheck.ts +133 -0
  1124. package/src/coreext.ts +47 -0
  1125. package/src/daemon.ts +1302 -0
  1126. package/src/debuground.ts +927 -0
  1127. package/src/debugroundpi.ts +262 -0
  1128. package/src/diagnostics.ts +14 -0
  1129. package/src/digest.ts +183 -0
  1130. package/src/dispatch.ts +2250 -0
  1131. package/src/engine/answerguard.ts +27 -0
  1132. package/src/engine/botsdk.ts +336 -0
  1133. package/src/engine/caserunner-sdk.ts +423 -0
  1134. package/src/engine/debugpi.ts +235 -0
  1135. package/src/eod.ts +669 -0
  1136. package/src/eodpinwatch.ts +556 -0
  1137. package/src/escalation.ts +1253 -0
  1138. package/src/eventloop.ts +1045 -0
  1139. package/src/execboundary.ts +633 -0
  1140. package/src/feedback.ts +1162 -0
  1141. package/src/findingscheck.ts +298 -0
  1142. package/src/fsdenied.ts +24 -0
  1143. package/src/ghread.ts +242 -0
  1144. package/src/github.ts +1497 -0
  1145. package/src/homes.ts +175 -0
  1146. package/src/hostroute.ts +53 -0
  1147. package/src/improvements.ts +787 -0
  1148. package/src/install.ts +1103 -0
  1149. package/src/installkind.ts +50 -0
  1150. package/src/ipc/capability.ts +333 -0
  1151. package/src/ipc/client.ts +269 -0
  1152. package/src/ipc/control.ts +90 -0
  1153. package/src/ipc/ghreadshim.ts +94 -0
  1154. package/src/ipc/launcher.ts +65 -0
  1155. package/src/ipc/permits.ts +362 -0
  1156. package/src/ipc/postshim.ts +164 -0
  1157. package/src/ipc/protocol.ts +342 -0
  1158. package/src/ipc/server.ts +371 -0
  1159. package/src/ipc/workerspec.ts +218 -0
  1160. package/src/learner/learner.ts +1655 -0
  1161. package/src/learner/miner.ts +1290 -0
  1162. package/src/learner/packet.ts +340 -0
  1163. package/src/learner/receipt.ts +168 -0
  1164. package/src/learner/status.ts +107 -0
  1165. package/src/ledgercheck.ts +157 -0
  1166. package/src/lessonledger.ts +703 -0
  1167. package/src/lessonselect.ts +888 -0
  1168. package/src/lessonshadow.ts +810 -0
  1169. package/src/levers.ts +393 -0
  1170. package/src/live/projection.ts +491 -0
  1171. package/src/live/publish.ts +92 -0
  1172. package/src/live/read.ts +86 -0
  1173. package/src/livecli.ts +336 -0
  1174. package/src/loginhint.ts +56 -0
  1175. package/src/loginrefresh/atomic.ts +76 -0
  1176. package/src/loginrefresh/lock.ts +104 -0
  1177. package/src/loginrefresh/main.ts +59 -0
  1178. package/src/loginrefresh/pirefresh.ts +57 -0
  1179. package/src/loginrefresh/refresher.ts +326 -0
  1180. package/src/loginrefresh/supervisor.ts +273 -0
  1181. package/src/loginrefresh/types.ts +35 -0
  1182. package/src/memory/context.ts +385 -0
  1183. package/src/memory/datafence.ts +9 -0
  1184. package/src/memory/delivery.ts +171 -0
  1185. package/src/memory/eventtags.ts +80 -0
  1186. package/src/memory/health.ts +953 -0
  1187. package/src/memory/import.ts +664 -0
  1188. package/src/memory/layout.ts +261 -0
  1189. package/src/memory/legacy-file.ts +104 -0
  1190. package/src/memory/lessons.ts +447 -0
  1191. package/src/memory/ovsession.ts +601 -0
  1192. package/src/memory/relayout.ts +335 -0
  1193. package/src/memory/roundactivity.ts +209 -0
  1194. package/src/memory/selection.ts +989 -0
  1195. package/src/memory/shadowstatus.ts +125 -0
  1196. package/src/memory/store.ts +275 -0
  1197. package/src/memory/textidentity.ts +8 -0
  1198. package/src/metrics.ts +1056 -0
  1199. package/src/missingconfig.ts +7 -0
  1200. package/src/npmverbs.ts +264 -0
  1201. package/src/onboard/choices.ts +222 -0
  1202. package/src/onboard/engine.ts +518 -0
  1203. package/src/onboard/launch.ts +115 -0
  1204. package/src/onboard/steps/appslug.ts +265 -0
  1205. package/src/onboard/steps/bringup.ts +482 -0
  1206. package/src/onboard/steps/compose.ts +459 -0
  1207. package/src/onboard/steps/configdelivery.ts +58 -0
  1208. package/src/onboard/steps/configfill.ts +1352 -0
  1209. package/src/onboard/steps/deploysh.ts +105 -0
  1210. package/src/onboard/steps/embeddings.ts +550 -0
  1211. package/src/onboard/steps/escalation.ts +640 -0
  1212. package/src/onboard/steps/githubapp.ts +1414 -0
  1213. package/src/onboard/steps/golive.ts +577 -0
  1214. package/src/onboard/steps/index.ts +71 -0
  1215. package/src/onboard/steps/ingress.ts +907 -0
  1216. package/src/onboard/steps/inventory.ts +526 -0
  1217. package/src/onboard/steps/llmauth.ts +1095 -0
  1218. package/src/onboard/steps/memory.ts +1290 -0
  1219. package/src/onboard/steps/memorykeys.ts +85 -0
  1220. package/src/onboard/steps/preflight.ts +191 -0
  1221. package/src/onboard/steps/proof.ts +624 -0
  1222. package/src/onboard/steps/ssh.ts +377 -0
  1223. package/src/onboard/steps/topology.ts +337 -0
  1224. package/src/onboard/steps/transport.ts +665 -0
  1225. package/src/onboard/steps/verifyup.ts +448 -0
  1226. package/src/onboard/steps/vmdeploy.ts +442 -0
  1227. package/src/onboard/toml.ts +50 -0
  1228. package/src/opencli.ts +591 -0
  1229. package/src/openpr.ts +568 -0
  1230. package/src/openviking/client.ts +1131 -0
  1231. package/src/openviking/context.ts +263 -0
  1232. package/src/openviking/embedder.ts +516 -0
  1233. package/src/openviking/gapsink.ts +63 -0
  1234. package/src/openviking/health.ts +83 -0
  1235. package/src/openviking/index.ts +119 -0
  1236. package/src/openviking/lessonfind.ts +92 -0
  1237. package/src/openviking/probe.ts +129 -0
  1238. package/src/openviking/proc.ts +110 -0
  1239. package/src/openviking/provisionreport.ts +160 -0
  1240. package/src/openviking/reembed.ts +412 -0
  1241. package/src/openviking/relayout.ts +177 -0
  1242. package/src/openviking/sidecar-main.ts +38 -0
  1243. package/src/openviking/sidecar.ts +106 -0
  1244. package/src/openviking/store.ts +2025 -0
  1245. package/src/openviking/supervisor.ts +474 -0
  1246. package/src/openviking/types.ts +174 -0
  1247. package/src/operator-recovery.ts +237 -0
  1248. package/src/operatormessage.ts +52 -0
  1249. package/src/outcomes.ts +1670 -0
  1250. package/src/piauthcopy.ts +12 -0
  1251. package/src/picli.ts +49 -0
  1252. package/src/picwd.ts +25 -0
  1253. package/src/pilogin.ts +37 -0
  1254. package/src/pingpong.ts +1339 -0
  1255. package/src/posting-journal.ts +549 -0
  1256. package/src/posting-outbox.ts +802 -0
  1257. package/src/posting-receipt.ts +324 -0
  1258. package/src/posting.ts +603 -0
  1259. package/src/postops.ts +832 -0
  1260. package/src/pr-activity.ts +20 -0
  1261. package/src/pr-author.ts +6 -0
  1262. package/src/pr-conversation.ts +432 -0
  1263. package/src/pr-session-lifecycle.ts +631 -0
  1264. package/src/preflight.ts +391 -0
  1265. package/src/priorresolve.ts +343 -0
  1266. package/src/qcrbin.ts +186 -0
  1267. package/src/recheck.ts +1775 -0
  1268. package/src/reconcile.ts +1030 -0
  1269. package/src/recordedfindings.ts +27 -0
  1270. package/src/regression.ts +833 -0
  1271. package/src/replay.ts +816 -0
  1272. package/src/requiredpasses.ts +148 -0
  1273. package/src/resumecheck.ts +246 -0
  1274. package/src/reviewcli.ts +787 -0
  1275. package/src/reviewcontext.ts +35 -0
  1276. package/src/reviewdetach.ts +734 -0
  1277. package/src/reviewhistory.ts +427 -0
  1278. package/src/reviewhome.ts +36 -0
  1279. package/src/reviewmemory.ts +133 -0
  1280. package/src/reviewrendercli.ts +139 -0
  1281. package/src/round-work.ts +42 -0
  1282. package/src/runhost.ts +108 -0
  1283. package/src/scanauth.ts +36 -0
  1284. package/src/scanbox.ts +1081 -0
  1285. package/src/scancli.ts +1296 -0
  1286. package/src/scandriverlog.ts +32 -0
  1287. package/src/session-bridge.ts +139 -0
  1288. package/src/session-command-payload.ts +35 -0
  1289. package/src/session-command-supervisor.ts +1142 -0
  1290. package/src/sessionjsonl.ts +88 -0
  1291. package/src/sessions.ts +659 -0
  1292. package/src/setup/adapters.ts +335 -0
  1293. package/src/setup/baselines.ts +110 -0
  1294. package/src/setup/caserunner.ts +611 -0
  1295. package/src/setup/configcommit.ts +443 -0
  1296. package/src/setup/engine.ts +1001 -0
  1297. package/src/setup/facts.ts +627 -0
  1298. package/src/setup/findings.ts +1264 -0
  1299. package/src/setup/history.ts +443 -0
  1300. package/src/setup/interview.ts +243 -0
  1301. package/src/setup/ledger.ts +324 -0
  1302. package/src/setup/pins.ts +66 -0
  1303. package/src/setup/portrait.ts +635 -0
  1304. package/src/setup/presentation.ts +18 -0
  1305. package/src/setup/profile.ts +508 -0
  1306. package/src/setup/replay.ts +370 -0
  1307. package/src/setup/replaybuild.ts +392 -0
  1308. package/src/setup/replayrun.ts +1096 -0
  1309. package/src/setup/replaysource.ts +198 -0
  1310. package/src/setup/retune.ts +415 -0
  1311. package/src/setup/retunelog.ts +95 -0
  1312. package/src/setup/scan.ts +1033 -0
  1313. package/src/setup/scanbuild.ts +168 -0
  1314. package/src/setup/scanfacts.ts +107 -0
  1315. package/src/setup/scanlease.ts +297 -0
  1316. package/src/setup/scanreport.ts +373 -0
  1317. package/src/setup/scanrun.ts +2268 -0
  1318. package/src/setup/scantree.ts +259 -0
  1319. package/src/setup/stage2.ts +926 -0
  1320. package/src/setup/stage2request.ts +347 -0
  1321. package/src/setup/survey.ts +458 -0
  1322. package/src/setup/surveyrun.ts +758 -0
  1323. package/src/setup/textsniff.ts +95 -0
  1324. package/src/setup/trackersink.ts +433 -0
  1325. package/src/setup/triage.ts +768 -0
  1326. package/src/setup/tuning.ts +766 -0
  1327. package/src/setup/unitcut.ts +523 -0
  1328. package/src/singleflight.ts +86 -0
  1329. package/src/slack/announce.ts +911 -0
  1330. package/src/slack/api.ts +303 -0
  1331. package/src/slack/messages.ts +288 -0
  1332. package/src/slack/origin.ts +599 -0
  1333. package/src/slack/policy.ts +354 -0
  1334. package/src/slack/reply.ts +201 -0
  1335. package/src/slack/router.ts +285 -0
  1336. package/src/slack/socket.ts +384 -0
  1337. package/src/slack/types.ts +108 -0
  1338. package/src/sqlitehint.ts +32 -0
  1339. package/src/ssh-observation.ts +30 -0
  1340. package/src/state.ts +8243 -0
  1341. package/src/statemachine.ts +369 -0
  1342. package/src/stubs.ts +68 -0
  1343. package/src/takeover.ts +634 -0
  1344. package/src/takeoverpi.ts +293 -0
  1345. package/src/terminal-failure.ts +41 -0
  1346. package/src/thread-resolution-refusal.ts +2 -0
  1347. package/src/tmux.ts +153 -0
  1348. package/src/trackerid.ts +41 -0
  1349. package/src/transcriptindex.ts +665 -0
  1350. package/src/tui/actions.ts +484 -0
  1351. package/src/tui/alerts.ts +666 -0
  1352. package/src/tui/app.ts +1979 -0
  1353. package/src/tui/archiveops.ts +192 -0
  1354. package/src/tui/autofile.ts +233 -0
  1355. package/src/tui/board.ts +1432 -0
  1356. package/src/tui/candidates.ts +889 -0
  1357. package/src/tui/cellwidth.ts +148 -0
  1358. package/src/tui/changelog-store.ts +503 -0
  1359. package/src/tui/chord.ts +25 -0
  1360. package/src/tui/clipboard.ts +92 -0
  1361. package/src/tui/cmdbar.ts +296 -0
  1362. package/src/tui/companion.ts +1427 -0
  1363. package/src/tui/companionplan.ts +247 -0
  1364. package/src/tui/companiontab.ts +83 -0
  1365. package/src/tui/confighome.ts +155 -0
  1366. package/src/tui/connect.ts +68 -0
  1367. package/src/tui/deploy-ceremony.ts +104 -0
  1368. package/src/tui/deploy-drain.ts +129 -0
  1369. package/src/tui/deploy-process.ts +265 -0
  1370. package/src/tui/deploytab.ts +1523 -0
  1371. package/src/tui/design.ts +125 -0
  1372. package/src/tui/digesttab.ts +214 -0
  1373. package/src/tui/emptystate.ts +39 -0
  1374. package/src/tui/evidence.ts +999 -0
  1375. package/src/tui/feed.ts +604 -0
  1376. package/src/tui/feedbacktab.ts +1077 -0
  1377. package/src/tui/filters.ts +90 -0
  1378. package/src/tui/ghread.ts +321 -0
  1379. package/src/tui/historypull.ts +81 -0
  1380. package/src/tui/inboxpull.ts +236 -0
  1381. package/src/tui/index.ts +1565 -0
  1382. package/src/tui/issuesink.ts +238 -0
  1383. package/src/tui/jsonfile.ts +42 -0
  1384. package/src/tui/keys.ts +109 -0
  1385. package/src/tui/layout.ts +108 -0
  1386. package/src/tui/learning.ts +627 -0
  1387. package/src/tui/lessonswitch.ts +118 -0
  1388. package/src/tui/livecontrol.ts +228 -0
  1389. package/src/tui/memforward.ts +443 -0
  1390. package/src/tui/memincident.ts +363 -0
  1391. package/src/tui/memory.ts +620 -0
  1392. package/src/tui/memtab.ts +606 -0
  1393. package/src/tui/metricstab.ts +1177 -0
  1394. package/src/tui/model.ts +131 -0
  1395. package/src/tui/mouse.ts +98 -0
  1396. package/src/tui/msg.ts +268 -0
  1397. package/src/tui/nativecompanion.ts +417 -0
  1398. package/src/tui/openurl.ts +21 -0
  1399. package/src/tui/paint.ts +511 -0
  1400. package/src/tui/panes.ts +98 -0
  1401. package/src/tui/pipane.ts +219 -0
  1402. package/src/tui/poll-hooks.ts +12 -0
  1403. package/src/tui/poll.ts +147 -0
  1404. package/src/tui/proposals.ts +838 -0
  1405. package/src/tui/prpause.ts +150 -0
  1406. package/src/tui/reload.ts +436 -0
  1407. package/src/tui/remote.ts +413 -0
  1408. package/src/tui/rollover.ts +969 -0
  1409. package/src/tui/scantab.ts +1290 -0
  1410. package/src/tui/setupentry.ts +93 -0
  1411. package/src/tui/sidebar.ts +749 -0
  1412. package/src/tui/sshtriage.ts +510 -0
  1413. package/src/tui/startup.ts +231 -0
  1414. package/src/tui/tabs.ts +45 -0
  1415. package/src/tui/termmirror.ts +224 -0
  1416. package/src/tui/theme.ts +95 -0
  1417. package/src/tui/ticketstab.ts +310 -0
  1418. package/src/tui/tmuxsocket.ts +129 -0
  1419. package/src/tui/trackertickets.ts +84 -0
  1420. package/src/tui/unwedge.ts +286 -0
  1421. package/src/tui/viewer.ts +575 -0
  1422. package/src/tui/viewerinput.ts +173 -0
  1423. package/src/updatenotice.ts +178 -0
  1424. package/src/usage.ts +1154 -0
  1425. package/src/verdicts.ts +4689 -0
  1426. package/src/version.ts +80 -0
  1427. package/src/wakes.ts +476 -0
  1428. package/src/webhook.ts +942 -0
  1429. package/src/webhookheal.ts +276 -0
  1430. package/src/wire.ts +2153 -0
  1431. package/src/worker-session-host.ts +602 -0
  1432. package/src/workerbreaker.ts +156 -0
  1433. package/src/workercontrol.ts +104 -0
  1434. package/src/workerlease.ts +441 -0
  1435. package/src/workerliveness.ts +103 -0
  1436. package/src/workerlog.ts +79 -0
  1437. package/src/workerruntime.ts +30 -0
  1438. package/src/workersupervisor.ts +1538 -0
  1439. package/src/workerusage.ts +42 -0
  1440. package/src/workerworld.ts +427 -0
  1441. package/src/worktrees.ts +1138 -0
  1442. package/tests/fixtures/regression-cases.json +795 -0
  1443. package/tests/fixtures/seed-manifest.json +19 -0
@@ -0,0 +1,1275 @@
1
+ // Local companion container lifecycle.
2
+ //
3
+ // The shape mirrors remote.ts: command construction is pure so tests can assert
4
+ // the exact argv, execution goes through one injectable runner, and nothing
5
+ // here ever kills anything the user did not explicitly ask to stop.
6
+ //
7
+ // Lifecycle contract:
8
+ // `qcr tui` start → adopt a running container, else `docker start`, else
9
+ // `docker run` it. Quit → `docker stop` (conversation, pihome auth and
10
+ // the pi home survive on volumes; next start is a fast resume). TUI crash →
11
+ // the container keeps running and the next start adopts it. A second
12
+ // concurrent TUI attaches to the same container, never spawns a twin:
13
+ // ensureUp() and spawnPi() are both idempotent. A container goes away only
14
+ // on the explicit `qcr companion down`, or when ensureUp() recreates one
15
+ // whose mounts or DNS drifted from runArgv() or that can no longer start —
16
+ // the conversation and the pi home survive both on their volumes.
17
+ import { spawn } from "node:child_process";
18
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, } from "node:fs";
19
+ import { homedir, tmpdir } from "node:os";
20
+ import { isAbsolute, join } from "node:path";
21
+ import { companionConfigMirrorDir, CONFIG_FILE, companionGithubDir, configHome, KEYS_FILE, homeProfileDirs, legacySecretsDir, locateConfig, locateKeysFile, PORTRAIT_DIR, PROFILES_DIR, stateHome, } from "../install.js";
22
+ import { writeAtomic } from "../channelfs.js";
23
+ import { appStamp } from "../appstamp.js";
24
+ import { TOKEN_FILE } from "./ghread.js";
25
+ /** The one word `qcr companion status` prints — "unknown — <why>" when docker
26
+ * could not be asked, so a failed inspect never reads as a confirmed absence. */
27
+ export function companionStateWord(st) {
28
+ if (st.unknown !== undefined)
29
+ return `unknown — docker inspect failed: ${st.unknown}`;
30
+ return st.running ? "running" : st.exists ? "stopped" : "absent";
31
+ }
32
+ /**
33
+ * The companion's resolvers, pinned at `docker run`. Without this, docker's
34
+ * embedded DNS forwards to whatever upstream the host had at container
35
+ * creation — and that upstream is network-specific, so the first network
36
+ * change after a long-lived start leaves every lookup dead (pi: "Error: fetch
37
+ * failed" on all model calls). Public resolvers are identical on every
38
+ * network, and the companion needs no LAN names: it talks to public endpoints
39
+ * and to the memory bridge by IP.
40
+ */
41
+ export const PUBLIC_DNS = ["1.1.1.1", "8.8.8.8"];
42
+ export const realCompanionFs = {
43
+ exists: (p) => existsSync(p),
44
+ read: (p) => {
45
+ try {
46
+ return readFileSync(p, "utf8");
47
+ }
48
+ catch {
49
+ return undefined;
50
+ }
51
+ },
52
+ mode: (p) => {
53
+ try {
54
+ return statSync(p).mode & 0o777;
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ },
60
+ };
61
+ /** In-container paths. Fixed by the image (docker/Dockerfile, run in its
62
+ * `companion` mode), so they are constants rather than knobs — the host side
63
+ * of each is configured. */
64
+ export const CONTAINER = {
65
+ /** The release image's fixed HOME (`ENV HOME` in docker/Dockerfile),
66
+ * writable by whatever uid the container runs as. */
67
+ home: "/home/qcr",
68
+ piHome: "/home/qcr/.pi",
69
+ /** The image's app root: the companion's extensions, its system prompt and
70
+ * its agent set are loaded from here, never from the /workspace
71
+ * mount, so a change to them lands with `qcr companion rebuild`. */
72
+ app: "/opt/qcr",
73
+ /** The owner's checkout, read-write: the repo the companion works on and
74
+ * commits to. It is the working directory, and the data it is about — not
75
+ * where its code comes from. */
76
+ workspace: "/workspace",
77
+ tracker: "/tracker",
78
+ /** The tracker's protocol docs, read-only. `/tracker` carries the
79
+ * issues; the doc that says how to write them lives one level up on the
80
+ * host, outside every other mount. */
81
+ trackerDocs: "/tracker-docs",
82
+ /** The reviewed repo, read-only. The companion reads the code a
83
+ * review is about; `:ro` is the whole guarantee that it can never touch that
84
+ * repo's `master`. Mounted only when the host checkout exists — a missing
85
+ * one leaves the companion under-equipped, never broken. A generic path:
86
+ * the container path is not the place a repo's name belongs. */
87
+ reviewedRepo: "/reviewed-repo",
88
+ /** The install's config home, in part: `profiles/` read-write — setup
89
+ * writes and re-tunes the repo profile there — `portrait/` read-write (the
90
+ * portrait narrative), and a mirror of the live `config.toml` read-only.
91
+ * Deliberately not the whole home: it holds
92
+ * `secrets/` (the App's private key, which can write to GitHub), and the
93
+ * companion's reach into GitHub is the read-only token below and nothing
94
+ * else. Mounting the two pieces rather than masking a subdirectory is what
95
+ * keeps a `secrets/` created later out as well. */
96
+ configHome: "/qcr-config",
97
+ /** Inside `configHome`: the profiles dir (`:rw`) and a read-only mirror of
98
+ * the live config (`configLive`, a directory mount). The live config is not
99
+ * bind-mounted as a file: a file bind pins the inode, and every writer of
100
+ * config.toml (the onboarding wizard, most editors) replaces it by rename —
101
+ * the companion would read the old text until the container was recreated,
102
+ * and no drift check can see it (the source path never changes). The host
103
+ * copies the located config into the mirror dir instead
104
+ * (`syncConfigMirror`), and a rename inside a mounted dir is visible.
105
+ * Spelled out so the in-container paths are literals — nothing here builds
106
+ * a host config path (the locator does). */
107
+ configLive: "/qcr-config/live",
108
+ configFile: "/qcr-config/live/config.toml",
109
+ profiles: "/qcr-config/profiles",
110
+ /** Inside `configHome`: the repo portrait's narrative (`:rw`) —
111
+ * setup writes it there, keeping the one it replaced. */
112
+ portrait: "/qcr-config/portrait",
113
+ /** The install's state home, read-write: the survey, the scan
114
+ * ledger, findings and the portrait facts — PC-only, never shipped. */
115
+ stateHome: "/qcr-state",
116
+ /** The down-scoped read-only GitHub token, as an env file the host re-mints
117
+ * before it expires (`src/tui/ghread.ts`). `:ro` — the file is the
118
+ * host's to write; the token itself cannot write to GitHub either way. */
119
+ githubRead: "/github-read",
120
+ /** Finished-round evidence, read-only. The TUI holds the VM access and
121
+ * copies each finished round's artefacts down here; the companion reads
122
+ * plain files. That is the whole of the transport — it grants no ssh, no
123
+ * credential and no network reach (the companion boundary).
124
+ * `:ro` because this is a record: read history, never rewrite it. */
125
+ evidence: "/evidence",
126
+ feed: "/feed",
127
+ alerts: "/alerts",
128
+ /** The shared tmux-socket volume (`tmux-socket` mode): the companion's
129
+ * tmux server listens inside it, and the containerised TUI mounts the same
130
+ * volume to drive that server with `tmux -S`. The image creates the dir
131
+ * 1777, so a fresh named volume is writable by whatever uid runs. */
132
+ tmuxDir: "/run/qcr-tmux",
133
+ };
134
+ /** The named volume holding the companion's tmux socket. */
135
+ export function tmuxVolume(cfg) {
136
+ return `${cfg.container_name}-tmux`;
137
+ }
138
+ /** The companion's tmux server socket, the same path in both containers. */
139
+ export function tmuxSocketPath(cfg) {
140
+ return `${CONTAINER.tmuxDir}/${cfg.session}.sock`;
141
+ }
142
+ /** Set on the companion container in `tmux-socket` mode: the socket its
143
+ * entrypoint starts the tmux server on (docker/entrypoint.sh). */
144
+ export const TMUX_SOCKET_ENV = "QCR_TMUX_SOCKET";
145
+ /** The companion's ssh access to the VM (the companion boundary: full ssh, so
146
+ * it may deploy). Exactly three host
147
+ * sources, each read-only and each only when the host has it: the ssh agent's
148
+ * socket, the ssh config and `known_hosts` — the shape the containerised TUI
149
+ * gets. Never `~/.ssh` whole, never a private key file, never
150
+ * `docker.sock`: the keys stay in the host's agent. */
151
+ export const SSH = {
152
+ agent: "/run/qcr-ssh-agent",
153
+ config: `${CONTAINER.home}/.ssh/config`,
154
+ knownHosts: `${CONTAINER.home}/.ssh/known_hosts`,
155
+ };
156
+ /** In `tmux-socket` mode this process sees none of the host's files, so the
157
+ * host wrapper checks the three ssh sources itself and names the present ones
158
+ * in these env vars (scripts/qcr-host.sh); the planner mounts what they name. */
159
+ export const HOST_SSH_ENV = {
160
+ agent: "QCR_HOST_SSH_AUTH_SOCK",
161
+ config: "QCR_HOST_SSH_CONFIG",
162
+ knownHosts: "QCR_HOST_SSH_KNOWN_HOSTS",
163
+ };
164
+ /** An absolute single-line path, else undefined. */
165
+ function hostPath(v) {
166
+ const t = (v ?? "").trim();
167
+ return t !== "" && isAbsolute(t) && !/[\n\r]/.test(t) ? t : undefined;
168
+ }
169
+ /** How long a containerised TUI waits for the wrapper-run companion's tmux
170
+ * server (the wrapper starts the container just before it starts the TUI). */
171
+ const WRAPPER_START_WAIT_MS = 20_000;
172
+ /** The vendored patched OpenViking pi extension, relative to the app root. */
173
+ const VENDOR_EXTENSION = "extensions/vendor/openviking-pi/index.ts";
174
+ // ---- the memory forward -----------------------------------------------------
175
+ /**
176
+ * Constants of the host-owned memory path. Memory never rides ssh:
177
+ * a systemd --user forward on the owner's PC listens on a docker bridge the
178
+ * companion is attached to and relays to OpenViking inside the VM container.
179
+ *
180
+ * The subnet is fixed so the gateway address is knowable before the network
181
+ * exists; `resolveMemoryWiring()` still asks docker for the real gateway and
182
+ * only falls back to this constant.
183
+ */
184
+ export const MEMORY = {
185
+ /** User-defined docker bridge shared by the forward and the companion. */
186
+ network: "qcr-memory",
187
+ subnet: "198.51.100.0/24",
188
+ gateway: "198.51.100.1",
189
+ /** Same port the server listens on in the VM container ([openviking] port). */
190
+ port: 1933,
191
+ /** The OpenViking user whose memory the companion owns — becomes the
192
+ * `X-OpenViking-User` header on every request it makes. */
193
+ user: "companion",
194
+ /** Peer id the patched extension reports (`X-OpenViking-Actor-Peer`);
195
+ * verified to override the path-derived default. Same
196
+ * identity as `user` for the companion — one surface, one actor. */
197
+ peerId: "companion",
198
+ /** The vendored patched OpenViking pi extension, inside the image. */
199
+ extension: `${CONTAINER.app}/${VENDOR_EXTENSION}`,
200
+ /** The companion namespace's retired URIs, relative to the state home.
201
+ * The TUI's memory store publishes them there on the host
202
+ * (no daemon sweeps the companion's tree); the extension's per-turn recall
203
+ * reads them from the mount (`QCR_OV_RETIRED_FILE`, set in `piEnvPrefix`)
204
+ * and sends them as `exclude_uris`, so a retired companion memory is never
205
+ * recalled — not only one whose own text still opens with the tag. */
206
+ retiredFile: "openviking/companion-retired-uris.json",
207
+ };
208
+ /** The companion's lens tools (`lens`, `lens_wave`; packages/pi-plugin/extensions/lens.ts), which
209
+ * stand where pi's example subagent extension used to. They run a lens pass as
210
+ * an SDK session of the companion's own pi process, over its own login and
211
+ * reusing pi's HTTP dispatcher, and read their agents from the agents dir
212
+ * under pi's agent dir, which the entrypoint links at the companion agent set
213
+ * (explore/evidence/study, docker/companion/agents in the image). A lens
214
+ * session is never given a lens tool (leaves, rule 9), and there is no cwd or
215
+ * scope knob to pin: the tool takes `{agent, task}` and nothing else. */
216
+ export const LENS = {
217
+ extension: `${CONTAINER.app}/extensions/cli/lens.ts`,
218
+ /** The companion's process-wide pass cap: 8, because a survey or scan fans
219
+ * out one case per unit. Rides `QCR_SUBAGENT_MAX_CONCURRENCY` on the pi
220
+ * command line (the name the retired subagents glue gave it, which
221
+ * the lens tool and extensions/setup.ts still read). Pinned by
222
+ * tests/tui-companion.test.ts. */
223
+ maxConcurrency: 8,
224
+ };
225
+ /** Mirror `source` (the host's live config, or undefined when there is none)
226
+ * into `dir/config.toml`. Pure fs, total — see `syncConfigMirror`. */
227
+ export function mirrorConfigFile(source, dir) {
228
+ const target = join(dir, CONFIG_FILE);
229
+ try {
230
+ if (source === undefined) {
231
+ if (!existsSync(target))
232
+ return "absent";
233
+ rmSync(target, { force: true });
234
+ return "removed";
235
+ }
236
+ const text = readFileSync(source, "utf8");
237
+ let current;
238
+ try {
239
+ current = readFileSync(target, "utf8");
240
+ }
241
+ catch {
242
+ current = undefined;
243
+ }
244
+ if (current === text)
245
+ return "unchanged";
246
+ writeAtomic(target, text);
247
+ return "copied";
248
+ }
249
+ catch (err) {
250
+ return { failed: err instanceof Error ? err.message : String(err) };
251
+ }
252
+ }
253
+ /** The three env names the keys file must define. Values are never read into a
254
+ * log line or an argv — docker reads the file itself via `--env-file`. */
255
+ export const KEY_NAMES = [
256
+ "OPENVIKING_API_KEY",
257
+ "OPENVIKING_COMPANION_KEY",
258
+ "OPENVIKING_REVIEWER_KEY",
259
+ ];
260
+ /**
261
+ * Non-secret memory env + the key file, for a container on `wiring`.
262
+ *
263
+ * The two USER keys ride `--env-file`, so the values never appear in this
264
+ * process's argv (and therefore never in `ps`). docker reads the file
265
+ * itself. Never switch these to `--env NAME=<value>`. The companion and the
266
+ * scan sandbox (src/scanbox.ts) both wire memory with it.
267
+ */
268
+ export function memoryEnvArgv(wiring) {
269
+ const argv = [];
270
+ if (wiring.endpoint !== "") {
271
+ argv.push(
272
+ // Read straight out of the vendored extension's credential resolver
273
+ // (shared/credentials.mjs): OPENVIKING_URL wins, OPENVIKING_BASE_URL is
274
+ // its documented alias, and the user/peer headers are what select the
275
+ // namespace a request addresses.
276
+ "--env", `OPENVIKING_URL=${wiring.endpoint}`, "--env", `OPENVIKING_BASE_URL=${wiring.endpoint}`, "--env", `OPENVIKING_USER=${MEMORY.user}`, "--env", `OPENVIKING_PEER_ID=${MEMORY.peerId}`,
277
+ // Ours: the in-container health watcher (docker/companion/memory-health.mjs).
278
+ "--env", `QCR_MEMORY_ENDPOINT=${wiring.endpoint}`, "--env", `QCR_MEMORY_ALERTS_DIR=${CONTAINER.alerts}`);
279
+ }
280
+ if (wiring.keysFile)
281
+ argv.push("--env-file", wiring.keysFile);
282
+ return argv;
283
+ }
284
+ export function memoryEndpoint(gateway, port = MEMORY.port) {
285
+ return `http://${gateway}:${port}`;
286
+ }
287
+ /** Host path of the OpenViking key file, decided by the install locator:
288
+ * `QCR_OPENVIKING_KEYS`, else the operator home
289
+ * (`$XDG_CONFIG_HOME/qcr-operator/<install>/`, outside every dir a container
290
+ * mounts). 0600, and handed to the container by `--env-file`, so no value
291
+ * reaches an argv. */
292
+ export function keysFilePath(env = process.env, home = homedir()) {
293
+ return locateKeysFile({ env: { ...env, HOME: home } }).path;
294
+ }
295
+ /** Which of KEY_NAMES the file defines — names only, values never leave here. */
296
+ export function keyNamesIn(text) {
297
+ const found = new Set();
298
+ for (const line of text.split("\n")) {
299
+ const m = /^\s*(?:export\s+)?([A-Z0-9_]+)\s*=/.exec(line);
300
+ if (m && KEY_NAMES.includes(m[1]))
301
+ found.add(m[1]);
302
+ }
303
+ return [...found];
304
+ }
305
+ /**
306
+ * Lines docker's `--env-file` parser would choke on.
307
+ *
308
+ * This matters more than it looks: `docker run --env-file` rejects a file with
309
+ * `export FOO=…`, `FOO = bar` or a bare word, and a rejected `docker run` is a
310
+ * companion that does not start. Memory must never be able to do that, so a
311
+ * file we cannot vouch for is left out of the argv (degraded, not fatal).
312
+ */
313
+ export function envFileProblems(text) {
314
+ const problems = [];
315
+ text.split("\n").forEach((line, i) => {
316
+ const trimmed = line.trim();
317
+ if (trimmed === "" || trimmed.startsWith("#"))
318
+ return;
319
+ if (!/^[A-Za-z_][A-Za-z0-9_]*=/.test(line))
320
+ problems.push(`line ${i + 1} is not NAME=value`);
321
+ });
322
+ return problems;
323
+ }
324
+ /** Gateway address out of `docker network inspect -f …` output, or "". */
325
+ export function parseGateway(stdout) {
326
+ const line = stdout.trim().split("\n").pop() ?? "";
327
+ return /^\d{1,3}(\.\d{1,3}){3}$/.test(line.trim()) ? line.trim() : "";
328
+ }
329
+ /** Host side of the companion's inject dir (`/workspace/inject/<session>`):
330
+ * the rollover's day-open and `qcr setup`'s request land here. */
331
+ export function companionInjectDir(cfg) {
332
+ return `${cfg.repo_dir}/inject/${cfg.session}`;
333
+ }
334
+ /** Set on the companion's pi: code that runs there (the setup belt, a
335
+ * `qcr scan` the companion runs) knows it is inside the companion container. */
336
+ export const IN_COMPANION_ENV = "QCR_IN_COMPANION";
337
+ /** The release image's mode word for the companion container
338
+ * (docker/entrypoint.sh): provision pi, start the tmux server, stay alive. */
339
+ export const COMPANION_MODE = "companion";
340
+ /** Why the companion image cannot be built from `ctx`, or null: the build
341
+ * compiles the app itself, so all it needs is the release Dockerfile. */
342
+ export function imageBuildProblem(ctx, exists) {
343
+ const dockerfile = join(ctx, "docker", "Dockerfile");
344
+ return exists(dockerfile)
345
+ ? null
346
+ : `the companion image is built from the release Dockerfile, and ${dockerfile} is not there — is ${ctx} a qcr checkout?`;
347
+ }
348
+ /** `docker build` of the companion image: the release image
349
+ * (docker/Dockerfile, which compiles the app in its own build stage) from the
350
+ * app root `ctx`, as `image`, stamped. Builds an image and nothing else: no
351
+ * container is touched. No uid is baked in: the container is started as the
352
+ * host user (`hostUser`). */
353
+ export function appImageBuildArgv(image, ctx, stamp) {
354
+ return [
355
+ "build",
356
+ "-t",
357
+ image,
358
+ "-f",
359
+ join(ctx, "docker", "Dockerfile"),
360
+ "--build-arg",
361
+ `BUILD_SHA=${stamp}`,
362
+ ctx,
363
+ ];
364
+ }
365
+ /** The paths of the release image's build context (docker/Dockerfile): the
366
+ * Dockerfile and its own ignore file, then every `!path` that ignore file
367
+ * admits. Only BuildKit reads a per-Dockerfile ignore file; the legacy
368
+ * builder (a Docker without buildx) reads the root .dockerignore, which is the
369
+ * scan sandbox's narrower allowlist, so the context is assembled from this
370
+ * list instead of relying on either — as bench/aacr/bench.sh does. */
371
+ export function releaseContextPaths(ignoreText) {
372
+ const admitted = ignoreText
373
+ .split("\n")
374
+ .map((l) => l.trim())
375
+ .filter((l) => l.startsWith("!"))
376
+ .map((l) => l.slice(1).replace(/^\/+/, ""))
377
+ .filter((l) => l !== "");
378
+ return ["docker/Dockerfile", "docker/Dockerfile.dockerignore", ...admitted];
379
+ }
380
+ /** Copy the release image's context out of `ctx` into a fresh temp dir
381
+ * (node_modules, .git and tsbuildinfo files left out); the caller removes it. */
382
+ export function assembleReleaseContext(ctx) {
383
+ const out = mkdtempSync(join(tmpdir(), "qcr-companion-ctx-"));
384
+ const ignore = readFileSync(join(ctx, "docker", "Dockerfile.dockerignore"), "utf8");
385
+ for (const rel of releaseContextPaths(ignore)) {
386
+ const from = join(ctx, rel);
387
+ if (!existsSync(from))
388
+ continue;
389
+ cpSync(from, join(out, rel), {
390
+ recursive: true,
391
+ filter: (src) => !/(^|\/)(node_modules|\.git)(\/|$)|\.tsbuildinfo$/.test(src),
392
+ });
393
+ }
394
+ return out;
395
+ }
396
+ /** `uid:gid` the companion container runs as: the invoking host user, so the
397
+ * host bind mounts it writes (/workspace, /tracker, /feed, /alerts) stay
398
+ * writable and its files stay yours. The release image bakes no user. */
399
+ export function hostUser() {
400
+ const uid = typeof process.getuid === "function" ? process.getuid() : 1000;
401
+ const gid = typeof process.getgid === "function" ? process.getgid() : 1000;
402
+ return `${uid}:${gid}`;
403
+ }
404
+ export class CompanionLifecycle {
405
+ cfg;
406
+ docker;
407
+ env;
408
+ ensureDirs;
409
+ fs;
410
+ stampOf;
411
+ mode;
412
+ assembleContext;
413
+ /** Resolved by ensureUp() before any `docker run`; the constants are the
414
+ * safe standing answer so runArgv() stays pure and callable in tests. */
415
+ wiring = {
416
+ network: MEMORY.network,
417
+ endpoint: memoryEndpoint(MEMORY.gateway),
418
+ notes: [],
419
+ };
420
+ constructor(cfg, docker, env = process.env,
421
+ /** Host dirs are created before a cold run so docker does not invent them
422
+ * root-owned. Injectable so tests touch no filesystem. */
423
+ ensureDirs = (paths) => {
424
+ for (const p of paths)
425
+ mkdirSync(p, { recursive: true });
426
+ },
427
+ /** Filesystem seam: the keys file and the vendored extension are looked up,
428
+ * never assumed. Injectable so argv assertions need no real files. */
429
+ fs = realCompanionFs,
430
+ /** The build stamp of the app under the build context (src/appstamp.ts).
431
+ * Injectable so the build argv is assertable without hashing a tree. */
432
+ stampOf = appStamp,
433
+ /** How the tmux server is reached — see `CompanionMode`. */
434
+ mode = "docker",
435
+ /** Builds the image's context from the app root (`assembleReleaseContext`);
436
+ * injectable so the build argv is assertable without copying a tree. */
437
+ assembleContext = assembleReleaseContext) {
438
+ this.cfg = cfg;
439
+ this.docker = docker;
440
+ this.env = env;
441
+ this.ensureDirs = ensureDirs;
442
+ this.fs = fs;
443
+ this.stampOf = stampOf;
444
+ this.mode = mode;
445
+ this.assembleContext = assembleContext;
446
+ }
447
+ /** How long `ensureUp` waits, in `tmux-socket` mode, for the wrapper-run
448
+ * companion's tmux server. A field so tests need not wait it out. */
449
+ wrapperStartWaitMs = WRAPPER_START_WAIT_MS;
450
+ /** The shared socket in `tmux-socket` mode, else undefined. */
451
+ get socketPath() {
452
+ return this.mode === "tmux-socket" ? tmuxSocketPath(this.cfg) : undefined;
453
+ }
454
+ /** `tmux <argv…>`, on the shared socket when there is one. */
455
+ tmuxArgv(...args) {
456
+ const socket = this.socketPath;
457
+ return socket === undefined ? ["tmux", ...args] : ["tmux", "-S", socket, ...args];
458
+ }
459
+ /** Start without memory: no network, no endpoint env (the laptop route, where
460
+ * the memory sidecar is opt-in). Pure; for the argv a host wrapper runs. */
461
+ withoutMemory() {
462
+ this.wiring = { network: "", endpoint: "", notes: [] };
463
+ return this;
464
+ }
465
+ /** The memory path this instance will wire (or has wired). */
466
+ get memory() {
467
+ return this.wiring;
468
+ }
469
+ // ---- pure argv ----------------------------------------------------------
470
+ /** `docker run` for a cold start: every mount the companion needs,
471
+ * the three ssh sources among them (`SSH`, rule 8). Memory still rides the
472
+ * docker network the host-owned forward listens on, never ssh. */
473
+ runArgv() {
474
+ const argv = [
475
+ "run",
476
+ "--detach",
477
+ "--name",
478
+ this.cfg.container_name,
479
+ "--hostname",
480
+ this.cfg.container_name,
481
+ ];
482
+ // Memory-less is a legitimate mode: no network, no endpoint env, container
483
+ // still starts. It must never be able to wedge the companion.
484
+ if (this.wiring.network !== "")
485
+ argv.push("--network", this.wiring.network);
486
+ // Network-roaming survival: see PUBLIC_DNS.
487
+ for (const ip of PUBLIC_DNS)
488
+ argv.push("--dns", ip);
489
+ // The host user (the image bakes none), and the checkout as the working
490
+ // directory: pi reads the repo's own context files from there, and the
491
+ // image's WORKDIR is its app root, whose reviewer stub is not the
492
+ // companion's to load.
493
+ argv.push("--user", hostUser(), "--workdir", CONTAINER.workspace);
494
+ // The wrapper-run companion is hardened like every other container
495
+ // the wrapper starts; nothing it does needs a capability.
496
+ if (this.socketPath !== undefined)
497
+ argv.push("--init", "--cap-drop", "ALL", "--security-opt", "no-new-privileges");
498
+ for (const m of this.mountSpec())
499
+ argv.push("--volume", `${m.source}:${m.dest}${m.mode ? `:${m.mode}` : ""}`);
500
+ argv.push(...this.memoryEnvArgv());
501
+ // pi's default model, set by the entrypoint on every start.
502
+ argv.push("--env", `QCR_PI_DEFAULT_MODEL=${this.cfg.model}`);
503
+ if (this.socketPath !== undefined)
504
+ argv.push("--env", `${TMUX_SOCKET_ENV}=${this.socketPath}`);
505
+ // The agent is reached by its mounted socket; no key is in the container.
506
+ if (this.sshSources().agent !== undefined)
507
+ argv.push("--env", `SSH_AUTH_SOCK=${SSH.agent}`);
508
+ // The release image, in its companion mode (docker/entrypoint.sh).
509
+ argv.push(this.cfg.image, COMPANION_MODE);
510
+ return argv;
511
+ }
512
+ /** Non-secret memory env + the key file, over this instance's wiring
513
+ * (the free `memoryEnvArgv`). */
514
+ memoryEnvArgv() {
515
+ return memoryEnvArgv(this.wiring);
516
+ }
517
+ /**
518
+ * The pi argv inside the companion's tmux session.
519
+ *
520
+ * Every extension, the theme and the system prompt are the IMAGE's
521
+ * (`CONTAINER.app`): the companion runs the code it was built with, and
522
+ * the checkout at /workspace is the repo it works on, not where its hands
523
+ * come from.
524
+ *
525
+ * VERIFY-ON-PIN, every flag (same list as sessions.ts buildPiCommand):
526
+ * `--model` is documented; `--session-dir` is documented for RPC mode only
527
+ * and assumed to apply to the interactive TUI; `--system-prompt` and
528
+ * `--name` are not documented at all — the fallback for `--system-prompt` is
529
+ * writing the body to <pi home>/.pi/SYSTEM.md, which is reachable on the
530
+ * pihome volume without an image rebuild.
531
+ *
532
+ * The session dir lives on the pihome volume so the conversation survives
533
+ * `docker stop` (that is what makes quit-then-restart a resume rather than a
534
+ * fresh start), and so daily rollover can archive "the newest .jsonl".
535
+ */
536
+ piArgv() {
537
+ const ext = `${CONTAINER.app}/extensions`;
538
+ return [
539
+ `${CONTAINER.app}/node_modules/.bin/pi`,
540
+ // --approve: pre-trust project-local .pi resources for this run. Without
541
+ // it a cwd carrying .pi/ hits pi's interactive "Trust project folder?"
542
+ // modal and the session stalls forever. Same flag sessions.ts passes
543
+ // (the reviewer never blocks on interactive pi input).
544
+ "--approve",
545
+ // The vendored patched OpenViking extension — the companion's memory
546
+ // hands. It rides the image, so it is always there; with no memory
547
+ // wiring it finds no endpoint and stays idle (the companion never wedges).
548
+ "--extension",
549
+ MEMORY.extension,
550
+ // Ambient /feed state is deliberately not an extension: one would turn
551
+ // every meaningful poll into a synthetic user message in the operator's
552
+ // active conversation. The model reads the bounded feed files on demand
553
+ // instead.
554
+ "--extension",
555
+ `${ext}/cli/muted-theme.ts`,
556
+ "--theme",
557
+ `${ext}/muted-theme.json`,
558
+ "--extension",
559
+ // Day-open channel: the daemon's inject bridge, unmodified. It
560
+ // drains its directory on session_start before it starts watching, so
561
+ // the rollover can queue the opening message before pi is spawned and
562
+ // the delivery is race-free. QCR_INJECT_DIR (below) points it at the
563
+ // companion's own dir. VERIFY-ON-PIN: `--extension <path>` again.
564
+ `${ext}/cli/inject-bridge.ts`,
565
+ "--extension",
566
+ // The read-only `tui_*` tools over what the TUI published to
567
+ // /feed (screen.txt + view.json). Reads only — it holds no credential and
568
+ // cannot fetch; see the header of extensions/tui-view.ts for why that is
569
+ // structural and not a promise. QCR_FEED_DIR (below) points it at the
570
+ // same dir the FeedWriter writes. VERIFY-ON-PIN: `--extension <path>`.
571
+ `${ext}/cli/tui-view.ts`,
572
+ "--extension",
573
+ // Our lens tools (`lens`, `lens_wave`) at cap 8 — see LENS and
574
+ // packages/pi-plugin/extensions/lens.ts. First-party and always in the image, so it loads
575
+ // unconditionally like inject-bridge; its lens sessions are SDK
576
+ // sessions of this pi, over pi's own dispatcher. The cap rides
577
+ // QCR_SUBAGENT_MAX_CONCURRENCY in piEnvPrefix (LENS.maxConcurrency).
578
+ LENS.extension,
579
+ "--extension",
580
+ // The companion's bright lines (frozen seeds, no-GitHub,
581
+ // no-discard in the shared checkout) as tool_call vetoes — see the
582
+ // header of extensions/companion-guard.ts. Companion only; the VM has
583
+ // the gate.
584
+ `${ext}/cli/companion-guard.ts`,
585
+ "--extension",
586
+ // The setup & scan tool belt — the deterministic SetupEngine
587
+ // (repo facts, the unit cut) as `setup_*` tools. Reads the reviewed
588
+ // checkout (`:ro`) and writes only the state home; see the header of
589
+ // extensions/setup.ts. Unconditional like every other extension:
590
+ // a missing checkout is a refusal from the tools, never a pi that
591
+ // won't start.
592
+ `${ext}/cli/setup.ts`,
593
+ "--extension",
594
+ // `!{cmd}` inline expansion in typed prompts — vendored
595
+ // verbatim from the pinned pi package's shipped examples (see its
596
+ // header; byte-identical at 0.84.1 and 0.84.4). QoL for the operator
597
+ // typing into the mirrored terminal.
598
+ `${ext}/inline-bash.ts`,
599
+ "--model",
600
+ this.cfg.model,
601
+ "--session-dir",
602
+ companionSessionDir(this.cfg.session),
603
+ "--system-prompt",
604
+ `${CONTAINER.app}/prompts/companion.md`,
605
+ "--name",
606
+ this.cfg.session,
607
+ ];
608
+ }
609
+ /** QCR_* routing vars. They ride the pi command line, never the client env:
610
+ * tmux panes inherit the tmux server's environment. */
611
+ piEnvPrefix() {
612
+ return [
613
+ "env",
614
+ `QCR_ROOT=${CONTAINER.workspace}`,
615
+ `QCR_SESSION=${this.cfg.session}`,
616
+ // inject-bridge resolves its session name from this one.
617
+ `QCR_SESSION_NAME=${this.cfg.session}`,
618
+ `QCR_INJECT_DIR=${CONTAINER.workspace}/inject/${this.cfg.session}`,
619
+ `QCR_FEED_DIR=${CONTAINER.feed}`,
620
+ `QCR_ALERTS_DIR=${CONTAINER.alerts}`,
621
+ `QCR_TRACKER_DIR=${CONTAINER.tracker}`,
622
+ // Where the install's homes and the token file sit in here.
623
+ // The locator (src/install.ts) honours QCR_CONFIG_HOME/QCR_STATE_HOME
624
+ // as whole-home overrides, so code run in the container resolves the
625
+ // mounted homes — profiles written by setup land on the host, not in
626
+ // the container user's ~/.config. QCR_CONFIG always names the mirror:
627
+ // when the host has no config the file is simply absent, which the
628
+ // reader reports — never a silent fall back to /workspace/config.toml.
629
+ `QCR_CONFIG_HOME=${CONTAINER.configHome}`,
630
+ `QCR_CONFIG=${CONTAINER.configFile}`,
631
+ `QCR_STATE_HOME=${CONTAINER.stateHome}`,
632
+ // Where the setup tools read the reviewed checkout.
633
+ `QCR_REVIEWED_REPO=${CONTAINER.reviewedRepo}`,
634
+ `QCR_GITHUB_READ_ENV=${CONTAINER.githubRead}/${TOKEN_FILE}`,
635
+ // Survey and scan fan out one case per unit — see LENS.
636
+ `QCR_SUBAGENT_MAX_CONCURRENCY=${LENS.maxConcurrency}`,
637
+ // The lens tool builds its SDK host over pi's own agent dir and refuses
638
+ // a relative or absent one (packages/pi-plugin/extensions/lens.ts); name the one pi uses.
639
+ `PI_CODING_AGENT_DIR=${CONTAINER.piHome}/agent`,
640
+ // The retired URIs the recall excludes — see MEMORY.retiredFile.
641
+ `QCR_OV_RETIRED_FILE=${CONTAINER.stateHome}/${MEMORY.retiredFile}`,
642
+ // What a `qcr scan` run here reads to default its sandbox to the host.
643
+ `${IN_COMPANION_ENV}=1`,
644
+ ];
645
+ }
646
+ /**
647
+ * Host side of the install-home mounts, from the install locator over this
648
+ * lifecycle's env (so a test's temp XDG homes are what it mounts):
649
+ * `<config home>/profiles` and `<config home>/portrait`, the live config
650
+ * file when one exists (the source of the mirror — never itself mounted),
651
+ * the mirror dir, the state home, and the token dir. Never the config home
652
+ * as a whole — see `CONTAINER.configHome` for why `secrets/` must stay out.
653
+ */
654
+ hostHomes() {
655
+ const env = this.installEnv();
656
+ const located = locateConfig({ env });
657
+ return {
658
+ profiles: join(configHome(env), PROFILES_DIR),
659
+ portrait: join(configHome(env), PORTRAIT_DIR),
660
+ configFile: this.fs.exists(located.path) ? located.path : undefined,
661
+ configMirror: companionConfigMirrorDir(env),
662
+ state: stateHome(env),
663
+ github: companionGithubDir(env),
664
+ };
665
+ }
666
+ /**
667
+ * Copy the host's live config into the mirror dir the companion mounts
668
+ * (see `CONTAINER.configLive`), or remove the mirror when the host has none
669
+ * — an absent file is the honest answer, a stale one is not. Atomic (tmp +
670
+ * rename inside the mounted dir) and a no-op when nothing changed. Total:
671
+ * a failure is an outcome, never a throw.
672
+ */
673
+ syncConfigMirror() {
674
+ const homes = this.hostHomes();
675
+ return mirrorConfigFile(homes.configFile, homes.configMirror);
676
+ }
677
+ installEnv() {
678
+ return { ...this.env, HOME: this.env.HOME || homedir() };
679
+ }
680
+ /** `docker exec … tmux new-session -d -s companion env QCR_… pi …`. */
681
+ spawnArgv() {
682
+ return [
683
+ "exec",
684
+ this.cfg.container_name,
685
+ ...this.tmuxArgv("new-session", "-d", "-s", this.cfg.session),
686
+ ...this.piEnvPrefix(),
687
+ ...this.piArgv(),
688
+ ];
689
+ }
690
+ /** Local attach — plain `docker exec -it`, no ssh (this box owns the
691
+ * container). Returned argv is everything after the `docker` binary. */
692
+ attachArgv() {
693
+ return [
694
+ "exec",
695
+ "-it",
696
+ this.cfg.container_name,
697
+ ...this.tmuxArgv("attach", "-t", this.cfg.session),
698
+ ];
699
+ }
700
+ /** Host side of `/workspace/inject/<session>` — where the rollover drops the
701
+ * day-open message (the repo is bind-mounted, so no docker exec needed). */
702
+ hostInjectDir() {
703
+ return companionInjectDir(this.cfg);
704
+ }
705
+ /**
706
+ * Is the reviewed repo checked out on this host?
707
+ *
708
+ * The mount is conditional on this, in `runArgv()` and in `expectedMounts()`,
709
+ * so the two never disagree: present ⇒ the container must carry it (absence
710
+ * is drift, and drift recreates); absent ⇒ nothing is expected and a stale
711
+ * mount is still drift. `docker run` would happily invent an empty host dir
712
+ * for a missing bind source, which is worse than no mount: the companion
713
+ * would read an empty tree and conclude the code says nothing.
714
+ */
715
+ reviewedRepoPresent() {
716
+ return this.cfg.reviewed_repo_dir !== "" && this.fs.exists(this.cfg.reviewed_repo_dir);
717
+ }
718
+ // ---- state --------------------------------------------------------------
719
+ async status() {
720
+ // One inspect answers both questions: is it up, and what was it created
721
+ // with. Line 1 is the running flag; every later line is `dest=source` for
722
+ // one mount. Keeping it to a single call means the drift check
723
+ // costs nothing on the start path.
724
+ const res = await this.docker([
725
+ "inspect",
726
+ "--format",
727
+ // `.Name` for a named volume, `.Source` for a bind. Docker reports a
728
+ // volume's Source as its on-disk _data path, which never equals the
729
+ // volume name the run argv asks for — comparing Source would make the
730
+ // drift check fire on every start and recreate the container each time.
731
+ "{{.State.Running}}\n{{range .Mounts}}{{.Destination}}={{if .Name}}{{.Name}}{{else}}{{.Source}}{{end}}\n{{end}}",
732
+ this.cfg.container_name,
733
+ ]);
734
+ if (!res.ok) {
735
+ const none = { exists: false, running: false, mounts: new Map() };
736
+ return /no such (object|container)/i.test(res.stderr)
737
+ ? none
738
+ : { ...none, unknown: errText(res) };
739
+ }
740
+ const [first = "", ...rest] = res.stdout.split("\n");
741
+ const mounts = new Map();
742
+ for (const line of rest) {
743
+ const eq = line.indexOf("=");
744
+ if (eq > 0)
745
+ mounts.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());
746
+ }
747
+ return { exists: true, running: first.trim() === "true", mounts };
748
+ }
749
+ /**
750
+ * The DNS servers the container was created with, or undefined when docker
751
+ * could not be asked (no opinion — never drift). Its own inspect call on
752
+ * purpose: on docker 29, referencing `.HostConfig` in the same template as
753
+ * the mounts' `{{if .Name}}` makes execution fall back to the raw JSON map,
754
+ * where a bind mount has no "Name" key and the whole inspect errors. Do not
755
+ * fold this back into status().
756
+ */
757
+ async createdDns() {
758
+ const res = await this.docker([
759
+ "inspect",
760
+ "--format",
761
+ "{{range .HostConfig.Dns}}{{.}}\n{{end}}",
762
+ this.cfg.container_name,
763
+ ]);
764
+ if (!res.ok)
765
+ return undefined;
766
+ return res.stdout
767
+ .split("\n")
768
+ .map((s) => s.trim())
769
+ .filter((s) => s !== "");
770
+ }
771
+ /** True when the companion tmux session is already live in the container. */
772
+ async piRunning() {
773
+ const res = await this.docker([
774
+ "exec",
775
+ this.cfg.container_name,
776
+ ...this.tmuxArgv("has-session", "-t", this.cfg.session),
777
+ ]);
778
+ return res.ok;
779
+ }
780
+ /** Is the companion's tmux server answering on its socket? `show-options`
781
+ * never starts a server, so asking cannot birth one in the wrong container. */
782
+ async serverUp() {
783
+ const res = await this.docker([
784
+ "exec",
785
+ this.cfg.container_name,
786
+ ...this.tmuxArgv("show-options", "-s", "exit-empty"),
787
+ ]);
788
+ return res.ok;
789
+ }
790
+ // ---- transitions --------------------------------------------------------
791
+ /** True when the companion image exists locally. */
792
+ async imageExists() {
793
+ const res = await this.docker(["image", "inspect", this.cfg.image]);
794
+ return res.ok;
795
+ }
796
+ /** Remove the container, running or not. Volumes (pihome: OAuth, the
797
+ * conversation) survive — this replaces the container, never its memory. */
798
+ async removeContainer() {
799
+ const st = await this.status();
800
+ if (st.unknown)
801
+ throw new Error(`container status unknown: ${st.unknown}`);
802
+ if (!st.exists)
803
+ return;
804
+ await this.forceRemove();
805
+ }
806
+ /** `docker rm -f` the container; a refusal throws with docker's reason. */
807
+ async forceRemove() {
808
+ const res = await this.docker(["rm", "-f", this.cfg.container_name]);
809
+ if (!res.ok)
810
+ throw new Error(`docker rm ${this.cfg.container_name} failed: ${errText(res)}`);
811
+ }
812
+ /** Throws when the image cannot be built (`imageBuildProblem`) — asked
813
+ * before anything is removed for a rebuild. */
814
+ checkBuildable() {
815
+ const problem = imageBuildProblem(this.cfg.build_context, (p) => this.fs.exists(p));
816
+ if (problem !== null)
817
+ throw new Error(problem);
818
+ }
819
+ /** Build the companion image (idempotent, cached) from the app root, with
820
+ * the release Dockerfile (docker/Dockerfile): the image compiles the app
821
+ * itself, so nothing needs building on the host first, and it is stamped
822
+ * with the checkout's build stamp. The companion's extensions come from this
823
+ * image, so a change to them lands only with a rebuild. */
824
+ async buildImage(onProgress) {
825
+ this.checkBuildable();
826
+ const ctx = this.cfg.build_context;
827
+ onProgress?.(`building ${this.cfg.image} image (first run — takes a few minutes)…`);
828
+ // The stamp is the checkout's; the build reads an assembled copy of the
829
+ // release context, so it works on the legacy builder too.
830
+ const stamp = this.stampOf(ctx);
831
+ const built = this.assembleContext(ctx);
832
+ let res;
833
+ try {
834
+ res = await this.docker(appImageBuildArgv(this.cfg.image, built, stamp), {
835
+ timeoutMs: 1_800_000,
836
+ });
837
+ }
838
+ finally {
839
+ if (built !== ctx)
840
+ rmSync(built, { recursive: true, force: true });
841
+ }
842
+ if (!res.ok)
843
+ throw new Error(`docker build ${this.cfg.image} failed: ${errText(res)}`);
844
+ onProgress?.(`${this.cfg.image} image built`);
845
+ }
846
+ // ---- memory wiring --------------------------------------------------------
847
+ /**
848
+ * Work out how this container will reach memory, without ever failing.
849
+ *
850
+ * The forward (scripts/companion-memory-forward.mjs, run by systemd on the
851
+ * host) owns the network; the lifecycle only joins it. When the network is
852
+ * absent it is created here anyway — idempotent, same fixed subnet — so the
853
+ * container's wiring is stable and memory starts working the moment the
854
+ * forward comes up, rather than needing a container recreate. If even that
855
+ * fails (a subnet clash, docker unhappy) the wiring degrades to memory-less
856
+ * and the companion starts regardless.
857
+ */
858
+ async resolveMemoryWiring(onProgress) {
859
+ const notes = [];
860
+ let gateway = await this.networkGateway();
861
+ if (gateway === "") {
862
+ await this.docker(["network", "create", "--subnet", MEMORY.subnet, MEMORY.network]);
863
+ gateway = await this.networkGateway();
864
+ if (gateway !== "")
865
+ notes.push(`created docker network ${MEMORY.network}`);
866
+ }
867
+ if (gateway === "") {
868
+ notes.push(`docker network ${MEMORY.network} unavailable — companion starts without memory`);
869
+ this.wiring = { network: "", endpoint: "", notes };
870
+ for (const n of notes)
871
+ onProgress?.(n);
872
+ return this.wiring;
873
+ }
874
+ const keysFile = keysFilePath(this.env);
875
+ const keysText = this.fs.read(keysFile);
876
+ let usableKeys;
877
+ if (keysText === undefined) {
878
+ notes.push(`no OpenViking keys at ${keysFile} — memory reads/writes will be refused (see docs/companion-memory-access.md)`);
879
+ // The old-place copy is never read; say so when it is the reason.
880
+ const stranded = `${legacySecretsDir({ ...this.env, HOME: homedir() })}/${KEYS_FILE}`;
881
+ if (this.fs.exists(stranded))
882
+ notes.push(`${stranded} is no longer read — \`qcr install migrate\` moves it to ${keysFile}`);
883
+ }
884
+ else {
885
+ const names = keyNamesIn(keysText);
886
+ const missing = KEY_NAMES.filter((n) => !names.includes(n));
887
+ if (missing.length > 0)
888
+ notes.push(`${keysFile} is missing ${missing.join(", ")}`);
889
+ const mode = this.fs.mode(keysFile);
890
+ if (mode !== undefined && (mode & 0o077) !== 0)
891
+ notes.push(`${keysFile} is group/world readable — chmod 600 it`);
892
+ const bad = envFileProblems(keysText);
893
+ if (bad.length > 0) {
894
+ // Passing it anyway would make `docker run` refuse and the companion
895
+ // never start — memory is never allowed to cost that.
896
+ notes.push(`${keysFile} is not a docker env-file (${bad[0]}) — starting without memory keys`);
897
+ }
898
+ else {
899
+ usableKeys = keysFile;
900
+ }
901
+ }
902
+ this.wiring = {
903
+ network: MEMORY.network,
904
+ endpoint: memoryEndpoint(gateway),
905
+ keysFile: usableKeys,
906
+ notes,
907
+ };
908
+ for (const n of notes)
909
+ onProgress?.(n);
910
+ return this.wiring;
911
+ }
912
+ async networkGateway() {
913
+ const res = await this.docker([
914
+ "network",
915
+ "inspect",
916
+ "--format",
917
+ "{{range .IPAM.Config}}{{.Gateway}}{{end}}",
918
+ MEMORY.network,
919
+ ]);
920
+ return res.ok ? parseGateway(res.stdout) : "";
921
+ }
922
+ /** Attach an already-running container to the memory network. Idempotent —
923
+ * "already exists" is the normal answer and never an error here. */
924
+ async attachToMemoryNetwork() {
925
+ if (this.wiring.network === "")
926
+ return false;
927
+ const res = await this.docker([
928
+ "network",
929
+ "connect",
930
+ this.wiring.network,
931
+ this.cfg.container_name,
932
+ ]);
933
+ return res.ok;
934
+ }
935
+ /**
936
+ * Bind mounts are frozen at creation, so a container that predates a mount
937
+ * change keeps the old set forever — `docker start` on it silently resurrects
938
+ * the old boundary. That is not cosmetic: a stale container can carry a
939
+ * mount the boundary does not grant (an `~/.ssh` whole, a key file, a dead
940
+ * agent socket) — the companion boundary means its mounts to be structural.
941
+ *
942
+ * So: compare what the container has against what `runArgv()` asks for, and
943
+ * report the drift. Named volumes are compared by name, binds by host path.
944
+ * Extra mounts count as drift (an `~/.ssh` whole is that case); an unreadable inspect
945
+ * reports nothing.
946
+ */
947
+ mountDrift(actual) {
948
+ if (actual.size === 0)
949
+ return [];
950
+ const drift = [];
951
+ // An agent socket moves with every login and differs between shells: a
952
+ // container on another agent socket that is still live keeps its ssh, so
953
+ // a TUI started from another shell never recreates a running companion.
954
+ // A dead one is drift like any other.
955
+ const liveAgent = (dest, have) => dest === SSH.agent && have !== undefined && this.fs.exists(have);
956
+ for (const [dest, source] of this.expectedMounts()) {
957
+ const have = actual.get(dest);
958
+ if (have === undefined)
959
+ drift.push(`missing ${dest}`);
960
+ else if (have !== source && !liveAgent(dest, have))
961
+ drift.push(`${dest} points at ${have}, expected ${source}`);
962
+ }
963
+ for (const [dest, have] of actual) {
964
+ if (!this.expectedMounts().has(dest) && !liveAgent(dest, have))
965
+ drift.push(`stale ${dest}`);
966
+ }
967
+ return drift;
968
+ }
969
+ /**
970
+ * DNS is frozen at creation exactly like the mounts, so a container created
971
+ * before PUBLIC_DNS was pinned keeps forwarding lookups to a
972
+ * network-specific upstream forever (see PUBLIC_DNS).
973
+ * Same remedy as mount drift: recreate. Consulted only after status()
974
+ * yielded a mount set (see ensureUp) so an unreadable inspect stays
975
+ * "no opinion", never a recreate.
976
+ */
977
+ dnsDrift(actual) {
978
+ return actual.join(",") === PUBLIC_DNS.join(",")
979
+ ? []
980
+ : [`dns is [${actual.join(", ")}], expected [${PUBLIC_DNS.join(", ")}]`];
981
+ }
982
+ /**
983
+ * Every mount the container is created with, in `runArgv()` order — the
984
+ * one list `runArgv()` turns into `--volume` flags and `expectedMounts()`
985
+ * (the drift check) reads, so the two can never disagree.
986
+ */
987
+ mountSpec() {
988
+ const spec = [
989
+ { dest: CONTAINER.piHome, source: this.cfg.pihome_volume },
990
+ { dest: CONTAINER.workspace, source: this.cfg.repo_dir, mode: "rw" },
991
+ ];
992
+ // The tmux socket volume, shared with the containerised TUI. The only
993
+ // thing in it is the socket; it is no ssh agent and no docker.sock.
994
+ if (this.socketPath !== undefined)
995
+ spec.push({ dest: CONTAINER.tmuxDir, source: tmuxVolume(this.cfg), mode: "rw" });
996
+ // The tracker and its protocol docs are optional: mounted only when the
997
+ // operator configured them ([companion] tracker_dir / tracker_docs_dir).
998
+ if (this.cfg.tracker_dir !== "")
999
+ spec.push({ dest: CONTAINER.tracker, source: this.cfg.tracker_dir, mode: "rw" });
1000
+ if (this.cfg.tracker_docs_dir !== "")
1001
+ spec.push({ dest: CONTAINER.trackerDocs, source: this.cfg.tracker_docs_dir, mode: "ro" });
1002
+ // The reviewed repo, read-only and only when it is really there — see
1003
+ // reviewedRepoPresent().
1004
+ if (this.reviewedRepoPresent())
1005
+ spec.push({ dest: CONTAINER.reviewedRepo, source: this.cfg.reviewed_repo_dir, mode: "ro" });
1006
+ // The evidence store. Created by the TUI before the container starts
1007
+ // (`ensureDirs`), so unlike the reviewed repo it is never absent and the
1008
+ // mount is unconditional — an empty store is a companion that has seen no
1009
+ // rounds yet, which is honest; a missing one would have docker invent a
1010
+ // root-owned dir the copier could not then write.
1011
+ spec.push({ dest: CONTAINER.evidence, source: this.cfg.evidence_dir, mode: "ro" }, { dest: CONTAINER.feed, source: this.cfg.feed_dir, mode: "rw" }, { dest: CONTAINER.alerts, source: this.cfg.alerts_dir, mode: "rw" });
1012
+ // The install's homes (profiles/, portrait/, state), the config mirror
1013
+ // and the read-only GitHub token. All directories, created before a cold
1014
+ // run (`ensureDirs`), so unconditional like the evidence store.
1015
+ const homes = this.hostHomes();
1016
+ spec.push({ dest: CONTAINER.profiles, source: homes.profiles, mode: "rw" }, { dest: CONTAINER.portrait, source: homes.portrait, mode: "rw" }, { dest: CONTAINER.configLive, source: homes.configMirror, mode: "ro" }, { dest: CONTAINER.stateHome, source: homes.state, mode: "rw" }, { dest: CONTAINER.githubRead, source: homes.github, mode: "ro" });
1017
+ // The locator also searches beside an explicit config file. Keep that
1018
+ // fallback beside the mirrored config without mounting its secret home.
1019
+ const adjacent = homeProfileDirs(this.installEnv())[1];
1020
+ if (adjacent && adjacent !== homes.profiles && this.fs.exists(adjacent))
1021
+ spec.push({ dest: `${CONTAINER.configLive}/profiles`, source: adjacent, mode: "rw" });
1022
+ // The VM access (rule 8): exactly these three, read-only, present ones
1023
+ // only. An agent socket moves with every login, so a new path is mount
1024
+ // drift and the container is recreated onto it, unless the previous
1025
+ // socket still exists (mountDrift keeps the live agent's mount).
1026
+ const ssh = this.sshSources();
1027
+ if (ssh.agent !== undefined)
1028
+ spec.push({ dest: SSH.agent, source: ssh.agent, mode: "ro" });
1029
+ if (ssh.config !== undefined)
1030
+ spec.push({ dest: SSH.config, source: ssh.config, mode: "ro" });
1031
+ if (ssh.knownHosts !== undefined)
1032
+ spec.push({ dest: SSH.knownHosts, source: ssh.knownHosts, mode: "ro" });
1033
+ return spec;
1034
+ }
1035
+ /**
1036
+ * The host's ssh sources the companion is given (`SSH`): the agent socket
1037
+ * `SSH_AUTH_SOCK` names, and `config` and `known_hosts` under the
1038
+ * configured `ssh_dir` — each only when it exists. In `tmux-socket` mode
1039
+ * this process cannot see the host, so the wrapper names them
1040
+ * (`HOST_SSH_ENV`).
1041
+ */
1042
+ sshSources() {
1043
+ const out = {};
1044
+ const set = (key, path) => {
1045
+ if (path !== undefined)
1046
+ out[key] = path;
1047
+ };
1048
+ if (this.socketPath !== undefined) {
1049
+ set("agent", hostPath(this.env[HOST_SSH_ENV.agent]));
1050
+ set("config", hostPath(this.env[HOST_SSH_ENV.config]));
1051
+ set("knownHosts", hostPath(this.env[HOST_SSH_ENV.knownHosts]));
1052
+ return out;
1053
+ }
1054
+ const present = (path) => path !== undefined && this.fs.exists(path) ? path : undefined;
1055
+ const dir = hostPath(this.cfg.ssh_dir);
1056
+ set("agent", present(hostPath(this.env["SSH_AUTH_SOCK"])));
1057
+ set("config", present(dir === undefined ? undefined : join(dir, "config")));
1058
+ set("knownHosts", present(dir === undefined ? undefined : join(dir, "known_hosts")));
1059
+ return out;
1060
+ }
1061
+ /** `dest -> source` for every mount `runArgv()` declares. */
1062
+ expectedMounts() {
1063
+ return new Map(this.mountSpec().map((m) => [m.dest, m.source]));
1064
+ }
1065
+ /** Idempotent: running → adopt; stopped → start; absent → run. */
1066
+ async ensureUp(onProgress) {
1067
+ if (this.socketPath !== undefined)
1068
+ return this.adoptWrapperRun(onProgress);
1069
+ await this.resolveMemoryWiring(onProgress);
1070
+ // Say which of the two worlds this start is in, either way. A missing
1071
+ // checkout is a normal, non-fatal state — but a companion silently reading
1072
+ // no reviewed source at all must never be left implicit.
1073
+ onProgress?.(this.reviewedRepoPresent()
1074
+ ? `reviewed repo ${this.cfg.reviewed_repo_dir} → ${CONTAINER.reviewedRepo} (read-only)`
1075
+ : `no reviewed repo at ${this.cfg.reviewed_repo_dir} — companion starts without it ([companion] reviewed_repo_dir)`);
1076
+ let st = await this.status();
1077
+ let recreating = false;
1078
+ // Mount drift is repaired by recreation and nothing else. Do it before the
1079
+ // adopt/start branches, so a stale container never survives a TUI start.
1080
+ // The conversation is on the pihome volume and survives the rm.
1081
+ if (st.exists) {
1082
+ // DNS drift is only worth asking about when the mounts came back clean
1083
+ // (drifted mounts already force the recreate) and status() actually
1084
+ // yielded a mount set: a real companion container always reports its
1085
+ // mounts, so an empty set means the answer was unreadable — no opinion,
1086
+ // never a recreate. An unreadable createdDns() is no opinion too.
1087
+ const mountIssues = this.mountDrift(st.mounts);
1088
+ const dns = mountIssues.length === 0 && st.mounts.size > 0 ? await this.createdDns() : undefined;
1089
+ const drift = [...mountIssues, ...(dns === undefined ? [] : this.dnsDrift(dns))];
1090
+ if (drift.length > 0) {
1091
+ onProgress?.(`companion mounts drifted (${drift.join("; ")}) — recreating`);
1092
+ await this.forceRemove();
1093
+ st = { exists: false, running: false, mounts: new Map() };
1094
+ recreating = true;
1095
+ }
1096
+ }
1097
+ if (st.running) {
1098
+ // A container created before the memory forward existed is still on the
1099
+ // old network; connecting is live and needs no recreate.
1100
+ await this.attachToMemoryNetwork();
1101
+ return "adopted";
1102
+ }
1103
+ if (st.exists) {
1104
+ const started = await this.docker(["start", this.cfg.container_name]);
1105
+ if (started.ok) {
1106
+ // Same repair as adopting a running container: one created before
1107
+ // the memory network existed joins it now.
1108
+ await this.attachToMemoryNetwork();
1109
+ return "started";
1110
+ }
1111
+ // A stopped container can be permanently unstartable: bind-mount
1112
+ // sources are recorded at creation, and a source that no longer exists
1113
+ // makes `docker start` fail forever. Remove the husk and recreate with
1114
+ // the current environment; named volumes (pihome auth, conversation)
1115
+ // survive the rm.
1116
+ onProgress?.(`companion container unstartable (${firstLine(errText(started))}) — recreating`);
1117
+ await this.forceRemove();
1118
+ }
1119
+ // Self-healing first run: build the image rather than failing on its absence.
1120
+ if (!(await this.imageExists()))
1121
+ await this.buildImage(onProgress);
1122
+ const homes = this.hostHomes();
1123
+ this.ensureDirs([
1124
+ this.cfg.feed_dir,
1125
+ this.cfg.alerts_dir,
1126
+ this.cfg.evidence_dir,
1127
+ homes.profiles,
1128
+ homes.portrait,
1129
+ homes.configMirror,
1130
+ homes.state,
1131
+ homes.github,
1132
+ ]);
1133
+ const created = await this.docker(this.runArgv(), { timeoutMs: 120_000 });
1134
+ if (!created.ok)
1135
+ throw new Error(`docker run ${this.cfg.image} failed: ${errText(created)}`);
1136
+ return st.exists || recreating ? "recreated" : "created";
1137
+ }
1138
+ /**
1139
+ * `tmux-socket` mode: the host wrapper started (or adopted) the companion
1140
+ * container before this process existed, and this one has no docker, so the
1141
+ * start is only a wait for the tmux server to answer on the shared socket.
1142
+ * A server that never answers is the wrapper's failure to report, not ours
1143
+ * to repair.
1144
+ */
1145
+ async adoptWrapperRun(onProgress) {
1146
+ onProgress?.(`companion container run by the qcr wrapper — tmux socket ${this.socketPath}`);
1147
+ if (await this.waitRunning(this.wrapperStartWaitMs, 250))
1148
+ return "adopted";
1149
+ throw new Error(`the companion's tmux server is not answering on ${this.socketPath} — the qcr wrapper starts the companion container before the TUI (is ${this.cfg.container_name} running?)`);
1150
+ }
1151
+ /**
1152
+ * Wait until the container is actually running.
1153
+ *
1154
+ * `docker run --detach` returns once the container is created, and a
1155
+ * `docker exec` an instant later can still lose the race with "container is
1156
+ * not running", typically right after a recreate. Bounded and total: if
1157
+ * it never comes up, the caller's own error is the better message.
1158
+ */
1159
+ async waitRunning(timeoutMs = 5_000, stepMs = 100) {
1160
+ const deadline = Date.now() + timeoutMs;
1161
+ for (;;) {
1162
+ if (this.socketPath !== undefined) {
1163
+ // No docker to ask: the server answering is "running".
1164
+ if (await this.serverUp())
1165
+ return true;
1166
+ if (Date.now() >= deadline)
1167
+ return false;
1168
+ await new Promise((r) => setTimeout(r, stepMs));
1169
+ continue;
1170
+ }
1171
+ const st = await this.status();
1172
+ if (st.running)
1173
+ return true;
1174
+ // Gone rather than starting: waiting cannot help, and the caller's own
1175
+ // failure is a better message than a five-second stall.
1176
+ if (!st.exists)
1177
+ return false;
1178
+ if (Date.now() >= deadline)
1179
+ return false;
1180
+ await new Promise((r) => setTimeout(r, stepMs));
1181
+ }
1182
+ }
1183
+ /** Idempotent: a live companion session is left strictly alone (a second
1184
+ * TUI attaches to it). Returns true when this call birthed the session. */
1185
+ async spawnPi() {
1186
+ if (await this.piRunning())
1187
+ return false;
1188
+ let res = await this.docker(this.spawnArgv());
1189
+ // `docker run --detach` returns at create, not at running, so an exec an
1190
+ // instant after a recreate can lose the race with "container is not
1191
+ // running", which would report the companion unavailable for the whole
1192
+ // session. Wait for it once and try again;
1193
+ // anything else is a real failure and is raised as one.
1194
+ if (!res.ok && /is not running|tmux server is not up/i.test(errText(res))) {
1195
+ await this.waitRunning();
1196
+ res = await this.docker(this.spawnArgv());
1197
+ }
1198
+ // Another TUI may have created the session between the check above and
1199
+ // this spawn (tmux refuses a duplicate): that session is the one to adopt.
1200
+ if (!res.ok && (await this.piRunning()))
1201
+ return false;
1202
+ if (!res.ok)
1203
+ throw new Error(`spawning the companion pi session failed: ${errText(res)}`);
1204
+ return true;
1205
+ }
1206
+ /** Graceful `docker stop` — SIGTERM to the entrypoint; volumes persist.
1207
+ * A container docker could not be asked about is not a stopped one: that
1208
+ * throws rather than return as if the stop had happened. */
1209
+ async gracefulStop() {
1210
+ // The wrapper that ran the container stops it, after the TUI container
1211
+ // has exited; this process cannot, and must not pretend to.
1212
+ if (this.socketPath !== undefined)
1213
+ return;
1214
+ const st = await this.status();
1215
+ if (st.unknown !== undefined)
1216
+ throw new Error(`cannot tell whether ${this.cfg.container_name} is running (docker inspect failed: ${st.unknown}) — nothing was stopped`);
1217
+ if (!st.running)
1218
+ return;
1219
+ const res = await this.docker(["stop", this.cfg.container_name], { timeoutMs: 60_000 });
1220
+ if (!res.ok)
1221
+ throw new Error(`docker stop ${this.cfg.container_name} failed: ${errText(res)}`);
1222
+ }
1223
+ }
1224
+ /** One-line cause from a failed docker call: the last line of stderr, else
1225
+ * of stdout, else the exit status. */
1226
+ export function dockerFailureLine(r) {
1227
+ return (r.stderr.trim() || r.stdout.trim() || `exit ${r.code}`).split("\n").slice(-1)[0];
1228
+ }
1229
+ const errText = dockerFailureLine;
1230
+ /** The companion's pi session dir inside the container — where `piArgv` points
1231
+ * pi and where the day rollover looks for the conversation. */
1232
+ export function companionSessionDir(session) {
1233
+ return `${CONTAINER.piHome}/agent/sessions/${session}`;
1234
+ }
1235
+ function firstLine(s) {
1236
+ return s.split("\n", 1)[0] ?? "";
1237
+ }
1238
+ export const realDockerRunner = (argv, opts) => runCommand("docker", argv, opts);
1239
+ /** Run `bin argv…` and collect its output. A spawn failure (no such binary,
1240
+ * no permission) or the timeout is said in `stderr`, so the caller's
1241
+ * one-line cause names it instead of "exit null". */
1242
+ export function runCommand(bin, argv, opts) {
1243
+ return new Promise((resolvePromise) => {
1244
+ const child = spawn(bin, argv, { stdio: ["ignore", "pipe", "pipe"] });
1245
+ let stdout = "";
1246
+ let stderr = "";
1247
+ let settled = false;
1248
+ const settle = (ok, code) => {
1249
+ if (settled)
1250
+ return;
1251
+ settled = true;
1252
+ clearTimeout(timer);
1253
+ resolvePromise({ ok, stdout, stderr, code });
1254
+ };
1255
+ const timeoutMs = opts?.timeoutMs ?? 20_000;
1256
+ const timer = setTimeout(() => {
1257
+ child.kill("SIGKILL");
1258
+ stderr += `${stderr === "" || stderr.endsWith("\n") ? "" : "\n"}${bin} timed out after ${timeoutMs} ms`;
1259
+ settle(false, null);
1260
+ }, timeoutMs);
1261
+ // Decoded by the stream, never per chunk: a multi-byte character split
1262
+ // across two reads (a `─` in a large capture, any non-ASCII in a long
1263
+ // copied buffer) otherwise becomes two U+FFFD.
1264
+ child.stdout.setEncoding("utf8");
1265
+ child.stderr.setEncoding("utf8");
1266
+ child.stdout.on("data", (d) => (stdout += d));
1267
+ child.stderr.on("data", (d) => (stderr += d));
1268
+ child.on("error", (err) => {
1269
+ stderr += `${stderr === "" || stderr.endsWith("\n") ? "" : "\n"}${bin}: ${err.message}`;
1270
+ settle(false, null);
1271
+ });
1272
+ child.on("close", (code) => settle(code === 0, code));
1273
+ });
1274
+ }
1275
+ //# sourceMappingURL=companion.js.map