@quickchatai/quickreview 0.0.0-stage → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (1479) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +401 -2
  3. package/THIRD-PARTY-NOTICES.md +134 -0
  4. package/agents/pass-abstraction.md +64 -0
  5. package/agents/pass-ask-human.md +134 -0
  6. package/agents/pass-backcompat.md +67 -0
  7. package/agents/pass-baseline.md +61 -0
  8. package/agents/pass-behaviour-delta.md +64 -0
  9. package/agents/pass-bounds.md +36 -0
  10. package/agents/pass-claims.md +31 -0
  11. package/agents/pass-concurrency.md +168 -0
  12. package/agents/pass-consistency.md +57 -0
  13. package/agents/pass-contracts.md +30 -0
  14. package/agents/pass-data-safety.md +54 -0
  15. package/agents/pass-deferred.md +35 -0
  16. package/agents/pass-delivery.md +37 -0
  17. package/agents/pass-duplication.md +60 -0
  18. package/agents/pass-intent.md +63 -0
  19. package/agents/pass-lint-conformance.md +46 -0
  20. package/agents/pass-maintainability.md +67 -0
  21. package/agents/pass-placement.md +68 -0
  22. package/agents/pass-prod-risk.md +24 -0
  23. package/agents/pass-rollout.md +32 -0
  24. package/agents/pass-roundtrip.md +46 -0
  25. package/agents/pass-security.md +31 -0
  26. package/agents/pass-simplicity.md +68 -0
  27. package/agents/pass-statelessness.md +27 -0
  28. package/agents/pass-style.md +37 -0
  29. package/agents/pass-sweep.md +57 -0
  30. package/agents/pass-tests.md +58 -0
  31. package/agents/pass-ui.md +23 -0
  32. package/agents/pass-validate.md +50 -0
  33. package/agents/pass-widened.md +46 -0
  34. package/agents/scan/pass-scan.md +86 -0
  35. package/bin/qcr.js +11 -0
  36. package/config.example.toml +1099 -0
  37. package/deploy/ansible/README.md +356 -0
  38. package/deploy/ansible/group_vars/all.yml +212 -0
  39. package/deploy/ansible/inventory.example.ini +25 -0
  40. package/deploy/ansible/playbook.yml +360 -0
  41. package/deploy/collect-secrets.sh +289 -0
  42. package/deploy/collect-slack-secrets.mjs +1190 -0
  43. package/deploy/github-app-manifest.json +31 -0
  44. package/deploy/manifest.schema.json +75 -0
  45. package/deploy/qcr-update.sh +803 -0
  46. package/deploy/reviewer/AGENTS.override.md +12 -0
  47. package/dist/extensions/cli/bash-guard-ambient.js +42 -0
  48. package/dist/extensions/cli/bash-guard-ambient.js.map +1 -0
  49. package/dist/extensions/cli/bash-guard.js +5 -0
  50. package/dist/extensions/cli/bash-guard.js.map +1 -0
  51. package/dist/extensions/cli/companion-guard.js +5 -0
  52. package/dist/extensions/cli/companion-guard.js.map +1 -0
  53. package/dist/extensions/cli/debug-round.js +8 -0
  54. package/dist/extensions/cli/debug-round.js.map +1 -0
  55. package/dist/extensions/cli/inject-bridge.js +5 -0
  56. package/dist/extensions/cli/inject-bridge.js.map +1 -0
  57. package/dist/extensions/cli/ledger-pin.js +5 -0
  58. package/dist/extensions/cli/ledger-pin.js.map +1 -0
  59. package/dist/extensions/cli/lens.js +13 -0
  60. package/dist/extensions/cli/lens.js.map +1 -0
  61. package/dist/extensions/cli/login-route.js +5 -0
  62. package/dist/extensions/cli/login-route.js.map +1 -0
  63. package/dist/extensions/cli/muted-theme.js +5 -0
  64. package/dist/extensions/cli/muted-theme.js.map +1 -0
  65. package/dist/extensions/cli/onboarding.js +5 -0
  66. package/dist/extensions/cli/onboarding.js.map +1 -0
  67. package/dist/extensions/cli/setup.js +5 -0
  68. package/dist/extensions/cli/setup.js.map +1 -0
  69. package/dist/extensions/cli/tui-view.js +5 -0
  70. package/dist/extensions/cli/tui-view.js.map +1 -0
  71. package/dist/extensions/companion-guard.js +361 -0
  72. package/dist/extensions/companion-guard.js.map +1 -0
  73. package/dist/extensions/debug-round.js +204 -0
  74. package/dist/extensions/debug-round.js.map +1 -0
  75. package/dist/extensions/inject-bridge.js +603 -0
  76. package/dist/extensions/inject-bridge.js.map +1 -0
  77. package/dist/extensions/login-route.js +180 -0
  78. package/dist/extensions/login-route.js.map +1 -0
  79. package/dist/extensions/muted-theme.js +12 -0
  80. package/dist/extensions/muted-theme.js.map +1 -0
  81. package/dist/extensions/onboarding.js +198 -0
  82. package/dist/extensions/onboarding.js.map +1 -0
  83. package/dist/extensions/setup-replay.js +269 -0
  84. package/dist/extensions/setup-replay.js.map +1 -0
  85. package/dist/extensions/setup-scan.js +998 -0
  86. package/dist/extensions/setup-scan.js.map +1 -0
  87. package/dist/extensions/setup-triage.js +175 -0
  88. package/dist/extensions/setup-triage.js.map +1 -0
  89. package/dist/extensions/setup.js +1160 -0
  90. package/dist/extensions/setup.js.map +1 -0
  91. package/dist/extensions/tui-view.js +389 -0
  92. package/dist/extensions/tui-view.js.map +1 -0
  93. package/dist/extensions/vendor/openviking-pi/client.js +376 -0
  94. package/dist/extensions/vendor/openviking-pi/client.js.map +1 -0
  95. package/dist/extensions/vendor/openviking-pi/config.js +189 -0
  96. package/dist/extensions/vendor/openviking-pi/config.js.map +1 -0
  97. package/dist/extensions/vendor/openviking-pi/index.js +421 -0
  98. package/dist/extensions/vendor/openviking-pi/index.js.map +1 -0
  99. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +86 -0
  100. package/dist/extensions/vendor/openviking-pi/lib/capture-adapter.mjs.map +1 -0
  101. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs +408 -0
  102. package/dist/extensions/vendor/openviking-pi/lib/takeover-core.mjs.map +1 -0
  103. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +41 -0
  104. package/dist/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs.map +1 -0
  105. package/dist/extensions/vendor/openviking-pi/qcr-hint.js +290 -0
  106. package/dist/extensions/vendor/openviking-pi/qcr-hint.js.map +1 -0
  107. package/dist/extensions/vendor/openviking-pi/qcr-honest.js +148 -0
  108. package/dist/extensions/vendor/openviking-pi/qcr-honest.js.map +1 -0
  109. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js +68 -0
  110. package/dist/extensions/vendor/openviking-pi/qcr-hooks.js.map +1 -0
  111. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js +77 -0
  112. package/dist/extensions/vendor/openviking-pi/qcr-leaf.js.map +1 -0
  113. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js +216 -0
  114. package/dist/extensions/vendor/openviking-pi/qcr-lesson.js.map +1 -0
  115. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js +154 -0
  116. package/dist/extensions/vendor/openviking-pi/qcr-namespaces.js.map +1 -0
  117. package/dist/extensions/vendor/openviking-pi/qcr-recall.js +254 -0
  118. package/dist/extensions/vendor/openviking-pi/qcr-recall.js.map +1 -0
  119. package/dist/extensions/vendor/openviking-pi/qcr-shim.js +94 -0
  120. package/dist/extensions/vendor/openviking-pi/qcr-shim.js.map +1 -0
  121. package/dist/extensions/vendor/openviking-pi/qcr-write.js +333 -0
  122. package/dist/extensions/vendor/openviking-pi/qcr-write.js.map +1 -0
  123. package/dist/extensions/vendor/openviking-pi/recall.js +108 -0
  124. package/dist/extensions/vendor/openviking-pi/recall.js.map +1 -0
  125. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs +529 -0
  126. package/dist/extensions/vendor/openviking-pi/shared/capture-utils.mjs.map +1 -0
  127. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs +205 -0
  128. package/dist/extensions/vendor/openviking-pi/shared/credentials.mjs.map +1 -0
  129. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs +442 -0
  130. package/dist/extensions/vendor/openviking-pi/shared/pending-queue.mjs.map +1 -0
  131. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs +286 -0
  132. package/dist/extensions/vendor/openviking-pi/shared/profile-inject.mjs.map +1 -0
  133. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +189 -0
  134. package/dist/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs.map +1 -0
  135. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs +580 -0
  136. package/dist/extensions/vendor/openviking-pi/shared/recall-core.mjs.map +1 -0
  137. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs +11 -0
  138. package/dist/extensions/vendor/openviking-pi/shared/retryable.mjs.map +1 -0
  139. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs +63 -0
  140. package/dist/extensions/vendor/openviking-pi/shared/session-model.mjs.map +1 -0
  141. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs +58 -0
  142. package/dist/extensions/vendor/openviking-pi/shared/uri-guard.mjs.map +1 -0
  143. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  144. package/dist/extensions/vendor/openviking-pi/shared/workspace-peer.mjs.map +1 -0
  145. package/dist/extensions/vendor/openviking-pi/sync.js +127 -0
  146. package/dist/extensions/vendor/openviking-pi/sync.js.map +1 -0
  147. package/dist/extensions/vendor/openviking-pi/takeover.js +25 -0
  148. package/dist/extensions/vendor/openviking-pi/takeover.js.map +1 -0
  149. package/dist/extensions/vendor/openviking-pi/tools.js +417 -0
  150. package/dist/extensions/vendor/openviking-pi/tools.js.map +1 -0
  151. package/dist/src/anomalies.js +483 -0
  152. package/dist/src/anomalies.js.map +1 -0
  153. package/dist/src/answerworker.js +180 -0
  154. package/dist/src/answerworker.js.map +1 -0
  155. package/dist/src/appstamp.js +68 -0
  156. package/dist/src/appstamp.js.map +1 -0
  157. package/dist/src/archive/admission.js +3 -0
  158. package/dist/src/archive/admission.js.map +1 -0
  159. package/dist/src/archive/archivedb.js +474 -0
  160. package/dist/src/archive/archivedb.js.map +1 -0
  161. package/dist/src/archive/archiver.js +1770 -0
  162. package/dist/src/archive/archiver.js.map +1 -0
  163. package/dist/src/archive/archivewrite.js +198 -0
  164. package/dist/src/archive/archivewrite.js.map +1 -0
  165. package/dist/src/archive/bundle.js +266 -0
  166. package/dist/src/archive/bundle.js.map +1 -0
  167. package/dist/src/archive/dbmove.js +638 -0
  168. package/dist/src/archive/dbmove.js.map +1 -0
  169. package/dist/src/archive/duplicatecounts.js +80 -0
  170. package/dist/src/archive/duplicatecounts.js.map +1 -0
  171. package/dist/src/archive/eligibility.js +113 -0
  172. package/dist/src/archive/eligibility.js.map +1 -0
  173. package/dist/src/archive/extract.js +288 -0
  174. package/dist/src/archive/extract.js.map +1 -0
  175. package/dist/src/archive/fsutil.js +330 -0
  176. package/dist/src/archive/fsutil.js.map +1 -0
  177. package/dist/src/archive/gzfile.js +277 -0
  178. package/dist/src/archive/gzfile.js.map +1 -0
  179. package/dist/src/archive/kinds.js +22 -0
  180. package/dist/src/archive/kinds.js.map +1 -0
  181. package/dist/src/archive/livecheckpoint.js +178 -0
  182. package/dist/src/archive/livecheckpoint.js.map +1 -0
  183. package/dist/src/archive/liveretention.js +145 -0
  184. package/dist/src/archive/liveretention.js.map +1 -0
  185. package/dist/src/archive/manifest.js +284 -0
  186. package/dist/src/archive/manifest.js.map +1 -0
  187. package/dist/src/archive/overseer.js +283 -0
  188. package/dist/src/archive/overseer.js.map +1 -0
  189. package/dist/src/archive/piglobal.js +129 -0
  190. package/dist/src/archive/piglobal.js.map +1 -0
  191. package/dist/src/archive/plan.js +398 -0
  192. package/dist/src/archive/plan.js.map +1 -0
  193. package/dist/src/archive/prarchive.js +831 -0
  194. package/dist/src/archive/prarchive.js.map +1 -0
  195. package/dist/src/archive/prfiles.js +303 -0
  196. package/dist/src/archive/prfiles.js.map +1 -0
  197. package/dist/src/archive/report.js +169 -0
  198. package/dist/src/archive/report.js.map +1 -0
  199. package/dist/src/archive/restore.js +276 -0
  200. package/dist/src/archive/restore.js.map +1 -0
  201. package/dist/src/archive/rows.js +106 -0
  202. package/dist/src/archive/rows.js.map +1 -0
  203. package/dist/src/archive/seal.js +480 -0
  204. package/dist/src/archive/seal.js.map +1 -0
  205. package/dist/src/archive/status.js +528 -0
  206. package/dist/src/archive/status.js.map +1 -0
  207. package/dist/src/archive/storepass.js +409 -0
  208. package/dist/src/archive/storepass.js.map +1 -0
  209. package/dist/src/backfill.js +217 -0
  210. package/dist/src/backfill.js.map +1 -0
  211. package/dist/src/bench/cost.js +87 -0
  212. package/dist/src/bench/exec.js +76 -0
  213. package/dist/src/bench/values.js +14 -0
  214. package/dist/src/botcontrol.js +127 -0
  215. package/dist/src/botcontrol.js.map +1 -0
  216. package/dist/src/botworker.js +805 -0
  217. package/dist/src/botworker.js.map +1 -0
  218. package/dist/src/capture.js +234 -0
  219. package/dist/src/capture.js.map +1 -0
  220. package/dist/src/cases.js +1491 -0
  221. package/dist/src/cases.js.map +1 -0
  222. package/dist/src/channelfs.js +27 -0
  223. package/dist/src/channelfs.js.map +1 -0
  224. package/dist/src/checklist.js +312 -0
  225. package/dist/src/checklist.js.map +1 -0
  226. package/dist/src/ciretry.js +92 -0
  227. package/dist/src/ciretry.js.map +1 -0
  228. package/dist/src/cli.js +1947 -0
  229. package/dist/src/cli.js.map +1 -0
  230. package/dist/src/cliops.js +334 -0
  231. package/dist/src/cliops.js.map +1 -0
  232. package/dist/src/clonedir.js +15 -0
  233. package/dist/src/clonedir.js.map +1 -0
  234. package/dist/src/commandeffects.js +43 -0
  235. package/dist/src/commandeffects.js.map +1 -0
  236. package/dist/src/commands.js +1335 -0
  237. package/dist/src/commands.js.map +1 -0
  238. package/dist/src/companioninbox.js +203 -0
  239. package/dist/src/companioninbox.js.map +1 -0
  240. package/dist/src/compare.js +673 -0
  241. package/dist/src/compare.js.map +1 -0
  242. package/dist/src/config.js +1041 -0
  243. package/dist/src/config.js.map +1 -0
  244. package/dist/src/configcheck.js +111 -0
  245. package/dist/src/configcheck.js.map +1 -0
  246. package/dist/src/coreext.js +43 -0
  247. package/dist/src/coreext.js.map +1 -0
  248. package/dist/src/daemon.js +945 -0
  249. package/dist/src/daemon.js.map +1 -0
  250. package/dist/src/debuground.js +731 -0
  251. package/dist/src/debuground.js.map +1 -0
  252. package/dist/src/debugroundpi.js +223 -0
  253. package/dist/src/debugroundpi.js.map +1 -0
  254. package/dist/src/diagnostics.js +14 -0
  255. package/dist/src/diagnostics.js.map +1 -0
  256. package/dist/src/digest.js +139 -0
  257. package/dist/src/digest.js.map +1 -0
  258. package/dist/src/dispatch.js +1727 -0
  259. package/dist/src/dispatch.js.map +1 -0
  260. package/dist/src/engine/answerguard.js +18 -0
  261. package/dist/src/engine/answerguard.js.map +1 -0
  262. package/dist/src/engine/botsdk.js +262 -0
  263. package/dist/src/engine/botsdk.js.map +1 -0
  264. package/dist/src/engine/caserunner-sdk.js +332 -0
  265. package/dist/src/engine/caserunner-sdk.js.map +1 -0
  266. package/dist/src/engine/debugpi.js +206 -0
  267. package/dist/src/engine/debugpi.js.map +1 -0
  268. package/dist/src/eod.js +473 -0
  269. package/dist/src/eod.js.map +1 -0
  270. package/dist/src/eodpinwatch.js +391 -0
  271. package/dist/src/eodpinwatch.js.map +1 -0
  272. package/dist/src/escalation.js +1009 -0
  273. package/dist/src/escalation.js.map +1 -0
  274. package/dist/src/eventloop.js +779 -0
  275. package/dist/src/eventloop.js.map +1 -0
  276. package/dist/src/execboundary.js +437 -0
  277. package/dist/src/execboundary.js.map +1 -0
  278. package/dist/src/feedback.js +915 -0
  279. package/dist/src/feedback.js.map +1 -0
  280. package/dist/src/findingscheck.js +200 -0
  281. package/dist/src/findingscheck.js.map +1 -0
  282. package/dist/src/fsdenied.js +26 -0
  283. package/dist/src/fsdenied.js.map +1 -0
  284. package/dist/src/ghread.js +181 -0
  285. package/dist/src/ghread.js.map +1 -0
  286. package/dist/src/github.js +1098 -0
  287. package/dist/src/github.js.map +1 -0
  288. package/dist/src/githubtoken.js +346 -0
  289. package/dist/src/githubtoken.js.map +1 -0
  290. package/dist/src/homes.js +103 -0
  291. package/dist/src/homes.js.map +1 -0
  292. package/dist/src/hostroute.js +24 -0
  293. package/dist/src/hostroute.js.map +1 -0
  294. package/dist/src/improvements.js +625 -0
  295. package/dist/src/improvements.js.map +1 -0
  296. package/dist/src/install.js +946 -0
  297. package/dist/src/install.js.map +1 -0
  298. package/dist/src/installkind.js +40 -0
  299. package/dist/src/installkind.js.map +1 -0
  300. package/dist/src/ipc/capability.js +286 -0
  301. package/dist/src/ipc/capability.js.map +1 -0
  302. package/dist/src/ipc/client.js +226 -0
  303. package/dist/src/ipc/client.js.map +1 -0
  304. package/dist/src/ipc/control.js +70 -0
  305. package/dist/src/ipc/control.js.map +1 -0
  306. package/dist/src/ipc/ghreadshim.js +77 -0
  307. package/dist/src/ipc/ghreadshim.js.map +1 -0
  308. package/dist/src/ipc/launcher.js +65 -0
  309. package/dist/src/ipc/launcher.js.map +1 -0
  310. package/dist/src/ipc/permits.js +293 -0
  311. package/dist/src/ipc/permits.js.map +1 -0
  312. package/dist/src/ipc/postshim.js +137 -0
  313. package/dist/src/ipc/postshim.js.map +1 -0
  314. package/dist/src/ipc/protocol.js +239 -0
  315. package/dist/src/ipc/protocol.js.map +1 -0
  316. package/dist/src/ipc/server.js +312 -0
  317. package/dist/src/ipc/server.js.map +1 -0
  318. package/dist/src/ipc/workerspec.js +148 -0
  319. package/dist/src/ipc/workerspec.js.map +1 -0
  320. package/dist/src/learner/learner.js +1272 -0
  321. package/dist/src/learner/learner.js.map +1 -0
  322. package/dist/src/learner/miner.js +948 -0
  323. package/dist/src/learner/miner.js.map +1 -0
  324. package/dist/src/learner/packet.js +216 -0
  325. package/dist/src/learner/packet.js.map +1 -0
  326. package/dist/src/learner/receipt.js +135 -0
  327. package/dist/src/learner/receipt.js.map +1 -0
  328. package/dist/src/learner/status.js +38 -0
  329. package/dist/src/learner/status.js.map +1 -0
  330. package/dist/src/ledgercheck.js +120 -0
  331. package/dist/src/ledgercheck.js.map +1 -0
  332. package/dist/src/lessonledger.js +451 -0
  333. package/dist/src/lessonledger.js.map +1 -0
  334. package/dist/src/lessonselect.js +639 -0
  335. package/dist/src/lessonselect.js.map +1 -0
  336. package/dist/src/lessonshadow.js +540 -0
  337. package/dist/src/lessonshadow.js.map +1 -0
  338. package/dist/src/levers.js +302 -0
  339. package/dist/src/levers.js.map +1 -0
  340. package/dist/src/live/projection.js +450 -0
  341. package/dist/src/live/projection.js.map +1 -0
  342. package/dist/src/live/publish.js +83 -0
  343. package/dist/src/live/publish.js.map +1 -0
  344. package/dist/src/live/read.js +60 -0
  345. package/dist/src/live/read.js.map +1 -0
  346. package/dist/src/livecli.js +288 -0
  347. package/dist/src/livecli.js.map +1 -0
  348. package/dist/src/loginhint.js +47 -0
  349. package/dist/src/loginhint.js.map +1 -0
  350. package/dist/src/loginrefresh/atomic.js +66 -0
  351. package/dist/src/loginrefresh/atomic.js.map +1 -0
  352. package/dist/src/loginrefresh/lock.js +93 -0
  353. package/dist/src/loginrefresh/lock.js.map +1 -0
  354. package/dist/src/loginrefresh/main.js +58 -0
  355. package/dist/src/loginrefresh/main.js.map +1 -0
  356. package/dist/src/loginrefresh/pirefresh.js +52 -0
  357. package/dist/src/loginrefresh/pirefresh.js.map +1 -0
  358. package/dist/src/loginrefresh/refresher.js +286 -0
  359. package/dist/src/loginrefresh/refresher.js.map +1 -0
  360. package/dist/src/loginrefresh/supervisor.js +253 -0
  361. package/dist/src/loginrefresh/supervisor.js.map +1 -0
  362. package/dist/src/loginrefresh/types.js +4 -0
  363. package/dist/src/loginrefresh/types.js.map +1 -0
  364. package/dist/src/memory/context.js +251 -0
  365. package/dist/src/memory/context.js.map +1 -0
  366. package/dist/src/memory/datafence.js +5 -0
  367. package/dist/src/memory/datafence.js.map +1 -0
  368. package/dist/src/memory/delivery.js +131 -0
  369. package/dist/src/memory/delivery.js.map +1 -0
  370. package/dist/src/memory/eventtags.js +81 -0
  371. package/dist/src/memory/eventtags.js.map +1 -0
  372. package/dist/src/memory/health.js +713 -0
  373. package/dist/src/memory/health.js.map +1 -0
  374. package/dist/src/memory/import.js +504 -0
  375. package/dist/src/memory/import.js.map +1 -0
  376. package/dist/src/memory/layout.js +252 -0
  377. package/dist/src/memory/layout.js.map +1 -0
  378. package/dist/src/memory/legacy-file.js +100 -0
  379. package/dist/src/memory/legacy-file.js.map +1 -0
  380. package/dist/src/memory/lessons.js +370 -0
  381. package/dist/src/memory/lessons.js.map +1 -0
  382. package/dist/src/memory/ovsession.js +449 -0
  383. package/dist/src/memory/ovsession.js.map +1 -0
  384. package/dist/src/memory/relayout.js +259 -0
  385. package/dist/src/memory/relayout.js.map +1 -0
  386. package/dist/src/memory/roundactivity.js +180 -0
  387. package/dist/src/memory/roundactivity.js.map +1 -0
  388. package/dist/src/memory/selection.js +690 -0
  389. package/dist/src/memory/selection.js.map +1 -0
  390. package/dist/src/memory/shadowstatus.js +69 -0
  391. package/dist/src/memory/shadowstatus.js.map +1 -0
  392. package/dist/src/memory/store.js +83 -0
  393. package/dist/src/memory/store.js.map +1 -0
  394. package/dist/src/memory/textidentity.js +8 -0
  395. package/dist/src/memory/textidentity.js.map +1 -0
  396. package/dist/src/metrics.js +773 -0
  397. package/dist/src/metrics.js.map +1 -0
  398. package/dist/src/missingconfig.js +8 -0
  399. package/dist/src/missingconfig.js.map +1 -0
  400. package/dist/src/npmverbs.js +228 -0
  401. package/dist/src/npmverbs.js.map +1 -0
  402. package/dist/src/onboard/choices.js +219 -0
  403. package/dist/src/onboard/choices.js.map +1 -0
  404. package/dist/src/onboard/engine.js +329 -0
  405. package/dist/src/onboard/engine.js.map +1 -0
  406. package/dist/src/onboard/launch.js +75 -0
  407. package/dist/src/onboard/launch.js.map +1 -0
  408. package/dist/src/onboard/pipin.js +22 -0
  409. package/dist/src/onboard/pipin.js.map +1 -0
  410. package/dist/src/onboard/stepforward.js +41 -0
  411. package/dist/src/onboard/stepforward.js.map +1 -0
  412. package/dist/src/onboard/steps/appslug.js +214 -0
  413. package/dist/src/onboard/steps/appslug.js.map +1 -0
  414. package/dist/src/onboard/steps/bringup.js +408 -0
  415. package/dist/src/onboard/steps/bringup.js.map +1 -0
  416. package/dist/src/onboard/steps/compose.js +372 -0
  417. package/dist/src/onboard/steps/compose.js.map +1 -0
  418. package/dist/src/onboard/steps/configdelivery.js +50 -0
  419. package/dist/src/onboard/steps/configdelivery.js.map +1 -0
  420. package/dist/src/onboard/steps/configfill.js +1218 -0
  421. package/dist/src/onboard/steps/configfill.js.map +1 -0
  422. package/dist/src/onboard/steps/deploysh.js +80 -0
  423. package/dist/src/onboard/steps/deploysh.js.map +1 -0
  424. package/dist/src/onboard/steps/embeddings.js +472 -0
  425. package/dist/src/onboard/steps/embeddings.js.map +1 -0
  426. package/dist/src/onboard/steps/escalation.js +571 -0
  427. package/dist/src/onboard/steps/escalation.js.map +1 -0
  428. package/dist/src/onboard/steps/githubaccount.js +595 -0
  429. package/dist/src/onboard/steps/githubaccount.js.map +1 -0
  430. package/dist/src/onboard/steps/githubapp.js +1150 -0
  431. package/dist/src/onboard/steps/githubapp.js.map +1 -0
  432. package/dist/src/onboard/steps/golive.js +508 -0
  433. package/dist/src/onboard/steps/golive.js.map +1 -0
  434. package/dist/src/onboard/steps/index.js +78 -0
  435. package/dist/src/onboard/steps/index.js.map +1 -0
  436. package/dist/src/onboard/steps/ingress.js +817 -0
  437. package/dist/src/onboard/steps/ingress.js.map +1 -0
  438. package/dist/src/onboard/steps/inventory.js +420 -0
  439. package/dist/src/onboard/steps/inventory.js.map +1 -0
  440. package/dist/src/onboard/steps/llmauth.js +966 -0
  441. package/dist/src/onboard/steps/llmauth.js.map +1 -0
  442. package/dist/src/onboard/steps/memory.js +1112 -0
  443. package/dist/src/onboard/steps/memory.js.map +1 -0
  444. package/dist/src/onboard/steps/memorykeys.js +43 -0
  445. package/dist/src/onboard/steps/memorykeys.js.map +1 -0
  446. package/dist/src/onboard/steps/preflight.js +172 -0
  447. package/dist/src/onboard/steps/preflight.js.map +1 -0
  448. package/dist/src/onboard/steps/proof.js +812 -0
  449. package/dist/src/onboard/steps/proof.js.map +1 -0
  450. package/dist/src/onboard/steps/ssh.js +300 -0
  451. package/dist/src/onboard/steps/ssh.js.map +1 -0
  452. package/dist/src/onboard/steps/topology.js +291 -0
  453. package/dist/src/onboard/steps/topology.js.map +1 -0
  454. package/dist/src/onboard/steps/transport.js +519 -0
  455. package/dist/src/onboard/steps/transport.js.map +1 -0
  456. package/dist/src/onboard/steps/tuiroute.js +102 -0
  457. package/dist/src/onboard/steps/tuiroute.js.map +1 -0
  458. package/dist/src/onboard/steps/verifyup.js +378 -0
  459. package/dist/src/onboard/steps/verifyup.js.map +1 -0
  460. package/dist/src/onboard/steps/vmdeploy.js +368 -0
  461. package/dist/src/onboard/steps/vmdeploy.js.map +1 -0
  462. package/dist/src/onboard/toml.js +53 -0
  463. package/dist/src/onboard/toml.js.map +1 -0
  464. package/dist/src/onboard/toolnames.js +30 -0
  465. package/dist/src/onboard/toolnames.js.map +1 -0
  466. package/dist/src/opencli.js +508 -0
  467. package/dist/src/opencli.js.map +1 -0
  468. package/dist/src/openpr.js +457 -0
  469. package/dist/src/openpr.js.map +1 -0
  470. package/dist/src/openviking/client.js +821 -0
  471. package/dist/src/openviking/client.js.map +1 -0
  472. package/dist/src/openviking/context.js +218 -0
  473. package/dist/src/openviking/context.js.map +1 -0
  474. package/dist/src/openviking/embedder.js +457 -0
  475. package/dist/src/openviking/embedder.js.map +1 -0
  476. package/dist/src/openviking/gapsink.js +38 -0
  477. package/dist/src/openviking/gapsink.js.map +1 -0
  478. package/dist/src/openviking/health.js +69 -0
  479. package/dist/src/openviking/health.js.map +1 -0
  480. package/dist/src/openviking/index.js +24 -0
  481. package/dist/src/openviking/index.js.map +1 -0
  482. package/dist/src/openviking/lessonfind.js +54 -0
  483. package/dist/src/openviking/lessonfind.js.map +1 -0
  484. package/dist/src/openviking/probe.js +89 -0
  485. package/dist/src/openviking/probe.js.map +1 -0
  486. package/dist/src/openviking/proc.js +95 -0
  487. package/dist/src/openviking/proc.js.map +1 -0
  488. package/dist/src/openviking/provisionreport.js +144 -0
  489. package/dist/src/openviking/provisionreport.js.map +1 -0
  490. package/dist/src/openviking/reembed.js +317 -0
  491. package/dist/src/openviking/reembed.js.map +1 -0
  492. package/dist/src/openviking/relayout.js +153 -0
  493. package/dist/src/openviking/relayout.js.map +1 -0
  494. package/dist/src/openviking/sidecar-main.js +39 -0
  495. package/dist/src/openviking/sidecar-main.js.map +1 -0
  496. package/dist/src/openviking/sidecar.js +79 -0
  497. package/dist/src/openviking/sidecar.js.map +1 -0
  498. package/dist/src/openviking/store.js +1758 -0
  499. package/dist/src/openviking/store.js.map +1 -0
  500. package/dist/src/openviking/supervisor.js +422 -0
  501. package/dist/src/openviking/supervisor.js.map +1 -0
  502. package/dist/src/openviking/types.js +5 -0
  503. package/dist/src/openviking/types.js.map +1 -0
  504. package/dist/src/operator-recovery.js +172 -0
  505. package/dist/src/operator-recovery.js.map +1 -0
  506. package/dist/src/operatormessage.js +44 -0
  507. package/dist/src/operatormessage.js.map +1 -0
  508. package/dist/src/operatorwrite.js +23 -0
  509. package/dist/src/operatorwrite.js.map +1 -0
  510. package/dist/src/outcomes.js +1364 -0
  511. package/dist/src/outcomes.js.map +1 -0
  512. package/dist/src/piauthcopy.js +5 -0
  513. package/dist/src/piauthcopy.js.map +1 -0
  514. package/dist/src/picli.js +45 -0
  515. package/dist/src/picli.js.map +1 -0
  516. package/dist/src/picwd.js +25 -0
  517. package/dist/src/picwd.js.map +1 -0
  518. package/dist/src/pilogin.js +35 -0
  519. package/dist/src/pilogin.js.map +1 -0
  520. package/dist/src/pingpong.js +1028 -0
  521. package/dist/src/pingpong.js.map +1 -0
  522. package/dist/src/posting-journal.js +442 -0
  523. package/dist/src/posting-journal.js.map +1 -0
  524. package/dist/src/posting-outbox.js +666 -0
  525. package/dist/src/posting-outbox.js.map +1 -0
  526. package/dist/src/posting-receipt.js +216 -0
  527. package/dist/src/posting-receipt.js.map +1 -0
  528. package/dist/src/posting.js +348 -0
  529. package/dist/src/posting.js.map +1 -0
  530. package/dist/src/postops.js +637 -0
  531. package/dist/src/postops.js.map +1 -0
  532. package/dist/src/pr-activity.js +11 -0
  533. package/dist/src/pr-activity.js.map +1 -0
  534. package/dist/src/pr-author.js +5 -0
  535. package/dist/src/pr-author.js.map +1 -0
  536. package/dist/src/pr-conversation.js +355 -0
  537. package/dist/src/pr-conversation.js.map +1 -0
  538. package/dist/src/pr-session-lifecycle.js +542 -0
  539. package/dist/src/pr-session-lifecycle.js.map +1 -0
  540. package/dist/src/preflight.js +341 -0
  541. package/dist/src/preflight.js.map +1 -0
  542. package/dist/src/priorresolve.js +275 -0
  543. package/dist/src/priorresolve.js.map +1 -0
  544. package/dist/src/qcrbin.js +180 -0
  545. package/dist/src/qcrbin.js.map +1 -0
  546. package/dist/src/recheck.js +1399 -0
  547. package/dist/src/recheck.js.map +1 -0
  548. package/dist/src/reconcile.js +840 -0
  549. package/dist/src/reconcile.js.map +1 -0
  550. package/dist/src/recordedfindings.js +15 -0
  551. package/dist/src/recordedfindings.js.map +1 -0
  552. package/dist/src/regression.js +546 -0
  553. package/dist/src/regression.js.map +1 -0
  554. package/dist/src/replay.js +598 -0
  555. package/dist/src/replay.js.map +1 -0
  556. package/dist/src/requiredpasses.js +113 -0
  557. package/dist/src/requiredpasses.js.map +1 -0
  558. package/dist/src/resumecheck.js +168 -0
  559. package/dist/src/resumecheck.js.map +1 -0
  560. package/dist/src/reviewcli.js +671 -0
  561. package/dist/src/reviewcli.js.map +1 -0
  562. package/dist/src/reviewcontext.js +22 -0
  563. package/dist/src/reviewcontext.js.map +1 -0
  564. package/dist/src/reviewdetach.js +574 -0
  565. package/dist/src/reviewdetach.js.map +1 -0
  566. package/dist/src/reviewhistory.js +310 -0
  567. package/dist/src/reviewhistory.js.map +1 -0
  568. package/dist/src/reviewhome.js +26 -0
  569. package/dist/src/reviewhome.js.map +1 -0
  570. package/dist/src/reviewmemory.js +110 -0
  571. package/dist/src/reviewmemory.js.map +1 -0
  572. package/dist/src/reviewnow.js +45 -0
  573. package/dist/src/reviewnow.js.map +1 -0
  574. package/dist/src/reviewrendercli.js +104 -0
  575. package/dist/src/reviewrendercli.js.map +1 -0
  576. package/dist/src/round-work.js +20 -0
  577. package/dist/src/round-work.js.map +1 -0
  578. package/dist/src/runhost.js +72 -0
  579. package/dist/src/runhost.js.map +1 -0
  580. package/dist/src/scanauth.js +25 -0
  581. package/dist/src/scanauth.js.map +1 -0
  582. package/dist/src/scanbox.js +846 -0
  583. package/dist/src/scanbox.js.map +1 -0
  584. package/dist/src/scancli.js +1074 -0
  585. package/dist/src/scancli.js.map +1 -0
  586. package/dist/src/scandriverlog.js +30 -0
  587. package/dist/src/scandriverlog.js.map +1 -0
  588. package/dist/src/session-bridge.js +75 -0
  589. package/dist/src/session-bridge.js.map +1 -0
  590. package/dist/src/session-command-payload.js +29 -0
  591. package/dist/src/session-command-payload.js.map +1 -0
  592. package/dist/src/session-command-supervisor.js +939 -0
  593. package/dist/src/session-command-supervisor.js.map +1 -0
  594. package/dist/src/sessionjsonl.js +85 -0
  595. package/dist/src/sessionjsonl.js.map +1 -0
  596. package/dist/src/sessions.js +541 -0
  597. package/dist/src/sessions.js.map +1 -0
  598. package/dist/src/setup/adapters.js +330 -0
  599. package/dist/src/setup/adapters.js.map +1 -0
  600. package/dist/src/setup/baselines.js +64 -0
  601. package/dist/src/setup/baselines.js.map +1 -0
  602. package/dist/src/setup/caserunner.js +554 -0
  603. package/dist/src/setup/caserunner.js.map +1 -0
  604. package/dist/src/setup/configcommit.js +358 -0
  605. package/dist/src/setup/configcommit.js.map +1 -0
  606. package/dist/src/setup/engine.js +672 -0
  607. package/dist/src/setup/engine.js.map +1 -0
  608. package/dist/src/setup/facts.js +593 -0
  609. package/dist/src/setup/facts.js.map +1 -0
  610. package/dist/src/setup/findings.js +948 -0
  611. package/dist/src/setup/findings.js.map +1 -0
  612. package/dist/src/setup/history.js +338 -0
  613. package/dist/src/setup/history.js.map +1 -0
  614. package/dist/src/setup/interview.js +185 -0
  615. package/dist/src/setup/interview.js.map +1 -0
  616. package/dist/src/setup/ledger.js +207 -0
  617. package/dist/src/setup/ledger.js.map +1 -0
  618. package/dist/src/setup/pins.js +43 -0
  619. package/dist/src/setup/pins.js.map +1 -0
  620. package/dist/src/setup/portrait.js +432 -0
  621. package/dist/src/setup/portrait.js.map +1 -0
  622. package/dist/src/setup/presentation.js +9 -0
  623. package/dist/src/setup/presentation.js.map +1 -0
  624. package/dist/src/setup/profile.js +417 -0
  625. package/dist/src/setup/profile.js.map +1 -0
  626. package/dist/src/setup/replay.js +240 -0
  627. package/dist/src/setup/replay.js.map +1 -0
  628. package/dist/src/setup/replaybuild.js +311 -0
  629. package/dist/src/setup/replaybuild.js.map +1 -0
  630. package/dist/src/setup/replayrun.js +806 -0
  631. package/dist/src/setup/replayrun.js.map +1 -0
  632. package/dist/src/setup/replaysource.js +169 -0
  633. package/dist/src/setup/replaysource.js.map +1 -0
  634. package/dist/src/setup/retune.js +333 -0
  635. package/dist/src/setup/retune.js.map +1 -0
  636. package/dist/src/setup/retunelog.js +51 -0
  637. package/dist/src/setup/retunelog.js.map +1 -0
  638. package/dist/src/setup/scan.js +725 -0
  639. package/dist/src/setup/scan.js.map +1 -0
  640. package/dist/src/setup/scanbuild.js +133 -0
  641. package/dist/src/setup/scanbuild.js.map +1 -0
  642. package/dist/src/setup/scanfacts.js +96 -0
  643. package/dist/src/setup/scanfacts.js.map +1 -0
  644. package/dist/src/setup/scanlease.js +251 -0
  645. package/dist/src/setup/scanlease.js.map +1 -0
  646. package/dist/src/setup/scanreport.js +289 -0
  647. package/dist/src/setup/scanreport.js.map +1 -0
  648. package/dist/src/setup/scanrun.js +1733 -0
  649. package/dist/src/setup/scanrun.js.map +1 -0
  650. package/dist/src/setup/scantree.js +257 -0
  651. package/dist/src/setup/scantree.js.map +1 -0
  652. package/dist/src/setup/stage2.js +617 -0
  653. package/dist/src/setup/stage2.js.map +1 -0
  654. package/dist/src/setup/stage2request.js +259 -0
  655. package/dist/src/setup/stage2request.js.map +1 -0
  656. package/dist/src/setup/survey.js +307 -0
  657. package/dist/src/setup/survey.js.map +1 -0
  658. package/dist/src/setup/surveyrun.js +549 -0
  659. package/dist/src/setup/surveyrun.js.map +1 -0
  660. package/dist/src/setup/textsniff.js +89 -0
  661. package/dist/src/setup/textsniff.js.map +1 -0
  662. package/dist/src/setup/trackersink.js +395 -0
  663. package/dist/src/setup/trackersink.js.map +1 -0
  664. package/dist/src/setup/triage.js +636 -0
  665. package/dist/src/setup/triage.js.map +1 -0
  666. package/dist/src/setup/tuning.js +585 -0
  667. package/dist/src/setup/tuning.js.map +1 -0
  668. package/dist/src/setup/unitcut.js +437 -0
  669. package/dist/src/setup/unitcut.js.map +1 -0
  670. package/dist/src/singleflight.js +72 -0
  671. package/dist/src/singleflight.js.map +1 -0
  672. package/dist/src/slack/announce.js +725 -0
  673. package/dist/src/slack/announce.js.map +1 -0
  674. package/dist/src/slack/api.js +227 -0
  675. package/dist/src/slack/api.js.map +1 -0
  676. package/dist/src/slack/messages.js +158 -0
  677. package/dist/src/slack/messages.js.map +1 -0
  678. package/dist/src/slack/origin.js +550 -0
  679. package/dist/src/slack/origin.js.map +1 -0
  680. package/dist/src/slack/policy.js +225 -0
  681. package/dist/src/slack/policy.js.map +1 -0
  682. package/dist/src/slack/reply.js +153 -0
  683. package/dist/src/slack/reply.js.map +1 -0
  684. package/dist/src/slack/router.js +189 -0
  685. package/dist/src/slack/router.js.map +1 -0
  686. package/dist/src/slack/socket.js +323 -0
  687. package/dist/src/slack/socket.js.map +1 -0
  688. package/dist/src/slack/types.js +11 -0
  689. package/dist/src/slack/types.js.map +1 -0
  690. package/dist/src/sqlitehint.js +30 -0
  691. package/dist/src/sqlitehint.js.map +1 -0
  692. package/dist/src/ssh-observation.js +25 -0
  693. package/dist/src/ssh-observation.js.map +1 -0
  694. package/dist/src/state.js +5869 -0
  695. package/dist/src/state.js.map +1 -0
  696. package/dist/src/statemachine.js +223 -0
  697. package/dist/src/statemachine.js.map +1 -0
  698. package/dist/src/stubs.js +52 -0
  699. package/dist/src/stubs.js.map +1 -0
  700. package/dist/src/takeover.js +506 -0
  701. package/dist/src/takeover.js.map +1 -0
  702. package/dist/src/takeoverpi.js +262 -0
  703. package/dist/src/takeoverpi.js.map +1 -0
  704. package/dist/src/terminal-failure.js +26 -0
  705. package/dist/src/terminal-failure.js.map +1 -0
  706. package/dist/src/thread-resolution-refusal.js +4 -0
  707. package/dist/src/thread-resolution-refusal.js.map +1 -0
  708. package/dist/src/tmux.js +126 -0
  709. package/dist/src/tmux.js.map +1 -0
  710. package/dist/src/trackerid.js +43 -0
  711. package/dist/src/trackerid.js.map +1 -0
  712. package/dist/src/transcriptindex.js +512 -0
  713. package/dist/src/transcriptindex.js.map +1 -0
  714. package/dist/src/tui/actions.js +404 -0
  715. package/dist/src/tui/actions.js.map +1 -0
  716. package/dist/src/tui/alerts.js +523 -0
  717. package/dist/src/tui/alerts.js.map +1 -0
  718. package/dist/src/tui/app.js +1884 -0
  719. package/dist/src/tui/app.js.map +1 -0
  720. package/dist/src/tui/archiveops.js +164 -0
  721. package/dist/src/tui/archiveops.js.map +1 -0
  722. package/dist/src/tui/autofile.js +191 -0
  723. package/dist/src/tui/autofile.js.map +1 -0
  724. package/dist/src/tui/board.js +1219 -0
  725. package/dist/src/tui/board.js.map +1 -0
  726. package/dist/src/tui/candidates.js +699 -0
  727. package/dist/src/tui/candidates.js.map +1 -0
  728. package/dist/src/tui/cellwidth.js +151 -0
  729. package/dist/src/tui/cellwidth.js.map +1 -0
  730. package/dist/src/tui/changelog-store.js +404 -0
  731. package/dist/src/tui/changelog-store.js.map +1 -0
  732. package/dist/src/tui/chord.js +26 -0
  733. package/dist/src/tui/chord.js.map +1 -0
  734. package/dist/src/tui/clipboard.js +81 -0
  735. package/dist/src/tui/clipboard.js.map +1 -0
  736. package/dist/src/tui/cmdbar.js +256 -0
  737. package/dist/src/tui/cmdbar.js.map +1 -0
  738. package/dist/src/tui/companion.js +1339 -0
  739. package/dist/src/tui/companion.js.map +1 -0
  740. package/dist/src/tui/companionplan.js +200 -0
  741. package/dist/src/tui/companionplan.js.map +1 -0
  742. package/dist/src/tui/companiontab.js +61 -0
  743. package/dist/src/tui/companiontab.js.map +1 -0
  744. package/dist/src/tui/confighome.js +105 -0
  745. package/dist/src/tui/confighome.js.map +1 -0
  746. package/dist/src/tui/connect.js +50 -0
  747. package/dist/src/tui/connect.js.map +1 -0
  748. package/dist/src/tui/deploy-ceremony.js +75 -0
  749. package/dist/src/tui/deploy-ceremony.js.map +1 -0
  750. package/dist/src/tui/deploy-drain.js +93 -0
  751. package/dist/src/tui/deploy-drain.js.map +1 -0
  752. package/dist/src/tui/deploy-process.js +234 -0
  753. package/dist/src/tui/deploy-process.js.map +1 -0
  754. package/dist/src/tui/deploytab.js +1263 -0
  755. package/dist/src/tui/deploytab.js.map +1 -0
  756. package/dist/src/tui/design.js +111 -0
  757. package/dist/src/tui/design.js.map +1 -0
  758. package/dist/src/tui/digesttab.js +148 -0
  759. package/dist/src/tui/digesttab.js.map +1 -0
  760. package/dist/src/tui/emptystate.js +32 -0
  761. package/dist/src/tui/emptystate.js.map +1 -0
  762. package/dist/src/tui/evidence.js +790 -0
  763. package/dist/src/tui/evidence.js.map +1 -0
  764. package/dist/src/tui/feed.js +415 -0
  765. package/dist/src/tui/feed.js.map +1 -0
  766. package/dist/src/tui/feedbacktab.js +907 -0
  767. package/dist/src/tui/feedbacktab.js.map +1 -0
  768. package/dist/src/tui/filters.js +75 -0
  769. package/dist/src/tui/filters.js.map +1 -0
  770. package/dist/src/tui/ghread.js +328 -0
  771. package/dist/src/tui/ghread.js.map +1 -0
  772. package/dist/src/tui/historypull.js +61 -0
  773. package/dist/src/tui/historypull.js.map +1 -0
  774. package/dist/src/tui/inboxpull.js +197 -0
  775. package/dist/src/tui/inboxpull.js.map +1 -0
  776. package/dist/src/tui/index.js +1511 -0
  777. package/dist/src/tui/index.js.map +1 -0
  778. package/dist/src/tui/issuesink.js +206 -0
  779. package/dist/src/tui/issuesink.js.map +1 -0
  780. package/dist/src/tui/jsonfile.js +32 -0
  781. package/dist/src/tui/jsonfile.js.map +1 -0
  782. package/dist/src/tui/keys.js +97 -0
  783. package/dist/src/tui/keys.js.map +1 -0
  784. package/dist/src/tui/layout.js +70 -0
  785. package/dist/src/tui/layout.js.map +1 -0
  786. package/dist/src/tui/learning.js +469 -0
  787. package/dist/src/tui/learning.js.map +1 -0
  788. package/dist/src/tui/lessonswitch.js +103 -0
  789. package/dist/src/tui/lessonswitch.js.map +1 -0
  790. package/dist/src/tui/livecontrol.js +167 -0
  791. package/dist/src/tui/livecontrol.js.map +1 -0
  792. package/dist/src/tui/memforward.js +347 -0
  793. package/dist/src/tui/memforward.js.map +1 -0
  794. package/dist/src/tui/memincident.js +287 -0
  795. package/dist/src/tui/memincident.js.map +1 -0
  796. package/dist/src/tui/memory.js +485 -0
  797. package/dist/src/tui/memory.js.map +1 -0
  798. package/dist/src/tui/memtab.js +513 -0
  799. package/dist/src/tui/memtab.js.map +1 -0
  800. package/dist/src/tui/metricstab.js +979 -0
  801. package/dist/src/tui/metricstab.js.map +1 -0
  802. package/dist/src/tui/model.js +129 -0
  803. package/dist/src/tui/model.js.map +1 -0
  804. package/dist/src/tui/mouse.js +83 -0
  805. package/dist/src/tui/mouse.js.map +1 -0
  806. package/dist/src/tui/msg.js +196 -0
  807. package/dist/src/tui/msg.js.map +1 -0
  808. package/dist/src/tui/nativecompanion.js +321 -0
  809. package/dist/src/tui/nativecompanion.js.map +1 -0
  810. package/dist/src/tui/openurl.js +17 -0
  811. package/dist/src/tui/openurl.js.map +1 -0
  812. package/dist/src/tui/paint.js +501 -0
  813. package/dist/src/tui/paint.js.map +1 -0
  814. package/dist/src/tui/panes.js +74 -0
  815. package/dist/src/tui/panes.js.map +1 -0
  816. package/dist/src/tui/pipane.js +166 -0
  817. package/dist/src/tui/pipane.js.map +1 -0
  818. package/dist/src/tui/poll-hooks.js +11 -0
  819. package/dist/src/tui/poll-hooks.js.map +1 -0
  820. package/dist/src/tui/poll.js +113 -0
  821. package/dist/src/tui/poll.js.map +1 -0
  822. package/dist/src/tui/proposals.js +715 -0
  823. package/dist/src/tui/proposals.js.map +1 -0
  824. package/dist/src/tui/prpause.js +139 -0
  825. package/dist/src/tui/prpause.js.map +1 -0
  826. package/dist/src/tui/reload.js +351 -0
  827. package/dist/src/tui/reload.js.map +1 -0
  828. package/dist/src/tui/remote.js +318 -0
  829. package/dist/src/tui/remote.js.map +1 -0
  830. package/dist/src/tui/rollover.js +767 -0
  831. package/dist/src/tui/rollover.js.map +1 -0
  832. package/dist/src/tui/route.js +143 -0
  833. package/dist/src/tui/route.js.map +1 -0
  834. package/dist/src/tui/scantab.js +1053 -0
  835. package/dist/src/tui/scantab.js.map +1 -0
  836. package/dist/src/tui/setupentry.js +81 -0
  837. package/dist/src/tui/setupentry.js.map +1 -0
  838. package/dist/src/tui/setupstate.js +29 -0
  839. package/dist/src/tui/setupstate.js.map +1 -0
  840. package/dist/src/tui/sidebar.js +602 -0
  841. package/dist/src/tui/sidebar.js.map +1 -0
  842. package/dist/src/tui/sshtriage.js +490 -0
  843. package/dist/src/tui/sshtriage.js.map +1 -0
  844. package/dist/src/tui/startup.js +172 -0
  845. package/dist/src/tui/startup.js.map +1 -0
  846. package/dist/src/tui/tabs.js +41 -0
  847. package/dist/src/tui/tabs.js.map +1 -0
  848. package/dist/src/tui/termmirror.js +217 -0
  849. package/dist/src/tui/termmirror.js.map +1 -0
  850. package/dist/src/tui/theme.js +96 -0
  851. package/dist/src/tui/theme.js.map +1 -0
  852. package/dist/src/tui/ticketstab.js +224 -0
  853. package/dist/src/tui/ticketstab.js.map +1 -0
  854. package/dist/src/tui/tmuxsocket.js +101 -0
  855. package/dist/src/tui/tmuxsocket.js.map +1 -0
  856. package/dist/src/tui/trackertickets.js +77 -0
  857. package/dist/src/tui/trackertickets.js.map +1 -0
  858. package/dist/src/tui/unwedge.js +235 -0
  859. package/dist/src/tui/unwedge.js.map +1 -0
  860. package/dist/src/tui/viewer.js +479 -0
  861. package/dist/src/tui/viewer.js.map +1 -0
  862. package/dist/src/tui/viewerinput.js +125 -0
  863. package/dist/src/tui/viewerinput.js.map +1 -0
  864. package/dist/src/updatenotice.js +143 -0
  865. package/dist/src/updatenotice.js.map +1 -0
  866. package/dist/src/usage.js +1007 -0
  867. package/dist/src/usage.js.map +1 -0
  868. package/dist/src/verdicts.js +3860 -0
  869. package/dist/src/verdicts.js.map +1 -0
  870. package/dist/src/version.js +58 -0
  871. package/dist/src/version.js.map +1 -0
  872. package/dist/src/wakes.js +411 -0
  873. package/dist/src/wakes.js.map +1 -0
  874. package/dist/src/webhook.js +736 -0
  875. package/dist/src/webhook.js.map +1 -0
  876. package/dist/src/webhookheal.js +238 -0
  877. package/dist/src/webhookheal.js.map +1 -0
  878. package/dist/src/wire.js +1823 -0
  879. package/dist/src/wire.js.map +1 -0
  880. package/dist/src/worker-session-host.js +531 -0
  881. package/dist/src/worker-session-host.js.map +1 -0
  882. package/dist/src/workerbreaker.js +112 -0
  883. package/dist/src/workerbreaker.js.map +1 -0
  884. package/dist/src/workercontrol.js +49 -0
  885. package/dist/src/workercontrol.js.map +1 -0
  886. package/dist/src/workerlease.js +351 -0
  887. package/dist/src/workerlease.js.map +1 -0
  888. package/dist/src/workerliveness.js +84 -0
  889. package/dist/src/workerliveness.js.map +1 -0
  890. package/dist/src/workerlog.js +65 -0
  891. package/dist/src/workerlog.js.map +1 -0
  892. package/dist/src/workerruntime.js +10 -0
  893. package/dist/src/workerruntime.js.map +1 -0
  894. package/dist/src/workersupervisor.js +1206 -0
  895. package/dist/src/workersupervisor.js.map +1 -0
  896. package/dist/src/workerusage.js +23 -0
  897. package/dist/src/workerusage.js.map +1 -0
  898. package/dist/src/workerworld.js +338 -0
  899. package/dist/src/workerworld.js.map +1 -0
  900. package/dist/src/worktrees.js +984 -0
  901. package/dist/src/worktrees.js.map +1 -0
  902. package/docker/Dockerfile +284 -0
  903. package/docker/Dockerfile.dockerignore +35 -0
  904. package/docker/README.md +221 -0
  905. package/docker/companion/Dockerfile +139 -0
  906. package/docker/companion/agents/evidence.md +34 -0
  907. package/docker/companion/agents/explore.md +26 -0
  908. package/docker/companion/agents/pass-survey.md +49 -0
  909. package/docker/companion/agents/study.md +26 -0
  910. package/docker/companion/entrypoint.sh +362 -0
  911. package/docker/companion/memory-health.mjs +269 -0
  912. package/docker/entrypoint.sh +799 -0
  913. package/docker/memory/Dockerfile +191 -0
  914. package/docker/memory/Dockerfile.dockerignore +20 -0
  915. package/docker/memory/README.md +60 -0
  916. package/docker/memory/entrypoint.sh +163 -0
  917. package/docker/setid/qcr-setid.c +109 -0
  918. package/docker/tunnel/Dockerfile.cloudflared +51 -0
  919. package/docker/tunnel/Dockerfile.ngrok +40 -0
  920. package/docker/tunnel/sidecar.mjs +411 -0
  921. package/docker/vm/tmux.conf +26 -0
  922. package/docker-compose.yml +348 -0
  923. package/extensions/cli/bash-guard-ambient.ts +71 -0
  924. package/extensions/cli/bash-guard.ts +12 -0
  925. package/extensions/cli/companion-guard.ts +8 -0
  926. package/extensions/cli/debug-round.ts +9 -0
  927. package/extensions/cli/inject-bridge.ts +8 -0
  928. package/extensions/cli/ledger-pin.ts +13 -0
  929. package/extensions/cli/lens.ts +22 -0
  930. package/extensions/cli/login-route.ts +10 -0
  931. package/extensions/cli/muted-theme.ts +9 -0
  932. package/extensions/cli/onboarding.ts +8 -0
  933. package/extensions/cli/setup.ts +8 -0
  934. package/extensions/cli/tui-view.ts +8 -0
  935. package/extensions/companion-guard.ts +411 -0
  936. package/extensions/debug-round.ts +281 -0
  937. package/extensions/inject-bridge.ts +630 -0
  938. package/extensions/inline-bash.ts +102 -0
  939. package/extensions/login-route.ts +220 -0
  940. package/extensions/muted-theme.json +68 -0
  941. package/extensions/muted-theme.ts +23 -0
  942. package/extensions/onboarding.ts +262 -0
  943. package/extensions/setup-replay.ts +336 -0
  944. package/extensions/setup-scan.ts +1236 -0
  945. package/extensions/setup-triage.ts +190 -0
  946. package/extensions/setup.ts +1428 -0
  947. package/extensions/tui-view.ts +489 -0
  948. package/extensions/vendor/openviking-pi/LICENSE +201 -0
  949. package/extensions/vendor/openviking-pi/NOTICE +68 -0
  950. package/extensions/vendor/openviking-pi/README.md +550 -0
  951. package/extensions/vendor/openviking-pi/client.ts +588 -0
  952. package/extensions/vendor/openviking-pi/config.json +28 -0
  953. package/extensions/vendor/openviking-pi/config.ts +271 -0
  954. package/extensions/vendor/openviking-pi/index.ts +523 -0
  955. package/extensions/vendor/openviking-pi/lib/capture-adapter.d.mts +10 -0
  956. package/extensions/vendor/openviking-pi/lib/capture-adapter.mjs +88 -0
  957. package/extensions/vendor/openviking-pi/lib/takeover-core.d.mts +80 -0
  958. package/extensions/vendor/openviking-pi/lib/takeover-core.mjs +406 -0
  959. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.d.mts +1 -0
  960. package/extensions/vendor/openviking-pi/lib/uri-guard-adapter.mjs +42 -0
  961. package/extensions/vendor/openviking-pi/patches/0000-vendoring-and-build-conformance.patch +726 -0
  962. package/extensions/vendor/openviking-pi/patches/0001-remove-session-before-compact-handler.patch +101 -0
  963. package/extensions/vendor/openviking-pi/patches/0002-no-ambient-work-in-subagent-leaves.patch +202 -0
  964. package/extensions/vendor/openviking-pi/patches/0003-never-auto-create-openviking-sessions.patch +120 -0
  965. package/extensions/vendor/openviking-pi/patches/0004-viking-write-durable-batch-write.patch +521 -0
  966. package/extensions/vendor/openviking-pi/patches/0005-second-memory-namespace.patch +786 -0
  967. package/extensions/vendor/openviking-pi/patches/0006-writes-that-stick-and-honest-errors.patch +996 -0
  968. package/extensions/vendor/openviking-pi/patches/0007-bounded-recall-no-commit.patch +794 -0
  969. package/extensions/vendor/openviking-pi/patches/0008-pr-derived-recall.patch +379 -0
  970. package/extensions/vendor/openviking-pi/patches/0009-pr-session-tools-in-round-areas.patch +375 -0
  971. package/extensions/vendor/openviking-pi/patches/0010-typed-lessons.patch +448 -0
  972. package/extensions/vendor/openviking-pi/patches/0011-review-session-memory-boundary.patch +382 -0
  973. package/extensions/vendor/openviking-pi/patches/0012-typecheck-against-the-real-pi.patch +129 -0
  974. package/extensions/vendor/openviking-pi/patches/0013-createopenviking-factory.patch +1168 -0
  975. package/extensions/vendor/openviking-pi/qcr-hint.ts +313 -0
  976. package/extensions/vendor/openviking-pi/qcr-honest.ts +183 -0
  977. package/extensions/vendor/openviking-pi/qcr-hooks.ts +87 -0
  978. package/extensions/vendor/openviking-pi/qcr-leaf.ts +78 -0
  979. package/extensions/vendor/openviking-pi/qcr-lesson.ts +231 -0
  980. package/extensions/vendor/openviking-pi/qcr-namespaces.ts +198 -0
  981. package/extensions/vendor/openviking-pi/qcr-recall.ts +288 -0
  982. package/extensions/vendor/openviking-pi/qcr-shim.ts +142 -0
  983. package/extensions/vendor/openviking-pi/qcr-write.ts +427 -0
  984. package/extensions/vendor/openviking-pi/recall.ts +149 -0
  985. package/extensions/vendor/openviking-pi/shared/capture-utils.d.mts +9 -0
  986. package/extensions/vendor/openviking-pi/shared/capture-utils.mjs +515 -0
  987. package/extensions/vendor/openviking-pi/shared/credentials.d.mts +14 -0
  988. package/extensions/vendor/openviking-pi/shared/credentials.mjs +228 -0
  989. package/extensions/vendor/openviking-pi/shared/pending-queue.d.mts +19 -0
  990. package/extensions/vendor/openviking-pi/shared/pending-queue.mjs +458 -0
  991. package/extensions/vendor/openviking-pi/shared/profile-inject.d.mts +17 -0
  992. package/extensions/vendor/openviking-pi/shared/profile-inject.mjs +294 -0
  993. package/extensions/vendor/openviking-pi/shared/recall-compress-core.mjs +195 -0
  994. package/extensions/vendor/openviking-pi/shared/recall-core.d.mts +13 -0
  995. package/extensions/vendor/openviking-pi/shared/recall-core.mjs +609 -0
  996. package/extensions/vendor/openviking-pi/shared/retryable.mjs +9 -0
  997. package/extensions/vendor/openviking-pi/shared/session-model.d.mts +3 -0
  998. package/extensions/vendor/openviking-pi/shared/session-model.mjs +57 -0
  999. package/extensions/vendor/openviking-pi/shared/uri-guard.mjs +56 -0
  1000. package/extensions/vendor/openviking-pi/shared/workspace-peer.d.mts +5 -0
  1001. package/extensions/vendor/openviking-pi/shared/workspace-peer.mjs +16 -0
  1002. package/extensions/vendor/openviking-pi/sync.ts +163 -0
  1003. package/extensions/vendor/openviking-pi/takeover.ts +36 -0
  1004. package/extensions/vendor/openviking-pi/tools.ts +479 -0
  1005. package/extensions/vendor/openviking-pi/upstream.json +48 -0
  1006. package/package.json +138 -4
  1007. package/profiles/default/README.md +21 -0
  1008. package/profiles/default/addenda/.gitkeep +0 -0
  1009. package/profiles/default/profile.toml +17 -0
  1010. package/profiles/example-django/README.md +21 -0
  1011. package/profiles/example-django/addenda/10-migration-discipline.md +13 -0
  1012. package/profiles/example-django/addenda/20-new-column-nullability.md +12 -0
  1013. package/profiles/example-django/addenda/30-test-doctrine.md +12 -0
  1014. package/profiles/example-django/addenda/40-orm-usage.md +19 -0
  1015. package/profiles/example-django/addenda/50-declared-shape.md +10 -0
  1016. package/profiles/example-django/addenda/60-config-placement.md +14 -0
  1017. package/profiles/example-django/heuristics.toml +24 -0
  1018. package/profiles/example-django/profile.toml +9 -0
  1019. package/profiles/fast/README.md +24 -0
  1020. package/profiles/fast/profile.toml +59 -0
  1021. package/profiles/minimal/README.md +17 -0
  1022. package/profiles/minimal/addenda/.gitkeep +0 -0
  1023. package/profiles/minimal/profile.toml +8 -0
  1024. package/profiles/snappy/README.md +24 -0
  1025. package/profiles/snappy/addenda/10-race-as-question.md +6 -0
  1026. package/profiles/snappy/profile.toml +46 -0
  1027. package/prompts/companion-day-open.md +46 -0
  1028. package/prompts/companion-first-run.md +104 -0
  1029. package/prompts/companion.md +821 -0
  1030. package/prompts/conversation-system.md +117 -0
  1031. package/prompts/eod-sweep.md +120 -0
  1032. package/prompts/onboarding.md +113 -0
  1033. package/prompts/pass-common.md +52 -0
  1034. package/prompts/pass-quality-common.md +22 -0
  1035. package/prompts/round-first.md +874 -0
  1036. package/prompts/round-follow-up.md +991 -0
  1037. package/prompts/round-re-review.md +889 -0
  1038. package/prompts/round-recheck.md +196 -0
  1039. package/prompts/round-staleness.md +856 -0
  1040. package/schemas/review-result.v1.json +391 -0
  1041. package/schemas/scan-result.v1.json +421 -0
  1042. package/schemas/version.v1.json +57 -0
  1043. package/scripts/approve-pr.sh +16 -0
  1044. package/scripts/build-stamp.mjs +67 -0
  1045. package/scripts/commit-verdict.sh +155 -0
  1046. package/scripts/companion-e2e.sh +250 -0
  1047. package/scripts/companion-memory-forward.mjs +1966 -0
  1048. package/scripts/config-fingerprint.sh +28 -0
  1049. package/scripts/deploy-manifest.mjs +141 -0
  1050. package/scripts/deploy.sh +1435 -0
  1051. package/scripts/directory-lock.mjs +193 -0
  1052. package/scripts/heartbeat-fresh.mjs +54 -0
  1053. package/scripts/install-locator.sh +328 -0
  1054. package/scripts/install-wrapper.sh +76 -0
  1055. package/scripts/memory-search-check.mjs +98 -0
  1056. package/scripts/mint-token.sh +233 -0
  1057. package/scripts/openviking-embedder.py +392 -0
  1058. package/scripts/openviking-ov-conf.mjs +161 -0
  1059. package/scripts/openviking-provision.mjs +1122 -0
  1060. package/scripts/openviking-reembed.mjs +1154 -0
  1061. package/scripts/post-review.sh +16 -0
  1062. package/scripts/pr-checks.sh +256 -0
  1063. package/scripts/pr-context.py +174 -0
  1064. package/scripts/pr-context.sh +63 -0
  1065. package/scripts/pr-self.sh +144 -0
  1066. package/scripts/pr-threads.sh +145 -0
  1067. package/scripts/qcr +15 -0
  1068. package/scripts/qcr-host.sh +769 -0
  1069. package/scripts/render-review.sh +24 -0
  1070. package/scripts/reply-thread.sh +16 -0
  1071. package/scripts/required-passes.py +112 -0
  1072. package/scripts/smoke-companion.sh +211 -0
  1073. package/scripts/tui-deploy-runner.mjs +61 -0
  1074. package/scripts/verify-dist.mjs +201 -0
  1075. package/scripts/vm-ghcr-login.sh +188 -0
  1076. package/seeds/agents/pass-baseline.md +61 -0
  1077. package/seeds/agents/pass-contracts.md +30 -0
  1078. package/seeds/agents/pass-data-safety.md +54 -0
  1079. package/seeds/agents/pass-delivery.md +37 -0
  1080. package/seeds/agents/pass-prod-risk.md +24 -0
  1081. package/seeds/agents/pass-security.md +31 -0
  1082. package/seeds/agents/pass-statelessness.md +27 -0
  1083. package/seeds/agents/pass-ui.md +23 -0
  1084. package/seeds/agents/pass-validate.md +50 -0
  1085. package/seeds/prompts/conversation-system.md +117 -0
  1086. package/seeds/prompts/eod-sweep.md +120 -0
  1087. package/seeds/prompts/pass-common.md +52 -0
  1088. package/skills/companion/adjudicate/SKILL.md +247 -0
  1089. package/skills/companion/evidence/SKILL.md +150 -0
  1090. package/skills/companion/land/SKILL.md +200 -0
  1091. package/skills/companion/replay/SKILL.md +138 -0
  1092. package/skills/companion/setup/SKILL.md +167 -0
  1093. package/skills/companion/survey/SKILL.md +160 -0
  1094. package/skills/selfimprove/README.md +34 -0
  1095. package/skills/selfimprove/SKILL.md +127 -0
  1096. package/skills/selfimprove/agents/openai.yaml +2 -0
  1097. package/skills/selfimprove/commands/selfimprove.md +6 -0
  1098. package/skills/selfimprove/references/fix-loop.md +49 -0
  1099. package/skills/selfimprove/references/ledger.md +20 -0
  1100. package/skills/selfimprove/references/setup.md +40 -0
  1101. package/src/anomalies.ts +566 -0
  1102. package/src/answerworker.ts +251 -0
  1103. package/src/appstamp.ts +65 -0
  1104. package/src/archive/admission.ts +3 -0
  1105. package/src/archive/archivedb.ts +548 -0
  1106. package/src/archive/archiver.ts +1963 -0
  1107. package/src/archive/archivewrite.ts +279 -0
  1108. package/src/archive/bundle.ts +333 -0
  1109. package/src/archive/dbmove.ts +911 -0
  1110. package/src/archive/duplicatecounts.ts +98 -0
  1111. package/src/archive/eligibility.ts +181 -0
  1112. package/src/archive/extract.ts +342 -0
  1113. package/src/archive/fsutil.ts +415 -0
  1114. package/src/archive/gzfile.ts +370 -0
  1115. package/src/archive/kinds.ts +21 -0
  1116. package/src/archive/livecheckpoint.ts +217 -0
  1117. package/src/archive/liveretention.ts +180 -0
  1118. package/src/archive/manifest.ts +519 -0
  1119. package/src/archive/overseer.ts +336 -0
  1120. package/src/archive/piglobal.ts +174 -0
  1121. package/src/archive/plan.ts +612 -0
  1122. package/src/archive/prarchive.ts +984 -0
  1123. package/src/archive/prfiles.ts +406 -0
  1124. package/src/archive/report.ts +252 -0
  1125. package/src/archive/restore.ts +330 -0
  1126. package/src/archive/rows.ts +187 -0
  1127. package/src/archive/seal.ts +630 -0
  1128. package/src/archive/status.ts +719 -0
  1129. package/src/archive/storepass.ts +581 -0
  1130. package/src/backfill.ts +362 -0
  1131. package/src/bench/cost.ts +113 -0
  1132. package/src/bench/exec.ts +90 -0
  1133. package/src/bench/values.ts +13 -0
  1134. package/src/botcontrol.ts +176 -0
  1135. package/src/botworker.ts +953 -0
  1136. package/src/capture.ts +321 -0
  1137. package/src/cases.ts +1789 -0
  1138. package/src/channelfs.ts +26 -0
  1139. package/src/checklist.ts +506 -0
  1140. package/src/ciretry.ts +142 -0
  1141. package/src/cli.ts +2306 -0
  1142. package/src/cliops.ts +385 -0
  1143. package/src/clonedir.ts +15 -0
  1144. package/src/commandeffects.ts +57 -0
  1145. package/src/commands.ts +1704 -0
  1146. package/src/companioninbox.ts +239 -0
  1147. package/src/compare.ts +957 -0
  1148. package/src/config.ts +2070 -0
  1149. package/src/configcheck.ts +133 -0
  1150. package/src/coreext.ts +47 -0
  1151. package/src/daemon.ts +1327 -0
  1152. package/src/debuground.ts +927 -0
  1153. package/src/debugroundpi.ts +272 -0
  1154. package/src/diagnostics.ts +14 -0
  1155. package/src/digest.ts +183 -0
  1156. package/src/dispatch.ts +2251 -0
  1157. package/src/engine/answerguard.ts +27 -0
  1158. package/src/engine/botsdk.ts +336 -0
  1159. package/src/engine/caserunner-sdk.ts +423 -0
  1160. package/src/engine/debugpi.ts +247 -0
  1161. package/src/eod.ts +669 -0
  1162. package/src/eodpinwatch.ts +556 -0
  1163. package/src/escalation.ts +1253 -0
  1164. package/src/eventloop.ts +1045 -0
  1165. package/src/execboundary.ts +645 -0
  1166. package/src/feedback.ts +1162 -0
  1167. package/src/findingscheck.ts +298 -0
  1168. package/src/fsdenied.ts +24 -0
  1169. package/src/ghread.ts +242 -0
  1170. package/src/github.ts +1539 -0
  1171. package/src/githubtoken.ts +435 -0
  1172. package/src/homes.ts +175 -0
  1173. package/src/hostroute.ts +53 -0
  1174. package/src/improvements.ts +787 -0
  1175. package/src/install.ts +1103 -0
  1176. package/src/installkind.ts +50 -0
  1177. package/src/ipc/capability.ts +333 -0
  1178. package/src/ipc/client.ts +269 -0
  1179. package/src/ipc/control.ts +90 -0
  1180. package/src/ipc/ghreadshim.ts +94 -0
  1181. package/src/ipc/launcher.ts +65 -0
  1182. package/src/ipc/permits.ts +362 -0
  1183. package/src/ipc/postshim.ts +164 -0
  1184. package/src/ipc/protocol.ts +342 -0
  1185. package/src/ipc/server.ts +371 -0
  1186. package/src/ipc/workerspec.ts +218 -0
  1187. package/src/learner/learner.ts +1655 -0
  1188. package/src/learner/miner.ts +1290 -0
  1189. package/src/learner/packet.ts +340 -0
  1190. package/src/learner/receipt.ts +168 -0
  1191. package/src/learner/status.ts +107 -0
  1192. package/src/ledgercheck.ts +157 -0
  1193. package/src/lessonledger.ts +703 -0
  1194. package/src/lessonselect.ts +888 -0
  1195. package/src/lessonshadow.ts +810 -0
  1196. package/src/levers.ts +393 -0
  1197. package/src/live/projection.ts +491 -0
  1198. package/src/live/publish.ts +92 -0
  1199. package/src/live/read.ts +86 -0
  1200. package/src/livecli.ts +336 -0
  1201. package/src/loginhint.ts +61 -0
  1202. package/src/loginrefresh/atomic.ts +76 -0
  1203. package/src/loginrefresh/lock.ts +104 -0
  1204. package/src/loginrefresh/main.ts +59 -0
  1205. package/src/loginrefresh/pirefresh.ts +60 -0
  1206. package/src/loginrefresh/refresher.ts +326 -0
  1207. package/src/loginrefresh/supervisor.ts +273 -0
  1208. package/src/loginrefresh/types.ts +35 -0
  1209. package/src/memory/context.ts +385 -0
  1210. package/src/memory/datafence.ts +9 -0
  1211. package/src/memory/delivery.ts +171 -0
  1212. package/src/memory/eventtags.ts +80 -0
  1213. package/src/memory/health.ts +953 -0
  1214. package/src/memory/import.ts +664 -0
  1215. package/src/memory/layout.ts +261 -0
  1216. package/src/memory/legacy-file.ts +104 -0
  1217. package/src/memory/lessons.ts +447 -0
  1218. package/src/memory/ovsession.ts +601 -0
  1219. package/src/memory/relayout.ts +335 -0
  1220. package/src/memory/roundactivity.ts +209 -0
  1221. package/src/memory/selection.ts +989 -0
  1222. package/src/memory/shadowstatus.ts +125 -0
  1223. package/src/memory/store.ts +275 -0
  1224. package/src/memory/textidentity.ts +8 -0
  1225. package/src/metrics.ts +1056 -0
  1226. package/src/missingconfig.ts +7 -0
  1227. package/src/npmverbs.ts +266 -0
  1228. package/src/onboard/choices.ts +255 -0
  1229. package/src/onboard/engine.ts +537 -0
  1230. package/src/onboard/launch.ts +103 -0
  1231. package/src/onboard/pipin.ts +31 -0
  1232. package/src/onboard/stepforward.ts +61 -0
  1233. package/src/onboard/steps/appslug.ts +268 -0
  1234. package/src/onboard/steps/bringup.ts +482 -0
  1235. package/src/onboard/steps/compose.ts +490 -0
  1236. package/src/onboard/steps/configdelivery.ts +58 -0
  1237. package/src/onboard/steps/configfill.ts +1440 -0
  1238. package/src/onboard/steps/deploysh.ts +105 -0
  1239. package/src/onboard/steps/embeddings.ts +550 -0
  1240. package/src/onboard/steps/escalation.ts +640 -0
  1241. package/src/onboard/steps/githubaccount.ts +716 -0
  1242. package/src/onboard/steps/githubapp.ts +1498 -0
  1243. package/src/onboard/steps/golive.ts +579 -0
  1244. package/src/onboard/steps/index.ts +82 -0
  1245. package/src/onboard/steps/ingress.ts +974 -0
  1246. package/src/onboard/steps/inventory.ts +526 -0
  1247. package/src/onboard/steps/llmauth.ts +1180 -0
  1248. package/src/onboard/steps/memory.ts +1347 -0
  1249. package/src/onboard/steps/memorykeys.ts +85 -0
  1250. package/src/onboard/steps/preflight.ts +191 -0
  1251. package/src/onboard/steps/proof.ts +1042 -0
  1252. package/src/onboard/steps/ssh.ts +377 -0
  1253. package/src/onboard/steps/topology.ts +338 -0
  1254. package/src/onboard/steps/transport.ts +680 -0
  1255. package/src/onboard/steps/tuiroute.ts +119 -0
  1256. package/src/onboard/steps/verifyup.ts +467 -0
  1257. package/src/onboard/steps/vmdeploy.ts +442 -0
  1258. package/src/onboard/toml.ts +50 -0
  1259. package/src/onboard/toolnames.ts +34 -0
  1260. package/src/opencli.ts +591 -0
  1261. package/src/openpr.ts +568 -0
  1262. package/src/openviking/client.ts +1131 -0
  1263. package/src/openviking/context.ts +263 -0
  1264. package/src/openviking/embedder.ts +516 -0
  1265. package/src/openviking/gapsink.ts +63 -0
  1266. package/src/openviking/health.ts +83 -0
  1267. package/src/openviking/index.ts +119 -0
  1268. package/src/openviking/lessonfind.ts +92 -0
  1269. package/src/openviking/probe.ts +129 -0
  1270. package/src/openviking/proc.ts +110 -0
  1271. package/src/openviking/provisionreport.ts +160 -0
  1272. package/src/openviking/reembed.ts +412 -0
  1273. package/src/openviking/relayout.ts +177 -0
  1274. package/src/openviking/sidecar-main.ts +38 -0
  1275. package/src/openviking/sidecar.ts +106 -0
  1276. package/src/openviking/store.ts +2025 -0
  1277. package/src/openviking/supervisor.ts +474 -0
  1278. package/src/openviking/types.ts +174 -0
  1279. package/src/operator-recovery.ts +237 -0
  1280. package/src/operatormessage.ts +52 -0
  1281. package/src/operatorwrite.ts +25 -0
  1282. package/src/outcomes.ts +1677 -0
  1283. package/src/piauthcopy.ts +12 -0
  1284. package/src/picli.ts +49 -0
  1285. package/src/picwd.ts +25 -0
  1286. package/src/pilogin.ts +37 -0
  1287. package/src/pingpong.ts +1339 -0
  1288. package/src/posting-journal.ts +554 -0
  1289. package/src/posting-outbox.ts +810 -0
  1290. package/src/posting-receipt.ts +341 -0
  1291. package/src/posting.ts +603 -0
  1292. package/src/postops.ts +832 -0
  1293. package/src/pr-activity.ts +20 -0
  1294. package/src/pr-author.ts +6 -0
  1295. package/src/pr-conversation.ts +432 -0
  1296. package/src/pr-session-lifecycle.ts +631 -0
  1297. package/src/preflight.ts +391 -0
  1298. package/src/priorresolve.ts +343 -0
  1299. package/src/qcrbin.ts +186 -0
  1300. package/src/recheck.ts +1776 -0
  1301. package/src/reconcile.ts +1056 -0
  1302. package/src/recordedfindings.ts +27 -0
  1303. package/src/regression.ts +833 -0
  1304. package/src/replay.ts +816 -0
  1305. package/src/requiredpasses.ts +148 -0
  1306. package/src/resumecheck.ts +246 -0
  1307. package/src/reviewcli.ts +787 -0
  1308. package/src/reviewcontext.ts +35 -0
  1309. package/src/reviewdetach.ts +734 -0
  1310. package/src/reviewhistory.ts +441 -0
  1311. package/src/reviewhome.ts +36 -0
  1312. package/src/reviewmemory.ts +133 -0
  1313. package/src/reviewnow.ts +76 -0
  1314. package/src/reviewrendercli.ts +139 -0
  1315. package/src/round-work.ts +42 -0
  1316. package/src/runhost.ts +108 -0
  1317. package/src/scanauth.ts +36 -0
  1318. package/src/scanbox.ts +1081 -0
  1319. package/src/scancli.ts +1296 -0
  1320. package/src/scandriverlog.ts +32 -0
  1321. package/src/session-bridge.ts +139 -0
  1322. package/src/session-command-payload.ts +35 -0
  1323. package/src/session-command-supervisor.ts +1142 -0
  1324. package/src/sessionjsonl.ts +88 -0
  1325. package/src/sessions.ts +659 -0
  1326. package/src/setup/adapters.ts +335 -0
  1327. package/src/setup/baselines.ts +110 -0
  1328. package/src/setup/caserunner.ts +611 -0
  1329. package/src/setup/configcommit.ts +443 -0
  1330. package/src/setup/engine.ts +1001 -0
  1331. package/src/setup/facts.ts +627 -0
  1332. package/src/setup/findings.ts +1264 -0
  1333. package/src/setup/history.ts +443 -0
  1334. package/src/setup/interview.ts +243 -0
  1335. package/src/setup/ledger.ts +324 -0
  1336. package/src/setup/pins.ts +66 -0
  1337. package/src/setup/portrait.ts +635 -0
  1338. package/src/setup/presentation.ts +18 -0
  1339. package/src/setup/profile.ts +508 -0
  1340. package/src/setup/replay.ts +370 -0
  1341. package/src/setup/replaybuild.ts +392 -0
  1342. package/src/setup/replayrun.ts +1096 -0
  1343. package/src/setup/replaysource.ts +198 -0
  1344. package/src/setup/retune.ts +415 -0
  1345. package/src/setup/retunelog.ts +95 -0
  1346. package/src/setup/scan.ts +1033 -0
  1347. package/src/setup/scanbuild.ts +168 -0
  1348. package/src/setup/scanfacts.ts +107 -0
  1349. package/src/setup/scanlease.ts +297 -0
  1350. package/src/setup/scanreport.ts +373 -0
  1351. package/src/setup/scanrun.ts +2268 -0
  1352. package/src/setup/scantree.ts +259 -0
  1353. package/src/setup/stage2.ts +926 -0
  1354. package/src/setup/stage2request.ts +347 -0
  1355. package/src/setup/survey.ts +458 -0
  1356. package/src/setup/surveyrun.ts +758 -0
  1357. package/src/setup/textsniff.ts +95 -0
  1358. package/src/setup/trackersink.ts +433 -0
  1359. package/src/setup/triage.ts +768 -0
  1360. package/src/setup/tuning.ts +766 -0
  1361. package/src/setup/unitcut.ts +523 -0
  1362. package/src/singleflight.ts +86 -0
  1363. package/src/slack/announce.ts +911 -0
  1364. package/src/slack/api.ts +303 -0
  1365. package/src/slack/messages.ts +288 -0
  1366. package/src/slack/origin.ts +599 -0
  1367. package/src/slack/policy.ts +354 -0
  1368. package/src/slack/reply.ts +201 -0
  1369. package/src/slack/router.ts +285 -0
  1370. package/src/slack/socket.ts +384 -0
  1371. package/src/slack/types.ts +108 -0
  1372. package/src/sqlitehint.ts +32 -0
  1373. package/src/ssh-observation.ts +30 -0
  1374. package/src/state.ts +8261 -0
  1375. package/src/statemachine.ts +372 -0
  1376. package/src/stubs.ts +68 -0
  1377. package/src/takeover.ts +634 -0
  1378. package/src/takeoverpi.ts +306 -0
  1379. package/src/terminal-failure.ts +41 -0
  1380. package/src/thread-resolution-refusal.ts +2 -0
  1381. package/src/tmux.ts +153 -0
  1382. package/src/trackerid.ts +41 -0
  1383. package/src/transcriptindex.ts +665 -0
  1384. package/src/tui/actions.ts +484 -0
  1385. package/src/tui/alerts.ts +666 -0
  1386. package/src/tui/app.ts +2026 -0
  1387. package/src/tui/archiveops.ts +192 -0
  1388. package/src/tui/autofile.ts +233 -0
  1389. package/src/tui/board.ts +1529 -0
  1390. package/src/tui/candidates.ts +889 -0
  1391. package/src/tui/cellwidth.ts +148 -0
  1392. package/src/tui/changelog-store.ts +503 -0
  1393. package/src/tui/chord.ts +25 -0
  1394. package/src/tui/clipboard.ts +92 -0
  1395. package/src/tui/cmdbar.ts +299 -0
  1396. package/src/tui/companion.ts +1495 -0
  1397. package/src/tui/companionplan.ts +247 -0
  1398. package/src/tui/companiontab.ts +83 -0
  1399. package/src/tui/confighome.ts +155 -0
  1400. package/src/tui/connect.ts +74 -0
  1401. package/src/tui/deploy-ceremony.ts +104 -0
  1402. package/src/tui/deploy-drain.ts +129 -0
  1403. package/src/tui/deploy-process.ts +265 -0
  1404. package/src/tui/deploytab.ts +1523 -0
  1405. package/src/tui/design.ts +125 -0
  1406. package/src/tui/digesttab.ts +214 -0
  1407. package/src/tui/emptystate.ts +39 -0
  1408. package/src/tui/evidence.ts +999 -0
  1409. package/src/tui/feed.ts +604 -0
  1410. package/src/tui/feedbacktab.ts +1077 -0
  1411. package/src/tui/filters.ts +94 -0
  1412. package/src/tui/ghread.ts +388 -0
  1413. package/src/tui/historypull.ts +81 -0
  1414. package/src/tui/inboxpull.ts +236 -0
  1415. package/src/tui/index.ts +1656 -0
  1416. package/src/tui/issuesink.ts +238 -0
  1417. package/src/tui/jsonfile.ts +42 -0
  1418. package/src/tui/keys.ts +109 -0
  1419. package/src/tui/layout.ts +108 -0
  1420. package/src/tui/learning.ts +627 -0
  1421. package/src/tui/lessonswitch.ts +118 -0
  1422. package/src/tui/livecontrol.ts +228 -0
  1423. package/src/tui/memforward.ts +492 -0
  1424. package/src/tui/memincident.ts +363 -0
  1425. package/src/tui/memory.ts +620 -0
  1426. package/src/tui/memtab.ts +606 -0
  1427. package/src/tui/metricstab.ts +1177 -0
  1428. package/src/tui/model.ts +165 -0
  1429. package/src/tui/mouse.ts +98 -0
  1430. package/src/tui/msg.ts +268 -0
  1431. package/src/tui/nativecompanion.ts +417 -0
  1432. package/src/tui/openurl.ts +21 -0
  1433. package/src/tui/paint.ts +511 -0
  1434. package/src/tui/panes.ts +98 -0
  1435. package/src/tui/pipane.ts +219 -0
  1436. package/src/tui/poll-hooks.ts +12 -0
  1437. package/src/tui/poll.ts +147 -0
  1438. package/src/tui/proposals.ts +838 -0
  1439. package/src/tui/prpause.ts +159 -0
  1440. package/src/tui/reload.ts +436 -0
  1441. package/src/tui/remote.ts +421 -0
  1442. package/src/tui/rollover.ts +969 -0
  1443. package/src/tui/route.ts +173 -0
  1444. package/src/tui/scantab.ts +1290 -0
  1445. package/src/tui/setupentry.ts +93 -0
  1446. package/src/tui/setupstate.ts +52 -0
  1447. package/src/tui/sidebar.ts +749 -0
  1448. package/src/tui/sshtriage.ts +631 -0
  1449. package/src/tui/startup.ts +231 -0
  1450. package/src/tui/tabs.ts +45 -0
  1451. package/src/tui/termmirror.ts +224 -0
  1452. package/src/tui/theme.ts +96 -0
  1453. package/src/tui/ticketstab.ts +310 -0
  1454. package/src/tui/tmuxsocket.ts +129 -0
  1455. package/src/tui/trackertickets.ts +84 -0
  1456. package/src/tui/unwedge.ts +274 -0
  1457. package/src/tui/viewer.ts +575 -0
  1458. package/src/tui/viewerinput.ts +173 -0
  1459. package/src/updatenotice.ts +178 -0
  1460. package/src/usage.ts +1154 -0
  1461. package/src/verdicts.ts +4707 -0
  1462. package/src/version.ts +80 -0
  1463. package/src/wakes.ts +476 -0
  1464. package/src/webhook.ts +951 -0
  1465. package/src/webhookheal.ts +276 -0
  1466. package/src/wire.ts +2182 -0
  1467. package/src/worker-session-host.ts +602 -0
  1468. package/src/workerbreaker.ts +156 -0
  1469. package/src/workercontrol.ts +104 -0
  1470. package/src/workerlease.ts +441 -0
  1471. package/src/workerliveness.ts +103 -0
  1472. package/src/workerlog.ts +79 -0
  1473. package/src/workerruntime.ts +30 -0
  1474. package/src/workersupervisor.ts +1538 -0
  1475. package/src/workerusage.ts +42 -0
  1476. package/src/workerworld.ts +433 -0
  1477. package/src/worktrees.ts +1138 -0
  1478. package/tests/fixtures/regression-cases.json +795 -0
  1479. package/tests/fixtures/seed-manifest.json +19 -0
@@ -0,0 +1,1335 @@
1
+ // The overseer's action space: command files in commands/*.json, validated
2
+ // against a closed whitelist and executed by the daemon. There is deliberately
3
+ // no generic escape hatch — anything not on the whitelist is rejected, logged
4
+ // loudly, and answered with a result file the overseer can read.
5
+ //
6
+ // Envelope: {"command": "<name>", ...payload}. Schemas are hand-rolled and
7
+ // STRICT — unknown fields are a rejection, not a warning.
8
+ //
9
+ // Two issuers, decided by directory. The same vocabulary is served
10
+ // from the daemon-owned `commands/` (and `commands/slack/`), where every
11
+ // writer is a pi SESSION, and from `commands/operator/`, which the operator's
12
+ // tools (`qcr drain`, the board's `u`, the deploy tab) create and write as
13
+ // ROOT so that the daemon's uid — the uid every session runs as — cannot write
14
+ // into it. Nothing in a command file says who wrote it; the watcher stamps
15
+ // the issuer from the directory and the seal, and the lever handlers
16
+ // (`src/levers.ts`) bound a session's `drain`/`pause-pr` and never an
17
+ // operator's. A file in the operator directory without the seal is served as
18
+ // a session's, loudly.
19
+ import { createHash } from "node:crypto";
20
+ import { closeSync, constants as fsConstants, existsSync, fstatSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, renameSync, statSync, watch, writeFileSync, } from "node:fs";
21
+ import { basename, dirname, join } from "node:path";
22
+ import { BRANCH_MAX, BRANCH_RE, isBranchName, SLUG_RE } from "./improvements.js";
23
+ import { validateReceipt } from "./learner/receipt.js";
24
+ import { beltRestricts } from "./slack/origin.js";
25
+ import { SLACK_TS_RE } from "./slack/reply.js";
26
+ /** The session-writable command vocabulary — the one list `CommandName` is
27
+ * read from, so a name cannot be typed without being admitted. */
28
+ export const COMMAND_WHITELIST = [
29
+ "rerun-round",
30
+ "restart-session",
31
+ "resolve-terminal-failure",
32
+ "pause-pr",
33
+ "resume-pr",
34
+ "flag-owner",
35
+ "file-improvement-pr",
36
+ "file-issue",
37
+ "slack_reply",
38
+ "drain",
39
+ "retry-thread-decision",
40
+ "eod-curation-receipt",
41
+ ];
42
+ /**
43
+ * The operator's round controls: what the viewer's input line, `msg.ts` and
44
+ * the companion send to a PR's running (or idle) session. `steer` goes straight
45
+ * to the running session (rule 9's one exception), `follow-up` queues behind
46
+ * the turn, `abort-round` stops the round, and `message` is the idle PR's line
47
+ * (`may_post` marks one the operator allows to post). `takeover`, `release` and
48
+ * `debug-round` take, free and replace the PR's host. They are operator
49
+ * commands, so they are valid only from the root-sealed channel, and no session
50
+ * or Slack turn can spell them. The daemon acts on them through the optional
51
+ * `operatorControl` dep; a daemon without it refuses with a reason.
52
+ */
53
+ export const CONTROL_COMMANDS = [
54
+ "steer",
55
+ "follow-up",
56
+ "abort-round",
57
+ "message",
58
+ "takeover",
59
+ "release",
60
+ "debug-round",
61
+ ];
62
+ /** The longest `text` a steer, follow-up or message carries, in characters. */
63
+ export const CONTROL_TEXT_MAX = 8000;
64
+ /**
65
+ * The operator's commands: the lesson kill switch and the archiver's two
66
+ * verbs (`archive-run`, one immediate bounded archiver tick, and
67
+ * `archive-restore`, one PR's archive back to its live paths, both run under
68
+ * the archiver's strict mutex). They are not in `COMMAND_WHITELIST` — the
69
+ * vocabulary a session (the overseer, the EOD curator, a Slack turn) may
70
+ * write — and they validate only for a file the watcher found in the
71
+ * root-sealed `commands/operator/` directory (`operatorSealVerdict`). A
72
+ * session's command file cannot spell them: in the session directories, and
73
+ * in an UNSEALED operator directory (served as a session's), they are refused
74
+ * at validation, and `executeCommand` refuses
75
+ * them again for any issuer but the operator. `review-now` is the operator's
76
+ * "review this PR now": one PR queued as a human-asked round, exactly as a
77
+ * fresh trigger label would queue it, with no GitHub write to ask for it (the
78
+ * onboarding wizard's proof round sends it). The kill switch's state lives
79
+ * in state.db (src/lessonledger.ts), never as a memory tag anyone holding a
80
+ * key could write.
81
+ */
82
+ export const OPERATOR_COMMANDS = [
83
+ "lessons-off",
84
+ "lessons-on",
85
+ "suppress-lesson",
86
+ "unsuppress-lesson",
87
+ "archive-run",
88
+ "archive-restore",
89
+ "review-now",
90
+ ...CONTROL_COMMANDS,
91
+ ];
92
+ /** A lesson the operator names: the full memory URI of one entry
93
+ * (`viking://user/<user>/memories/<area>/<file>.md`) — `qcr lessons
94
+ * suppress` expands a bare `experiences/<file>.md` to it. */
95
+ export const LESSON_URI_RE = /^viking:\/\/user\/[A-Za-z0-9_-]{1,64}\/memories\/(?:[a-z][a-z0-9_-]*\/){1,2}[A-Za-z0-9._-]{1,200}\.md$/;
96
+ function isOperatorCommand(name) {
97
+ return OPERATOR_COMMANDS.includes(name);
98
+ }
99
+ /**
100
+ * Retired command spellings, folded to their current name at validate time —
101
+ * the codebase's ESCALATE→NEEDS_HUMAN pattern: accepted on input (sessions on
102
+ * older prompts may still emit them), never emitted by us — the parsed
103
+ * `Command`, the result file and the event ledger all carry the current name.
104
+ * Pinned by tests/legacy-acceptance.test.ts; an entry here is a permanent
105
+ * belt, not a cleanup candidate.
106
+ */
107
+ const LEGACY_COMMAND_NAMES = {
108
+ "flag-owner-legacy": "flag-owner",
109
+ };
110
+ // ---- hand-rolled schema validation ----
111
+ function fieldError(obj, allowed) {
112
+ const unknown = Object.keys(obj).filter((k) => k !== "command" && !allowed.includes(k));
113
+ return unknown.length > 0 ? `unknown field(s): ${unknown.join(", ")}` : null;
114
+ }
115
+ function isPrNumber(v) {
116
+ return typeof v === "number" && Number.isInteger(v) && v > 0;
117
+ }
118
+ function isGitHubId(v) {
119
+ return typeof v === "number" && Number.isSafeInteger(v) && v > 0;
120
+ }
121
+ function isNonEmptyString(v) {
122
+ return typeof v === "string" && v.trim().length > 0;
123
+ }
124
+ /** The optional `for_min` of a lever command: absent, or a positive integer.
125
+ * The CAP is not checked here — the validator does not know the channel; the
126
+ * lever handler applies it with the issuer in hand. */
127
+ function forMinField(obj) {
128
+ const v = obj["for_min"];
129
+ if (v === undefined)
130
+ return { ok: true, value: undefined };
131
+ if (typeof v !== "number" || !Number.isInteger(v) || v < 1) {
132
+ return {
133
+ ok: false,
134
+ reason: "'for_min' must be a positive integer number of minutes when present",
135
+ };
136
+ }
137
+ return { ok: true, value: v };
138
+ }
139
+ /** Validate a parsed command file against the whitelist + per-command schema. */
140
+ export function validateCommand(raw, opts = {}) {
141
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw)) {
142
+ return { ok: false, reason: "command file must be a JSON object" };
143
+ }
144
+ const obj = raw;
145
+ const name = obj["command"];
146
+ if (typeof name !== "string")
147
+ return { ok: false, reason: "missing 'command' field" };
148
+ if (isOperatorCommand(name)) {
149
+ if (opts.issuer !== "operator")
150
+ return {
151
+ ok: false,
152
+ reason: `command '${name}' is the OPERATOR's alone: it is accepted only from the root-sealed commands/operator/ ` +
153
+ "channel (`qcr lessons off|on|suppress|unsuppress`, `qcr archive run|restore`, `qcr review-now`), never from a session's command file",
154
+ };
155
+ return validateOperatorCommand(name, obj);
156
+ }
157
+ // Legacy-name fold FIRST: an aliased spelling validates against — and parses
158
+ // into — its current name, so downstream (tier table, dispatch, result
159
+ // files) never sees the retired spelling.
160
+ const canonical = LEGACY_COMMAND_NAMES[name] ?? name;
161
+ if (!COMMAND_WHITELIST.includes(canonical)) {
162
+ // A retired command gets its own reason: a session spawned on an older
163
+ // prompt can still emit it and should learn why, not just "unknown". The
164
+ // hint is read by a model, so it must not describe a NEEDS_HUMAN park
165
+ // either — no such park exists.
166
+ if (name === "mark-escalation-worthy") {
167
+ return {
168
+ ok: false,
169
+ reason: "command 'mark-escalation-worthy' was RETIRED on 2026-08-18: escalation-worthiness no longer exists, " +
170
+ "and the review is never handed to a person — every round decides APPROVE or REQUEST_CHANGES on the merits. " +
171
+ "Use pause-pr to hold one, or flag-owner (formerly flag-owner-legacy — the old name is still accepted) to raise it.",
172
+ };
173
+ }
174
+ return {
175
+ ok: false,
176
+ reason: `command '${name}' is not whitelisted (allowed: ${COMMAND_WHITELIST.join(", ")})`,
177
+ };
178
+ }
179
+ switch (canonical) {
180
+ case "rerun-round": {
181
+ const err = fieldError(obj, ["pr"]);
182
+ if (err)
183
+ return { ok: false, reason: err };
184
+ if (!isPrNumber(obj["pr"]))
185
+ return { ok: false, reason: "'pr' must be a positive integer" };
186
+ return { ok: true, command: { command: "rerun-round", pr: obj["pr"] } };
187
+ }
188
+ case "restart-session": {
189
+ const err = fieldError(obj, ["pr"]);
190
+ if (err)
191
+ return { ok: false, reason: err };
192
+ const pr = obj["pr"];
193
+ if (!isPrNumber(pr)) {
194
+ return { ok: false, reason: "'pr' must be a positive integer" };
195
+ }
196
+ return { ok: true, command: { command: "restart-session", pr: pr } };
197
+ }
198
+ case "resolve-terminal-failure": {
199
+ const err = fieldError(obj, ["pr", "incident_id", "command_id", "head_sha"]);
200
+ if (err)
201
+ return { ok: false, reason: err };
202
+ if (!isPrNumber(obj["pr"]))
203
+ return { ok: false, reason: "'pr' must be a positive integer" };
204
+ for (const field of ["incident_id", "command_id"]) {
205
+ if (!isNonEmptyString(obj[field]))
206
+ return { ok: false, reason: `'${field}' must be a non-empty string` };
207
+ }
208
+ // An authoritative GitHub 404 can legitimately correspond to a legacy
209
+ // synthetic debt whose stored head is the empty string. Closed PRs are
210
+ // still forced to a non-empty exact match by resolveTerminalFailure().
211
+ if (typeof obj["head_sha"] !== "string")
212
+ return { ok: false, reason: "'head_sha' must be a string" };
213
+ if (obj["incident_id"] !== `review-failure:${obj["command_id"]}`) {
214
+ return {
215
+ ok: false,
216
+ reason: "'incident_id' must exactly equal review-failure:<command_id>",
217
+ };
218
+ }
219
+ return {
220
+ ok: true,
221
+ command: {
222
+ command: "resolve-terminal-failure",
223
+ pr: obj["pr"],
224
+ incident_id: obj["incident_id"],
225
+ command_id: obj["command_id"],
226
+ head_sha: obj["head_sha"],
227
+ },
228
+ };
229
+ }
230
+ case "pause-pr": {
231
+ const err = fieldError(obj, ["pr", "reason", "for_min"]);
232
+ if (err)
233
+ return { ok: false, reason: err };
234
+ if (!isPrNumber(obj["pr"]))
235
+ return { ok: false, reason: "'pr' must be a positive integer" };
236
+ if (!isNonEmptyString(obj["reason"]))
237
+ return { ok: false, reason: "'reason' must be a non-empty string" };
238
+ const forMin = forMinField(obj);
239
+ if (!forMin.ok)
240
+ return forMin;
241
+ return {
242
+ ok: true,
243
+ command: {
244
+ command: "pause-pr",
245
+ pr: obj["pr"],
246
+ reason: obj["reason"],
247
+ ...(forMin.value === undefined ? {} : { for_min: forMin.value }),
248
+ },
249
+ };
250
+ }
251
+ case "resume-pr": {
252
+ const err = fieldError(obj, ["pr"]);
253
+ if (err)
254
+ return { ok: false, reason: err };
255
+ if (!isPrNumber(obj["pr"]))
256
+ return { ok: false, reason: "'pr' must be a positive integer" };
257
+ return { ok: true, command: { command: "resume-pr", pr: obj["pr"] } };
258
+ }
259
+ case "flag-owner": {
260
+ const err = fieldError(obj, ["message"]);
261
+ if (err)
262
+ return { ok: false, reason: err };
263
+ if (!isNonEmptyString(obj["message"]))
264
+ return { ok: false, reason: "'message' must be a non-empty string" };
265
+ return { ok: true, command: { command: "flag-owner", message: obj["message"] } };
266
+ }
267
+ case "file-improvement-pr": {
268
+ const err = fieldError(obj, ["branch", "title", "body", "slug"]);
269
+ if (err)
270
+ return { ok: false, reason: err };
271
+ for (const f of ["branch", "title", "body", "slug"]) {
272
+ if (!isNonEmptyString(obj[f]))
273
+ return { ok: false, reason: `'${f}' must be a non-empty string` };
274
+ }
275
+ const slug = obj["slug"];
276
+ if (!SLUG_RE.test(slug)) {
277
+ return {
278
+ ok: false,
279
+ reason: `'slug' must match ${SLUG_RE.source} (it becomes bot/improve-<slug>)`,
280
+ };
281
+ }
282
+ // The session writes `branch` and the daemon hands it to git: only a
283
+ // plain local branch name passes — never an option (`--quiet`), a force
284
+ // refspec (`+wip/x`), a refspec (`a:b`) or a revision expression (`a..b`).
285
+ const branch = obj["branch"];
286
+ if (!isBranchName(branch)) {
287
+ return {
288
+ ok: false,
289
+ reason: `'branch' must be a plain local branch name matching ${BRANCH_RE.source} (at most ${BRANCH_MAX} chars)`,
290
+ };
291
+ }
292
+ return {
293
+ ok: true,
294
+ command: {
295
+ command: "file-improvement-pr",
296
+ branch,
297
+ title: obj["title"],
298
+ body: obj["body"],
299
+ slug,
300
+ },
301
+ };
302
+ }
303
+ case "file-issue": {
304
+ // `pr` is optional provenance — the PR a deferral's ticket was filed
305
+ // from. The review session files the ticket itself, in the round that
306
+ // deferred; `pr` attributes the filing — and a cap refusal, which then
307
+ // leaves a `deferral_ticket_refused` anomaly — to that PR, so the refusal
308
+ // is visible where the deferral happened. It grants no rights and lifts
309
+ // no cap.
310
+ const err = fieldError(obj, ["title", "body", "pr"]);
311
+ if (err)
312
+ return { ok: false, reason: err };
313
+ for (const f of ["title", "body"]) {
314
+ if (!isNonEmptyString(obj[f]))
315
+ return { ok: false, reason: `'${f}' must be a non-empty string` };
316
+ }
317
+ if (obj["pr"] !== undefined && !isPrNumber(obj["pr"])) {
318
+ return { ok: false, reason: "'pr' must be a positive integer when present" };
319
+ }
320
+ return {
321
+ ok: true,
322
+ command: {
323
+ command: "file-issue",
324
+ title: obj["title"],
325
+ body: obj["body"],
326
+ ...(obj["pr"] === undefined ? {} : { pr: obj["pr"] }),
327
+ },
328
+ };
329
+ }
330
+ case "drain": {
331
+ const err = fieldError(obj, ["on", "for_min", "hold"]);
332
+ if (err)
333
+ return { ok: false, reason: err };
334
+ if (typeof obj["on"] !== "boolean") {
335
+ return {
336
+ ok: false,
337
+ reason: "'on' must be a boolean (true = engage the drain, false = lift it)",
338
+ };
339
+ }
340
+ const forMin = forMinField(obj);
341
+ if (!forMin.ok)
342
+ return forMin;
343
+ if (forMin.value !== undefined && !obj["on"])
344
+ return { ok: false, reason: "'for_min' only applies with on = true" };
345
+ const hold = obj["hold"];
346
+ if (hold !== undefined && typeof hold !== "boolean")
347
+ return { ok: false, reason: "'hold' must be a boolean" };
348
+ if (hold === true && !obj["on"])
349
+ return { ok: false, reason: "'hold' only applies with on = true (drain off lifts a hold)" };
350
+ if (hold === true && forMin.value !== undefined)
351
+ return {
352
+ ok: false,
353
+ reason: "'hold' has no expiry; 'for_min' and 'hold' exclude each other",
354
+ };
355
+ return {
356
+ ok: true,
357
+ command: {
358
+ command: "drain",
359
+ on: obj["on"],
360
+ ...(forMin.value === undefined ? {} : { for_min: forMin.value }),
361
+ ...(hold === true ? { hold: true } : {}),
362
+ },
363
+ };
364
+ }
365
+ case "retry-thread-decision": {
366
+ const err = fieldError(obj, ["comment_id"]);
367
+ if (err)
368
+ return { ok: false, reason: err };
369
+ if (!isGitHubId(obj["comment_id"]))
370
+ return { ok: false, reason: "'comment_id' must be a positive safe integer" };
371
+ return {
372
+ ok: true,
373
+ command: { command: "retry-thread-decision", comment_id: obj["comment_id"] },
374
+ };
375
+ }
376
+ case "eod-curation-receipt": {
377
+ const res = validateReceipt(obj);
378
+ if (!res.ok)
379
+ return res;
380
+ return { ok: true, command: { command: "eod-curation-receipt", ...res.receipt } };
381
+ }
382
+ case "slack_reply": {
383
+ const err = fieldError(obj, ["thread", "text"]);
384
+ if (err)
385
+ return { ok: false, reason: err };
386
+ // No `channel` field, by construction: the daemon binds every reply to
387
+ // cfg.slack.channel, so posting in the wrong place is unrepresentable.
388
+ if (!isNonEmptyString(obj["thread"]) || !SLACK_TS_RE.test(obj["thread"])) {
389
+ return {
390
+ ok: false,
391
+ reason: "'thread' must be a Slack message ts like \"1712345678.123456\"",
392
+ };
393
+ }
394
+ if (!isNonEmptyString(obj["text"]))
395
+ return { ok: false, reason: "'text' must be a non-empty string" };
396
+ return {
397
+ ok: true,
398
+ command: { command: "slack_reply", thread: obj["thread"], text: obj["text"] },
399
+ };
400
+ }
401
+ }
402
+ }
403
+ /** The operator commands' strict schemas (the issuer was checked first). */
404
+ function validateOperatorCommand(name, obj) {
405
+ switch (name) {
406
+ case "lessons-off":
407
+ case "lessons-on": {
408
+ const err = fieldError(obj, []);
409
+ if (err)
410
+ return { ok: false, reason: err };
411
+ return { ok: true, command: { command: name } };
412
+ }
413
+ case "suppress-lesson":
414
+ case "unsuppress-lesson": {
415
+ const err = fieldError(obj, ["uri"]);
416
+ if (err)
417
+ return { ok: false, reason: err };
418
+ const uri = obj["uri"];
419
+ if (typeof uri !== "string" || !LESSON_URI_RE.test(uri) || uri.includes(".."))
420
+ return {
421
+ ok: false,
422
+ reason: "'uri' must be one memory entry's full URI (viking://user/<user>/memories/<area>/<file>.md)",
423
+ };
424
+ return { ok: true, command: { command: name, uri } };
425
+ }
426
+ case "archive-run": {
427
+ const err = fieldError(obj, []);
428
+ if (err)
429
+ return { ok: false, reason: err };
430
+ return { ok: true, command: { command: "archive-run" } };
431
+ }
432
+ case "archive-restore": {
433
+ const err = fieldError(obj, ["pr"]);
434
+ if (err)
435
+ return { ok: false, reason: err };
436
+ if (!isPrNumber(obj["pr"]))
437
+ return { ok: false, reason: "'pr' must be a positive integer" };
438
+ return { ok: true, command: { command: "archive-restore", pr: obj["pr"] } };
439
+ }
440
+ case "review-now": {
441
+ const err = fieldError(obj, ["pr"]);
442
+ if (err)
443
+ return { ok: false, reason: err };
444
+ if (!isPrNumber(obj["pr"]))
445
+ return { ok: false, reason: "'pr' must be a positive integer" };
446
+ return { ok: true, command: { command: "review-now", pr: obj["pr"] } };
447
+ }
448
+ case "steer":
449
+ case "follow-up":
450
+ case "message": {
451
+ const err = fieldError(obj, name === "message" ? ["pr", "text", "may_post"] : ["pr", "text"]);
452
+ if (err)
453
+ return { ok: false, reason: err };
454
+ if (!isPrNumber(obj["pr"]))
455
+ return { ok: false, reason: "'pr' must be a positive integer" };
456
+ const text = obj["text"];
457
+ if (!isNonEmptyString(text) || text.length > CONTROL_TEXT_MAX)
458
+ return {
459
+ ok: false,
460
+ reason: `'text' must be a non-empty string of at most ${CONTROL_TEXT_MAX} characters`,
461
+ };
462
+ if (name !== "message")
463
+ return { ok: true, command: { command: name, pr: obj["pr"], text } };
464
+ const mayPost = obj["may_post"];
465
+ if (mayPost !== undefined && typeof mayPost !== "boolean")
466
+ return { ok: false, reason: "'may_post' must be a boolean when present" };
467
+ return {
468
+ ok: true,
469
+ command: {
470
+ command: "message",
471
+ pr: obj["pr"],
472
+ text,
473
+ ...(mayPost === undefined ? {} : { may_post: mayPost }),
474
+ },
475
+ };
476
+ }
477
+ case "abort-round":
478
+ case "takeover":
479
+ case "release":
480
+ case "debug-round": {
481
+ const err = fieldError(obj, ["pr"]);
482
+ if (err)
483
+ return { ok: false, reason: err };
484
+ if (!isPrNumber(obj["pr"]))
485
+ return { ok: false, reason: "'pr' must be a positive integer" };
486
+ return { ok: true, command: { command: name, pr: obj["pr"] } };
487
+ }
488
+ }
489
+ }
490
+ // ---- tier enforcement ----
491
+ /**
492
+ * Commands a Slack turn may run, by tier. The table is data on purpose: adding
493
+ * a command without deciding its tier is then impossible to do by accident.
494
+ *
495
+ * `own` = allowed for tier `author` only when the target PR is in the origin's
496
+ * `authorized_prs` (recomputed by the router from `pr_state.author`, NEVER read
497
+ * out of the inject body).
498
+ *
499
+ * `owner` is the owner's tier (legacy persisted records with the retired tier
500
+ * spelling fold to it on read). The `companion` tier is not a column: it is the
501
+ * owner's column read again (`tierAllows`), so the two are one shared set and a
502
+ * verb added for the owner is the companion's in the same edit (by ruling:
503
+ * the companion can do everything the owner can). `system` is the
504
+ * daemon's own unattended turns (`OriginStore.writeLocal`): it is `true` on
505
+ * every row but the operator's round controls (`CONTROL_COMMANDS`, which a
506
+ * daemon turn has no standing to send) because a
507
+ * daemon turn with NO origin record already runs unrestricted —
508
+ * `authorizeCommand(_, null)` ⇒ ok, and that null-origin rule is unchanged.
509
+ * The column is the stamped, auditable form of that same
510
+ * standing authority; it never widens anything, and a Slack turn can never
511
+ * reach it (`computeTier` never returns `system`).
512
+ *
513
+ * Nothing here can reach the dry_run flag, a deploy, an approval or anything
514
+ * review-dismissing: those are not commands at all, and this feature adds no
515
+ * GitHub mutation — every write still goes through the MutationGate.
516
+ */
517
+ const TIER_TABLE = {
518
+ "rerun-round": { anyone: false, author: "own", owner: true, system: true },
519
+ "pause-pr": { anyone: false, author: "own", owner: true, system: true },
520
+ "resume-pr": { anyone: false, author: "own", owner: true, system: true },
521
+ // The safety valve: anyone may always reach a human.
522
+ "flag-owner": { anyone: true, author: true, owner: true, system: true },
523
+ "restart-session": { anyone: false, author: false, owner: true, system: true },
524
+ // Terminal debt cleanup is destructive and never author-reachable. Its
525
+ // handler additionally requires exact identity + authoritative terminality.
526
+ "resolve-terminal-failure": { anyone: false, author: false, owner: true, system: true },
527
+ // improvements.ts caps still apply ON TOP — a tier never lifts a cap.
528
+ "file-improvement-pr": { anyone: false, author: false, owner: true, system: true },
529
+ // The review session files a deferral's ticket itself, in the round that
530
+ // deferred — there is no author confirmation step. Its command files land
531
+ // in the `commands/` dir with no Slack origin, which already runs at full
532
+ // (system-column) autonomy, so the deferral path needs no widening here. On
533
+ // Slack, `file-issue` is the owner's alone. The day cap, the dedup and the
534
+ // gate all still apply downstream — a tier never lifts a cap.
535
+ "file-issue": { anyone: false, author: false, owner: true, system: true },
536
+ // Not user-tiered: gated by channel binding, thread, caps, cooldown, length.
537
+ slack_reply: { anyone: true, author: true, owner: true, system: true },
538
+ // Deploy-window drain: an operator lever (same standing as restart-session)
539
+ // — it stops the whole board, so it is never author-reachable.
540
+ drain: { anyone: false, author: false, owner: true, system: true },
541
+ "retry-thread-decision": { anyone: false, author: false, owner: true, system: true },
542
+ // The EOD curator's receipt — a daemon (system) turn's, or the
543
+ // owner's; never a Slack author's or anyone's.
544
+ "eod-curation-receipt": { anyone: false, author: false, owner: true, system: true },
545
+ // The operator's round controls reach the daemon only from the sealed
546
+ // operator channel (`validateCommand`), so this row never admits a Slack
547
+ // turn; it is the verb set the owner and the companion share. A daemon turn
548
+ // (`system`) never steers, takes over or releases a PR.
549
+ steer: { anyone: false, author: false, owner: true, system: false },
550
+ "follow-up": { anyone: false, author: false, owner: true, system: false },
551
+ "abort-round": { anyone: false, author: false, owner: true, system: false },
552
+ message: { anyone: false, author: false, owner: true, system: false },
553
+ takeover: { anyone: false, author: false, owner: true, system: false },
554
+ release: { anyone: false, author: false, owner: true, system: false },
555
+ "debug-round": { anyone: false, author: false, owner: true, system: false },
556
+ };
557
+ /** May `tier` send `name`? `companion` reads the owner's column: one set. */
558
+ export function tierAllows(tier, name) {
559
+ const row = TIER_TABLE[name];
560
+ if (!row)
561
+ return false;
562
+ return tier === "system" ? row.system : row.owner;
563
+ }
564
+ /** Every verb `tier` may send, in table order. */
565
+ export function tierVerbs(tier) {
566
+ return Object.keys(TIER_TABLE).filter((name) => tierAllows(tier, name));
567
+ }
568
+ /** Target PR of a command, when it has one. */
569
+ function targetPr(command) {
570
+ if (command.command === "restart-session")
571
+ return typeof command.pr === "number" ? command.pr : null;
572
+ return "pr" in command && typeof command.pr === "number" ? command.pr : null;
573
+ }
574
+ /**
575
+ * May this command run on behalf of `origin`?
576
+ *
577
+ * `origin === null` means "no Slack turn is live" — the command came from the
578
+ * daemon itself (EOD sweep, anomaly wake) or from the owner at the tmux pane,
579
+ * and keeps full autonomy (the same rights the `system` tier stamps
580
+ * explicitly). A caller that must fail closed (the Slack command directory)
581
+ * checks for a missing origin BEFORE calling this.
582
+ *
583
+ * Every rejection reason starts with `tier: ` so the overseer — and the
584
+ * `command_rejected` event stream — can tell a rights refusal from a cap.
585
+ */
586
+ export function authorizeCommand(command, origin) {
587
+ if (isOperatorCommand(command.command))
588
+ return {
589
+ ok: false,
590
+ reason: `tier: '${command.command}' is the operator's alone (commands/operator/), never a Slack turn's`,
591
+ };
592
+ if (origin === null)
593
+ return { ok: true };
594
+ const row = TIER_TABLE[command.command];
595
+ const pr = targetPr(command);
596
+ switch (origin.tier) {
597
+ case "owner":
598
+ return row.owner
599
+ ? { ok: true }
600
+ : { ok: false, reason: `tier: '${command.command}' is not available` };
601
+ case "system":
602
+ return row.system
603
+ ? { ok: true }
604
+ : { ok: false, reason: `tier: '${command.command}' is not available` };
605
+ case "author": {
606
+ if (row.author === true)
607
+ return { ok: true };
608
+ if (row.author === false) {
609
+ return {
610
+ ok: false,
611
+ reason: `tier: '${command.command}' requires tier owner (this turn is tier author)`,
612
+ };
613
+ }
614
+ if (pr === null)
615
+ return {
616
+ ok: false,
617
+ reason: `tier: '${command.command}' needs a PR number to check ownership`,
618
+ };
619
+ if (origin.authorized_prs.includes(pr))
620
+ return { ok: true };
621
+ return {
622
+ ok: false,
623
+ reason: `tier: PR #${pr} is not authored by this Slack user (authorized: ${origin.authorized_prs.length ? origin.authorized_prs.join(", ") : "none"})`,
624
+ };
625
+ }
626
+ default:
627
+ // `anyone` and `ignored` alike: read-only plus the two ungated commands.
628
+ return row.anyone
629
+ ? { ok: true }
630
+ : {
631
+ ok: false,
632
+ reason: `tier: '${command.command}' is not available at tier ${origin.tier}`,
633
+ };
634
+ }
635
+ }
636
+ /**
637
+ * May the companion send this command? Not a Slack origin: the TUI forwards a
638
+ * companion's action as an operator command, and checks it here first.
639
+ * The verbs are the owner's, no more and no fewer (`tierAllows`).
640
+ */
641
+ export function authorizeCompanionCommand(command) {
642
+ return tierAllows("companion", command.command)
643
+ ? { ok: true }
644
+ : { ok: false, reason: `tier: '${command.command}' is not available to the companion` };
645
+ }
646
+ function normalize(outcome) {
647
+ return outcome === undefined || outcome === null ? { ok: true } : outcome;
648
+ }
649
+ /** Dispatch a validated command to the injected handlers. */
650
+ export async function executeCommand(command, deps,
651
+ // A direct call with no context is a SESSION's: the bounded side is the
652
+ // default, and only the operator watcher ever stamps the other one.
653
+ ctx = { file: "", issuer: "session" }) {
654
+ switch (command.command) {
655
+ case "rerun-round":
656
+ return normalize(await deps.rerunRound(command.pr));
657
+ case "restart-session":
658
+ return normalize(await deps.restartSession(command.pr, ...(ctx.delivery_key ? [ctx.delivery_key] : [])));
659
+ case "resolve-terminal-failure":
660
+ return normalize(await deps.resolveTerminalFailure({
661
+ pr: command.pr,
662
+ incident_id: command.incident_id,
663
+ command_id: command.command_id,
664
+ head_sha: command.head_sha,
665
+ }));
666
+ case "pause-pr":
667
+ return normalize(await deps.pausePr(command.pr, command.reason, ctx.issuer, command.for_min, ...(ctx.delivery_key ? [ctx.delivery_key] : [])));
668
+ case "resume-pr":
669
+ return normalize(await deps.resumePr(command.pr, ctx.issuer));
670
+ case "flag-owner":
671
+ return normalize(await deps.flagOwner(command.message));
672
+ case "file-improvement-pr":
673
+ return normalize(await deps.fileImprovementPr({
674
+ branch: command.branch,
675
+ title: command.title,
676
+ body: command.body,
677
+ slug: command.slug,
678
+ }, ...(ctx.delivery_key ? [ctx.delivery_key] : [])));
679
+ case "file-issue":
680
+ return normalize(await deps.fileIssue({
681
+ title: command.title,
682
+ body: command.body,
683
+ ...(command.pr === undefined ? {} : { pr: command.pr }),
684
+ }, ...(ctx.delivery_key ? [ctx.delivery_key] : [])));
685
+ case "slack_reply": {
686
+ if (!deps.slackReply)
687
+ return { ok: false, reason: "slack_reply is not wired (Slack is disabled)" };
688
+ return normalize(await deps.slackReply({
689
+ thread: command.thread,
690
+ text: command.text,
691
+ // Session filenames are stable replay keys, even after archival.
692
+ // Sealed operator rewrites are separate, versioned deliveries.
693
+ file: ctx.issuer === "operator" ? (ctx.delivery_key ?? ctx.file) : ctx.file,
694
+ }));
695
+ }
696
+ case "drain": {
697
+ if (!deps.drain)
698
+ return { ok: false, reason: "drain is not wired on this daemon" };
699
+ if (command.hold === true)
700
+ return normalize(await deps.drain(command.on, ctx.issuer, command.for_min, ctx.delivery_key, true));
701
+ return normalize(await deps.drain(command.on, ctx.issuer, command.for_min, ...(ctx.delivery_key ? [ctx.delivery_key] : [])));
702
+ }
703
+ case "retry-thread-decision": {
704
+ if (!deps.retryThreadDecision)
705
+ return { ok: false, reason: "retry-thread-decision is not wired on this daemon" };
706
+ return normalize(await deps.retryThreadDecision(command.comment_id));
707
+ }
708
+ case "eod-curation-receipt": {
709
+ if (!deps.curationReceipt)
710
+ return {
711
+ ok: false,
712
+ reason: "eod-curation-receipt is not wired on this daemon (no learner)",
713
+ };
714
+ return normalize(await deps.curationReceipt({ day: command.day, decisions: command.decisions }));
715
+ }
716
+ case "lessons-off":
717
+ case "lessons-on":
718
+ case "suppress-lesson":
719
+ case "unsuppress-lesson": {
720
+ // The belt behind validation's brace: whoever reached this function,
721
+ // only an operator-stamped context may flip the kill switch.
722
+ if (ctx.issuer !== "operator")
723
+ return {
724
+ ok: false,
725
+ reason: `'${command.command}' is the operator's alone — refused for issuer '${ctx.issuer}'`,
726
+ };
727
+ if (!deps.lessonSwitch)
728
+ return { ok: false, reason: `${command.command} is not wired on this daemon` };
729
+ const change = command.command === "lessons-off"
730
+ ? { action: "off" }
731
+ : command.command === "lessons-on"
732
+ ? { action: "on" }
733
+ : command.command === "suppress-lesson"
734
+ ? { action: "suppress", uri: command.uri }
735
+ : { action: "unsuppress", uri: command.uri };
736
+ return normalize(await deps.lessonSwitch(change, ctx.delivery_key ?? ctx.file));
737
+ }
738
+ case "archive-run":
739
+ case "archive-restore": {
740
+ // The belt behind validation's brace, as for the kill switch.
741
+ if (ctx.issuer !== "operator")
742
+ return {
743
+ ok: false,
744
+ reason: `'${command.command}' is the operator's alone — refused for issuer '${ctx.issuer}'`,
745
+ };
746
+ if (command.command === "archive-run") {
747
+ if (!deps.archiveRun)
748
+ return { ok: false, reason: "archive-run is not wired on this daemon" };
749
+ return normalize(await deps.archiveRun(ctx.delivery_key ?? ctx.file));
750
+ }
751
+ if (!deps.archiveRestore)
752
+ return { ok: false, reason: "archive-restore is not wired on this daemon" };
753
+ return normalize(await deps.archiveRestore(command.pr, ctx.delivery_key ?? ctx.file));
754
+ }
755
+ case "review-now": {
756
+ // The belt behind validation's brace, as for the kill switch.
757
+ if (ctx.issuer !== "operator")
758
+ return {
759
+ ok: false,
760
+ reason: `'${command.command}' is the operator's alone — refused for issuer '${ctx.issuer}'`,
761
+ };
762
+ if (!deps.reviewNow)
763
+ return { ok: false, reason: "review-now is not wired on this daemon" };
764
+ return normalize(await deps.reviewNow(command.pr, ctx.delivery_key ?? ctx.file));
765
+ }
766
+ case "steer":
767
+ case "follow-up":
768
+ case "abort-round":
769
+ case "message":
770
+ case "takeover":
771
+ case "release":
772
+ case "debug-round": {
773
+ // The belt behind validation's brace, as for the kill switch.
774
+ if (ctx.issuer !== "operator")
775
+ return {
776
+ ok: false,
777
+ reason: `'${command.command}' is the operator's alone — refused for issuer '${ctx.issuer}'`,
778
+ };
779
+ if (!deps.operatorControl)
780
+ return { ok: false, reason: `${command.command} is not wired on this daemon` };
781
+ return normalize(await deps.operatorControl(command, ctx.delivery_key ?? ctx.file));
782
+ }
783
+ }
784
+ }
785
+ export function processOperatorSeal() {
786
+ return {
787
+ euid: process.geteuid?.() ?? -1,
788
+ gids: process.getgroups?.() ?? [],
789
+ stat(path) {
790
+ try {
791
+ // lstat, not stat: the seal is a claim about the path the operator's
792
+ // tools wrote, and a symlink planted at that path must show as one.
793
+ const s = lstatSync(path);
794
+ const kind = s.isSymbolicLink()
795
+ ? "symlink"
796
+ : s.isDirectory()
797
+ ? "dir"
798
+ : s.isFile()
799
+ ? "file"
800
+ : "other";
801
+ return { uid: s.uid, gid: s.gid, mode: s.mode & 0o7777, kind };
802
+ }
803
+ catch {
804
+ return null;
805
+ }
806
+ },
807
+ };
808
+ }
809
+ /** Could a process with `seal`'s identity write this path? */
810
+ function writableBy(seal, s) {
811
+ if (s.uid === seal.euid)
812
+ return true;
813
+ if ((s.mode & 0o002) !== 0)
814
+ return true;
815
+ return seal.gids.includes(s.gid) && (s.mode & 0o020) !== 0;
816
+ }
817
+ /**
818
+ * Is `file` in `dir` the OPERATOR's? It is exactly when the directory is a
819
+ * real directory and the file a real file, and neither could have been
820
+ * written by the daemon's own identity — the identity every session shares.
821
+ * Anything else (a symlink standing in for the directory — the parent
822
+ * `commands/` is the daemon's, so a session can plant one — the directory
823
+ * made by the daemon's uid, a group- or world-writable mode, a file the
824
+ * daemon could edit) is not a seal, and the verdict says which.
825
+ */
826
+ export function operatorSealVerdict(seal, dir, file) {
827
+ const d = seal.stat(dir);
828
+ if (!d)
829
+ return { sealed: false, reason: "operator directory missing" };
830
+ if (d.kind !== "dir")
831
+ return { sealed: false, reason: `operator directory is a ${d.kind}, not a directory` };
832
+ if (writableBy(seal, d))
833
+ return {
834
+ sealed: false,
835
+ reason: `operator directory is writable by uid ${seal.euid} (owner ${d.uid}, mode ${d.mode.toString(8)})`,
836
+ };
837
+ const f = seal.stat(file);
838
+ if (!f)
839
+ return { sealed: false, reason: "command file vanished" };
840
+ if (f.kind !== "file")
841
+ return { sealed: false, reason: `command file is a ${f.kind}, not a regular file` };
842
+ if (writableBy(seal, f))
843
+ return {
844
+ sealed: false,
845
+ reason: `command file is writable by uid ${seal.euid} (owner ${f.uid}, mode ${f.mode.toString(8)})`,
846
+ };
847
+ return { sealed: true };
848
+ }
849
+ /**
850
+ * The identity of one version of an operator command file — the key the
851
+ * handled-ledger is written under. The operator's directory is not the
852
+ * daemon's to archive from, so "handled" has to be remembered; remembering the
853
+ * name alone would make every re-used name a one-shot, silently ignoring a
854
+ * second write under the same name. Inode + mtime + size change on every
855
+ * rewrite that matters — the operator's tools land files by tmp+mv, a new
856
+ * inode each time — and stay put across the sweeps that re-see an unchanged
857
+ * file. `null` when the file is gone.
858
+ */
859
+ export function operatorCommandVersion(path) {
860
+ try {
861
+ return commandVersionOf(path, statSync(path, { bigint: true }));
862
+ }
863
+ catch {
864
+ return null;
865
+ }
866
+ }
867
+ function commandVersionOf(path, s) {
868
+ return `${basename(path)}:${s.ino}:${s.mtimeNs}:${s.size}`;
869
+ }
870
+ /** `dev:ino` of the object AT `path` (a symlink as itself), `null` when absent. */
871
+ function fileIdentityAt(path) {
872
+ try {
873
+ const s = lstatSync(path, { bigint: true });
874
+ return `${s.dev}:${s.ino}`;
875
+ }
876
+ catch {
877
+ return null;
878
+ }
879
+ }
880
+ /** The inode at `path` — what `fs.watch` binds to — or `null` when nothing is there. */
881
+ function inodeAt(path) {
882
+ try {
883
+ return statSync(path, { bigint: true }).ino;
884
+ }
885
+ catch {
886
+ return null;
887
+ }
888
+ }
889
+ // These handlers own richer durable recovery (filing reconciles exact GitHub
890
+ // markers; lever/process handlers journal their effects; the remaining handlers
891
+ // use their own delivery key). Pending recovery must reach those handlers.
892
+ const HANDLER_RECOVERY_COMMANDS = new Set([
893
+ "file-improvement-pr",
894
+ "file-issue",
895
+ "pause-pr",
896
+ "drain",
897
+ "restart-session",
898
+ "slack_reply",
899
+ "lessons-off",
900
+ "lessons-on",
901
+ "suppress-lesson",
902
+ "unsuppress-lesson",
903
+ ]);
904
+ export class CommandWatcher {
905
+ opts;
906
+ dir;
907
+ resultsDir;
908
+ processedDir;
909
+ now;
910
+ log;
911
+ watcher = null;
912
+ /** The inode `watcher` is bound to — `fs.watch` follows the inode, not the path. */
913
+ watchedIno = null;
914
+ parentWatcher = null;
915
+ sweepTimer = null;
916
+ inFlight = new Set();
917
+ /** Diagnostics only: every sweep still reads/parses the source anew. */
918
+ parseFailures = new Map();
919
+ /** Serialises handling so two file events can't race the same caps. */
920
+ queue = Promise.resolve();
921
+ constructor(opts) {
922
+ this.opts = opts;
923
+ this.dir = opts.dir;
924
+ const resultsRoot = opts.operator ? dirname(opts.dir) : opts.dir;
925
+ this.resultsDir = join(resultsRoot, "results");
926
+ this.processedDir = join(opts.dir, "processed");
927
+ this.now = opts.now ?? (() => new Date());
928
+ this.log = opts.log ?? ((line) => console.error(line));
929
+ }
930
+ ensureDirs() {
931
+ mkdirSync(this.resultsDir, { recursive: true });
932
+ if (this.opts.operator)
933
+ return; // never ours to create — see `operator`
934
+ mkdirSync(this.dir, { recursive: true });
935
+ mkdirSync(this.processedDir, { recursive: true });
936
+ }
937
+ /** Startup sweep + fs.watch. Returns once the initial sweep has completed.
938
+ * The operator directory may not exist yet (the operator's first write
939
+ * creates it): then the parent is watched for it to appear, and the sweep
940
+ * timer re-tries the attach as a belt. */
941
+ async start() {
942
+ this.ensureDirs();
943
+ // Watch FIRST, then sweep on the same queue the watch feeds: the sweep
944
+ // snapshots the directory and then awaits each handler, so a file a live
945
+ // session writes after the snapshot must already have a watcher to see
946
+ // it — a session dir has no periodic sweep, so a file missed here would
947
+ // wait for a restart. A file seen by both is handled once: the second
948
+ // visit finds it archived.
949
+ if (!this.opts.operator)
950
+ this.attachDirWatch();
951
+ const initial = this.queue.then(() => this.sweep());
952
+ this.queue = initial.catch((err) => this.log(`[commands] sweep failed: ${String(err)}`));
953
+ await initial;
954
+ const interval = this.opts.sweepIntervalMs ?? 15_000;
955
+ if (this.opts.operator && interval > 0) {
956
+ this.sweepTimer = setInterval(() => void this.enqueueSweep(), interval);
957
+ this.sweepTimer.unref?.();
958
+ }
959
+ }
960
+ /** Watch the served directory when it exists. True once watching. */
961
+ attachDirWatch() {
962
+ if (this.watcher)
963
+ return true;
964
+ const ino = inodeAt(this.dir);
965
+ if (ino === null)
966
+ return false;
967
+ this.watcher = watch(this.dir, (_event, filename) => {
968
+ if (!filename) {
969
+ void this.enqueueSweep();
970
+ return;
971
+ }
972
+ const name = filename.toString();
973
+ if (!name.endsWith(".json"))
974
+ return;
975
+ void this.enqueue(join(this.dir, name));
976
+ });
977
+ this.watchedIno = ino;
978
+ this.parentWatcher?.close();
979
+ this.parentWatcher = null;
980
+ return true;
981
+ }
982
+ detachDirWatch() {
983
+ this.watcher?.close();
984
+ this.watcher = null;
985
+ this.watchedIno = null;
986
+ }
987
+ /**
988
+ * Operator dir only: keep the watch on whatever directory is AT the path
989
+ * now. `fs.watch` binds to an inode, and the parent `commands/` is the
990
+ * daemon's — so a session can rename the sealed directory away and put
991
+ * another in its place, and a watcher left on the old inode would see none
992
+ * of the new one's files. Every sweep re-checks; a directory that is gone
993
+ * or replaced drops the old watch, a present one is attached, and an absent
994
+ * one leaves the parent watched for it to appear.
995
+ */
996
+ syncDirWatch() {
997
+ if (this.watcher && inodeAt(this.dir) !== this.watchedIno)
998
+ this.detachDirWatch();
999
+ if (!this.attachDirWatch() && !this.parentWatcher) {
1000
+ this.parentWatcher = watch(dirname(this.dir), (_event, filename) => {
1001
+ if (filename?.toString() === basename(this.dir))
1002
+ void this.enqueueSweep();
1003
+ });
1004
+ }
1005
+ }
1006
+ stop() {
1007
+ this.detachDirWatch();
1008
+ this.parentWatcher?.close();
1009
+ this.parentWatcher = null;
1010
+ if (this.sweepTimer)
1011
+ clearInterval(this.sweepTimer);
1012
+ this.sweepTimer = null;
1013
+ }
1014
+ /** Handle every pending <dir>/*.json once. */
1015
+ async sweep() {
1016
+ this.ensureDirs();
1017
+ if (this.opts.operator)
1018
+ this.syncDirWatch();
1019
+ if (!existsSync(this.dir))
1020
+ return;
1021
+ const names = readdirSync(this.dir, { withFileTypes: true })
1022
+ .filter((e) => e.isFile() && e.name.endsWith(".json"))
1023
+ .map((e) => e.name)
1024
+ .sort();
1025
+ for (const name of names)
1026
+ await this.handleFile(join(this.dir, name));
1027
+ }
1028
+ enqueueSweep() {
1029
+ this.queue = this.queue
1030
+ .then(() => this.sweep())
1031
+ .catch((err) => this.log(`[commands] sweep failed: ${String(err)}`));
1032
+ return this.queue;
1033
+ }
1034
+ enqueue(path) {
1035
+ this.queue = this.queue
1036
+ .then(async () => {
1037
+ await this.handleFile(path);
1038
+ })
1039
+ .catch((err) => this.log(`[commands] ${path}: ${String(err)}`));
1040
+ return this.queue;
1041
+ }
1042
+ /**
1043
+ * Read and parse one command file. `identity` names the file object the
1044
+ * parsed bytes came from (`dev:ino` of the open descriptor) so the operator
1045
+ * seal can be checked against THOSE bytes rather than against whatever
1046
+ * stands at the path a moment earlier or later (the parent `commands/` is
1047
+ * the daemon's, so a session can swap the directory between two syscalls).
1048
+ * `version` is the handled-ledger key of those same bytes
1049
+ * (`operatorCommandVersion`'s), so a file replaced during a parse retry is
1050
+ * recorded as the version that ran, not the one first seen. The descriptor
1051
+ * is stat'ed before AND after the read, and a read the two disagree on (a
1052
+ * writer's write landed in between) is retried like a partial one — so the
1053
+ * version always names the bytes parsed. `text` is those bytes; a failed
1054
+ * read still returns the last text it read, when it read one. The
1055
+ * operator directory is opened without following a symlink.
1056
+ */
1057
+ async readCommandFile(path) {
1058
+ const attempts = this.opts.parseAttempts ?? 3;
1059
+ const delay = this.opts.parseRetryMs ?? 40;
1060
+ const flags = this.opts.operator
1061
+ ? fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW
1062
+ : fsConstants.O_RDONLY;
1063
+ let lastError = "unreadable";
1064
+ let lastVersion = null;
1065
+ let lastText = null;
1066
+ for (let i = 0; i < attempts; i++) {
1067
+ if (!existsSync(path))
1068
+ return { ok: false, reason: "file disappeared", version: null, text: null };
1069
+ try {
1070
+ const fd = openSync(path, flags);
1071
+ let text;
1072
+ let identity;
1073
+ let version;
1074
+ let stable;
1075
+ try {
1076
+ const st = fstatSync(fd, { bigint: true });
1077
+ identity = `${st.dev}:${st.ino}`;
1078
+ text = readFileSync(fd, "utf8");
1079
+ version = commandVersionOf(path, fstatSync(fd, { bigint: true }));
1080
+ stable = version === commandVersionOf(path, st);
1081
+ }
1082
+ finally {
1083
+ closeSync(fd);
1084
+ }
1085
+ lastVersion = version;
1086
+ lastText = text;
1087
+ if (!stable) {
1088
+ lastError = "changed while it was read";
1089
+ }
1090
+ else if (text.trim().length === 0) {
1091
+ lastError = "empty file";
1092
+ }
1093
+ else {
1094
+ return { ok: true, value: JSON.parse(text), identity, version, text };
1095
+ }
1096
+ }
1097
+ catch (err) {
1098
+ // A partially-written file is expected transiently — retry before rejecting.
1099
+ lastError = err.message;
1100
+ }
1101
+ if (i < attempts - 1)
1102
+ await new Promise((r) => setTimeout(r, delay));
1103
+ }
1104
+ return {
1105
+ ok: false,
1106
+ reason: `malformed JSON: ${lastError}`,
1107
+ version: lastVersion,
1108
+ text: lastText,
1109
+ };
1110
+ }
1111
+ /**
1112
+ * Who wrote this file, as far as the daemon can PROVE it. A session
1113
+ * directory's writer is a session. The operator directory's writer is the
1114
+ * operator only when the seal holds; a file there without it is served as a
1115
+ * session's — loudly, with the reason in the ledger — so an unsealed channel
1116
+ * degrades to the bounded side and never to the unbounded one.
1117
+ */
1118
+ issuerFor(path, readIdentity) {
1119
+ const seal = this.opts.operator;
1120
+ if (!seal)
1121
+ return "session";
1122
+ // Checked AFTER the read, and then bound to it: the file at the path must
1123
+ // still be the very object whose bytes were parsed. Otherwise a directory
1124
+ // swapped in for the read and swapped back for the seal check would lend
1125
+ // its bytes the operator's authority.
1126
+ let verdict = operatorSealVerdict(seal, this.dir, path);
1127
+ if (verdict.sealed && readIdentity !== null && fileIdentityAt(path) !== readIdentity)
1128
+ verdict = { sealed: false, reason: "command file replaced while it was read" };
1129
+ if (verdict.sealed)
1130
+ return "operator";
1131
+ this.opts.store.logEvent("operator_channel_unsealed", null, {
1132
+ file: basename(path),
1133
+ reason: verdict.reason,
1134
+ });
1135
+ this.log(`[commands] ${basename(path)}: operator channel NOT sealed (${verdict.reason}) — served as a session's command`);
1136
+ return "session";
1137
+ }
1138
+ /** Validate + execute one command file, writing its result file. */
1139
+ async handleFile(path) {
1140
+ const name = basename(path, ".json");
1141
+ if (this.inFlight.has(path))
1142
+ return null;
1143
+ if (!existsSync(path))
1144
+ return null;
1145
+ // An operator file cannot be archived away (the directory is not ours to
1146
+ // write), so the store remembers what was handled across restarts — by
1147
+ // file VERSION, so a name written again with new content is a new command
1148
+ // and an unchanged file re-seen by the sweep is not.
1149
+ const seen = this.opts.operator ? operatorCommandVersion(path) : null;
1150
+ if (this.opts.operator) {
1151
+ if (seen === null)
1152
+ return null;
1153
+ if (this.opts.store.operatorCommandHandled(seen))
1154
+ return null;
1155
+ }
1156
+ this.inFlight.add(path);
1157
+ try {
1158
+ this.ensureDirs();
1159
+ const read = await this.readCommandFile(path);
1160
+ if (!read.ok && read.reason === "file disappeared")
1161
+ return null;
1162
+ // The ledger records the version whose bytes were parsed — a file
1163
+ // replaced during a parse retry is that new version.
1164
+ const version = this.opts.operator ? (read.version ?? seen) : null;
1165
+ if (version !== null && version !== seen && this.opts.store.operatorCommandHandled(version))
1166
+ return null;
1167
+ if (!read.ok) {
1168
+ const fingerprint = createHash("sha256")
1169
+ .update(JSON.stringify([read.version, read.text, read.reason]))
1170
+ .digest("hex");
1171
+ if (this.parseFailures.get(path) === fingerprint)
1172
+ return null;
1173
+ const result = this.finish(path, name, version, read.text, null, this.issuerFor(path, null), { ok: false, reason: `${read.reason}; file retained for a later completed write` }, true, true);
1174
+ this.parseFailures.set(path, fingerprint);
1175
+ if (this.parseFailures.size > 256) {
1176
+ const oldest = this.parseFailures.keys().next().value;
1177
+ if (oldest !== undefined)
1178
+ this.parseFailures.delete(oldest);
1179
+ }
1180
+ return result;
1181
+ }
1182
+ this.parseFailures.delete(path);
1183
+ const issuer = this.issuerFor(path, read.identity);
1184
+ const validation = validateCommand(read.value, { issuer });
1185
+ if (!validation.ok) {
1186
+ const attempted = read.value?.command;
1187
+ return this.finish(path, name, version, read.text, typeof attempted === "string" ? attempted : null, issuer, { ok: false, reason: validation.reason }, true);
1188
+ }
1189
+ // The Slack belt stops a SESSION mis-filing; a sealed operator file is
1190
+ // the human at the terminal and never rides a Slack turn. An UNSEALED
1191
+ // operator file is a session's and gets the belt like any other.
1192
+ const denial = issuer === "operator" ? null : this.tierCheck(validation.command);
1193
+ if (denial)
1194
+ return this.finish(path, name, version, read.text, validation.command.command, issuer, { ok: false, reason: denial }, true);
1195
+ const deliveryKey = `${this.opts.scope ?? "local"}/${read.identity}/${read.version}`;
1196
+ const receiptKey = createHash("sha256")
1197
+ .update(JSON.stringify([deliveryKey, validation.command]))
1198
+ .digest("hex");
1199
+ const journal = !HANDLER_RECOVERY_COMMANDS.has(validation.command.command);
1200
+ const reserved = !journal || this.opts.store.reserveCommandDelivery(receiptKey);
1201
+ let outcome;
1202
+ if (!reserved) {
1203
+ const receipt = this.opts.store.commandDeliveryReceipt(receiptKey);
1204
+ outcome =
1205
+ receipt.result === null
1206
+ ? {
1207
+ ok: false,
1208
+ reason: "command outcome unknown after interrupted delivery; submit a fresh command to retry",
1209
+ }
1210
+ : JSON.parse(receipt.result);
1211
+ }
1212
+ else {
1213
+ try {
1214
+ outcome = await executeCommand(validation.command, this.opts.deps, {
1215
+ file: this.opts.scope ? `${this.opts.scope}/${name}` : name,
1216
+ delivery_key: deliveryKey,
1217
+ issuer,
1218
+ });
1219
+ if (journal)
1220
+ this.opts.store.settleCommandDelivery(receiptKey, JSON.stringify(outcome));
1221
+ }
1222
+ catch (err) {
1223
+ // The effect may already have happened. Leave its pending receipt
1224
+ // unknown rather than allowing a restart to repeat it.
1225
+ outcome = { ok: false, reason: `handler threw: ${err.message}` };
1226
+ }
1227
+ }
1228
+ return this.finish(path, name, version, read.text, validation.command.command, issuer, outcome, !outcome.ok);
1229
+ }
1230
+ finally {
1231
+ this.inFlight.delete(path);
1232
+ }
1233
+ }
1234
+ /** Rights check between validation and execution. Returns a reason to deny. */
1235
+ tierCheck(command) {
1236
+ const gate = this.opts.tier;
1237
+ if (!gate)
1238
+ return null;
1239
+ let origin = null;
1240
+ try {
1241
+ origin = gate.currentOrigin();
1242
+ }
1243
+ catch (err) {
1244
+ // A gate that cannot answer denies — never fails open.
1245
+ return `tier: origin lookup failed (${err.message})`;
1246
+ }
1247
+ if (gate.dir === "slack") {
1248
+ if (!origin) {
1249
+ return "tier: no live Slack origin for this turn — a tier-checked command needs one (fail closed)";
1250
+ }
1251
+ const auth = authorizeCommand(command, origin);
1252
+ return auth.ok ? null : auth.reason;
1253
+ }
1254
+ // Local dir: only the belt applies. It fires on CAUSE, not on recency —
1255
+ // a daemon-caused turn (`source: "local"`) always passes, and a Slack-caused
1256
+ // one from a non-owner user always refuses, however old the record is.
1257
+ // (`parseOriginRecord` folds the retired tier spelling to "owner", so this
1258
+ // comparison only ever sees the current vocabulary; a Slack origin can
1259
+ // never be "system" — only `writeLocal` stamps that, with source "local".)
1260
+ if (origin && beltRestricts(origin)) {
1261
+ return (`tier: this turn was caused by a Slack message from ${origin.slack_user} (tier ${origin.tier}) — ` +
1262
+ `write '${command.command}' to commands/slack/ so it can be tier-checked`);
1263
+ }
1264
+ return null;
1265
+ }
1266
+ finish(path, name,
1267
+ /** The operator file's version key (`operatorCommandVersion`); null for a session dir. */
1268
+ version,
1269
+ /** The bytes that were read; null when no file was read. */
1270
+ readText, command, issuer, outcome, loud, unsettled = false) {
1271
+ const result = {
1272
+ ...outcome,
1273
+ command,
1274
+ file: basename(path),
1275
+ handled_at: this.now().toISOString(),
1276
+ issuer,
1277
+ };
1278
+ writeFileSync(join(this.resultsDir, `${name}.result.json`), JSON.stringify(result, null, 2));
1279
+ if (outcome.ok) {
1280
+ this.opts.store.logEvent("command_executed", null, { command, file: result.file, issuer });
1281
+ }
1282
+ else {
1283
+ this.opts.store.logEvent("command_rejected", null, {
1284
+ command,
1285
+ file: result.file,
1286
+ issuer,
1287
+ reason: outcome.reason,
1288
+ });
1289
+ }
1290
+ if (loud)
1291
+ this.log(`[commands] REJECTED ${result.file} (${command ?? "?"}): ${outcome.reason ?? "unknown reason"}`);
1292
+ // A parse failure is an observation, not a settled delivery: a slow
1293
+ // writer may complete after eager retries. Keep both session and operator
1294
+ // files eligible, even when metadata did not advance between observations.
1295
+ if (unsettled)
1296
+ return result;
1297
+ // Make sure a daemon restart cannot re-execute the file: session dirs
1298
+ // archive it into processed/; the operator dir is not writable by the
1299
+ // daemon, so the store keeps the ledger instead, keyed by this version.
1300
+ if (version !== null) {
1301
+ this.opts.store.markOperatorCommandHandled(version, result.file);
1302
+ }
1303
+ else {
1304
+ let atPath = null;
1305
+ try {
1306
+ atPath = readFileSync(path, "utf8");
1307
+ }
1308
+ catch {
1309
+ /* gone — nothing to archive or to run */
1310
+ }
1311
+ if (readText !== null && atPath !== null && atPath !== readText) {
1312
+ // A different command took this name while this one ran. It is not
1313
+ // the file this result describes, so it is not archived under it: it
1314
+ // is queued to run next (the watch event it raised found this one in
1315
+ // flight, and a session dir has no periodic sweep to find it later).
1316
+ // The same bytes written again are the same command, already run.
1317
+ this.log(`[commands] ${result.file} was replaced while it ran — running the new one next`);
1318
+ void this.enqueue(path);
1319
+ }
1320
+ else {
1321
+ try {
1322
+ renameSync(path, join(this.processedDir, `${name}.json`));
1323
+ }
1324
+ catch (err) {
1325
+ // A vanished file is fine; any other failure leaves the source
1326
+ // pending, so it is logged with the path and cause.
1327
+ if (err.code !== "ENOENT")
1328
+ this.log(`[commands] could not archive ${path}: ${err.code ?? err.message}`);
1329
+ }
1330
+ }
1331
+ }
1332
+ return result;
1333
+ }
1334
+ }
1335
+ //# sourceMappingURL=commands.js.map