@skrr-ai/cli 0.1.119 → 0.1.121

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 (336) hide show
  1. package/dist/base-command.d.ts +31 -0
  2. package/dist/base-command.js +117 -6
  3. package/dist/commands/agent/message/context.d.ts +1 -0
  4. package/dist/commands/agent/message/context.js +3 -2
  5. package/dist/commands/commands.js +11 -0
  6. package/dist/commands/commitments/action-proposal.d.ts +13 -0
  7. package/dist/commands/commitments/action-proposal.js +39 -0
  8. package/dist/commands/commitments/action-proposals/decide.js +8 -1
  9. package/dist/commands/commitments/action-proposals/execute.js +11 -1
  10. package/dist/commands/commitments/create.js +2 -2
  11. package/dist/commands/commitments/doctor.d.ts +8 -0
  12. package/dist/commands/commitments/doctor.js +22 -0
  13. package/dist/commands/commitments/execute-remediation.js +12 -1
  14. package/dist/commands/commitments/observations.js +2 -2
  15. package/dist/commands/commitments/pack/bind.d.ts +18 -1
  16. package/dist/commands/commitments/pack/bind.js +66 -8
  17. package/dist/commands/commitments/pack/plan.d.ts +4 -1
  18. package/dist/commands/commitments/pack/plan.js +30 -2
  19. package/dist/commands/commitments/update.js +8 -2
  20. package/dist/commands/completion/index.js +1 -1
  21. package/dist/commands/completion/values.js +1 -1
  22. package/dist/commands/computer-use/audio.d.ts +1 -0
  23. package/dist/commands/computer-use/audio.js +1 -0
  24. package/dist/commands/computer-use/browser.d.ts +1 -0
  25. package/dist/commands/computer-use/browser.js +1 -0
  26. package/dist/commands/computer-use/disable.d.ts +1 -0
  27. package/dist/commands/computer-use/disable.js +1 -0
  28. package/dist/commands/computer-use/enable.d.ts +1 -0
  29. package/dist/commands/computer-use/enable.js +1 -0
  30. package/dist/commands/computer-use/index.d.ts +1 -0
  31. package/dist/commands/computer-use/index.js +1 -0
  32. package/dist/commands/computer-use/install.d.ts +1 -0
  33. package/dist/commands/computer-use/install.js +1 -0
  34. package/dist/commands/computer-use/recover.d.ts +1 -0
  35. package/dist/commands/computer-use/recover.js +1 -0
  36. package/dist/commands/computer-use/status.d.ts +1 -0
  37. package/dist/commands/computer-use/status.js +1 -0
  38. package/dist/commands/computer-use/stop.d.ts +1 -0
  39. package/dist/commands/computer-use/stop.js +1 -0
  40. package/dist/commands/context.js +16 -11
  41. package/dist/commands/convos/recovery.js +1 -1
  42. package/dist/commands/daemon/diagnostics.d.ts +9 -0
  43. package/dist/commands/daemon/diagnostics.js +53 -0
  44. package/dist/commands/daemon/index.js +2 -0
  45. package/dist/commands/daemon/maintenance/attest.d.ts +15 -0
  46. package/dist/commands/daemon/maintenance/attest.js +52 -0
  47. package/dist/commands/daemon/maintenance/hold.d.ts +17 -0
  48. package/dist/commands/daemon/maintenance/hold.js +70 -0
  49. package/dist/commands/daemon/maintenance/index.d.ts +12 -0
  50. package/dist/commands/daemon/maintenance/index.js +30 -0
  51. package/dist/commands/daemon/maintenance/resume.d.ts +13 -0
  52. package/dist/commands/daemon/maintenance/resume.js +36 -0
  53. package/dist/commands/daemon/maintenance/status.d.ts +13 -0
  54. package/dist/commands/daemon/maintenance/status.js +36 -0
  55. package/dist/commands/doctor.d.ts +4 -1
  56. package/dist/commands/doctor.js +50 -6
  57. package/dist/commands/followups/plans/cancel-batch.js +6 -3
  58. package/dist/commands/followups/plans/capacity.js +7 -3
  59. package/dist/commands/followups/plans/observability.js +11 -3
  60. package/dist/commands/followups/plans/preferences/set.js +26 -8
  61. package/dist/commands/followups/plans/preferences.js +1 -1
  62. package/dist/commands/followups/plans/show.js +1 -1
  63. package/dist/commands/goals/key-results/assess.d.ts +36 -0
  64. package/dist/commands/goals/key-results/assess.js +110 -0
  65. package/dist/commands/goals/key-results/history.d.ts +30 -0
  66. package/dist/commands/goals/key-results/history.js +104 -0
  67. package/dist/commands/goals/key-results/list.d.ts +5 -4
  68. package/dist/commands/goals/key-results/list.js +12 -6
  69. package/dist/commands/goals/key-results/show.d.ts +4 -2
  70. package/dist/commands/goals/key-results/show.js +25 -4
  71. package/dist/commands/goals/key-results/update.d.ts +4 -0
  72. package/dist/commands/goals/key-results/update.js +18 -24
  73. package/dist/commands/goals/plan-now.js +12 -1
  74. package/dist/commands/goals/planner-compare.d.ts +30 -0
  75. package/dist/commands/goals/planner-compare.js +136 -0
  76. package/dist/commands/machines/dedicated/service/exec.d.ts +3 -0
  77. package/dist/commands/machines/dedicated/service/exec.js +70 -11
  78. package/dist/commands/machines/hosted/destroy.d.ts +1 -0
  79. package/dist/commands/machines/hosted/destroy.js +11 -1
  80. package/dist/commands/machines/hosted/exec.d.ts +1 -0
  81. package/dist/commands/machines/hosted/exec.js +12 -1
  82. package/dist/commands/machines/hosted/index.js +1 -0
  83. package/dist/commands/machines/hosted/operation.d.ts +14 -0
  84. package/dist/commands/machines/hosted/operation.js +51 -0
  85. package/dist/commands/machines/hosted/pull.js +1 -1
  86. package/dist/commands/machines/hosted/start.js +6 -2
  87. package/dist/commands/search/agents.js +5 -1
  88. package/dist/commands/search/all.js +74 -22
  89. package/dist/commands/search/skills.d.ts +1 -0
  90. package/dist/commands/search/skills.js +12 -1
  91. package/dist/commands/search/spaces.d.ts +8 -1
  92. package/dist/commands/search/spaces.js +50 -12
  93. package/dist/commands/search/tasks.d.ts +13 -2
  94. package/dist/commands/search/tasks.js +78 -14
  95. package/dist/commands/search/teams.js +5 -1
  96. package/dist/commands/search/workspace.d.ts +19 -0
  97. package/dist/commands/search/workspace.js +126 -0
  98. package/dist/commands/skills/browse.d.ts +1 -0
  99. package/dist/commands/skills/browse.js +8 -1
  100. package/dist/commands/skills/search.d.ts +1 -0
  101. package/dist/commands/skills/search.js +12 -1
  102. package/dist/commands/spaces/auto-executor/compare.d.ts +18 -0
  103. package/dist/commands/spaces/auto-executor/compare.js +139 -0
  104. package/dist/commands/spaces/auto-executor/runs.d.ts +3 -0
  105. package/dist/commands/spaces/auto-executor/runs.js +13 -1
  106. package/dist/commands/spaces/charter/create-from-description.d.ts +21 -0
  107. package/dist/commands/spaces/charter/create-from-description.js +77 -0
  108. package/dist/commands/spaces/charter/show.d.ts +22 -0
  109. package/dist/commands/spaces/charter/show.js +61 -0
  110. package/dist/commands/spaces/heartbeats/list.d.ts +1 -16
  111. package/dist/commands/spaces/heartbeats/list.js +13 -0
  112. package/dist/commands/spaces/install-template.js +3 -0
  113. package/dist/commands/spaces/list.d.ts +1 -0
  114. package/dist/commands/spaces/list.js +12 -3
  115. package/dist/commands/spaces/loops/migrate.d.ts +32 -0
  116. package/dist/commands/spaces/loops/migrate.js +103 -0
  117. package/dist/commands/spaces/loops/release.d.ts +14 -0
  118. package/dist/commands/spaces/loops/release.js +59 -0
  119. package/dist/commands/spaces/notebook.d.ts +25 -0
  120. package/dist/commands/spaces/notebook.js +75 -0
  121. package/dist/commands/spaces/proposals/discard.d.ts +17 -0
  122. package/dist/commands/spaces/proposals/discard.js +17 -0
  123. package/dist/commands/spaces/proposals/keep.d.ts +17 -0
  124. package/dist/commands/spaces/proposals/keep.js +17 -0
  125. package/dist/commands/spaces/proposals/list.d.ts +24 -0
  126. package/dist/commands/spaces/proposals/list.js +89 -0
  127. package/dist/commands/spaces/search.d.ts +1 -0
  128. package/dist/commands/spaces/search.js +9 -0
  129. package/dist/commands/spaces/skills/propose.d.ts +29 -0
  130. package/dist/commands/spaces/skills/propose.js +82 -0
  131. package/dist/commands/spaces/templates/fork.js +3 -0
  132. package/dist/commands/spaces/triggers/show.js +6 -0
  133. package/dist/commands/spaces/workflows/retry.d.ts +1 -0
  134. package/dist/commands/spaces/workflows/retry.js +8 -1
  135. package/dist/commands/spaces/workflows/triggers/create.js +3 -2
  136. package/dist/commands/spaces/workflows/triggers/fire.js +4 -0
  137. package/dist/commands/spaces/workflows/triggers/update.js +2 -1
  138. package/dist/commands/spaces/write-policy/set.d.ts +22 -0
  139. package/dist/commands/spaces/write-policy/set.js +82 -0
  140. package/dist/commands/spaces/write-policy/show.d.ts +19 -0
  141. package/dist/commands/spaces/write-policy/show.js +49 -0
  142. package/dist/commands/store/artifacts/receipt.d.ts +14 -0
  143. package/dist/commands/store/artifacts/receipt.js +34 -0
  144. package/dist/commands/store/artifacts/release.d.ts +2 -0
  145. package/dist/commands/store/artifacts/release.js +11 -0
  146. package/dist/commands/store/governance/publish-release.d.ts +2 -0
  147. package/dist/commands/store/governance/publish-release.js +30 -1
  148. package/dist/commands/store/governance/release-receipt.d.ts +12 -0
  149. package/dist/commands/store/governance/release-receipt.js +32 -0
  150. package/dist/commands/tasks/approvals/list.d.ts +11 -5
  151. package/dist/commands/tasks/approvals/list.js +13 -6
  152. package/dist/commands/tasks/attachments/facts.d.ts +12 -0
  153. package/dist/commands/tasks/attachments/facts.js +29 -0
  154. package/dist/commands/tasks/attachments/upload.d.ts +1 -0
  155. package/dist/commands/tasks/attachments/upload.js +16 -0
  156. package/dist/commands/tasks/bulk-update.js +35 -1
  157. package/dist/commands/tasks/context.d.ts +15 -0
  158. package/dist/commands/tasks/context.js +69 -0
  159. package/dist/commands/tasks/copy.d.ts +18 -0
  160. package/dist/commands/tasks/copy.js +85 -0
  161. package/dist/commands/tasks/create-tree.js +97 -23
  162. package/dist/commands/tasks/create.js +9 -1
  163. package/dist/commands/tasks/deliverable/add.js +20 -10
  164. package/dist/commands/tasks/move.js +8 -1
  165. package/dist/commands/tasks/operations/show.d.ts +16 -0
  166. package/dist/commands/tasks/operations/show.js +98 -0
  167. package/dist/commands/tasks/operations/wait.d.ts +22 -0
  168. package/dist/commands/tasks/operations/wait.js +114 -0
  169. package/dist/commands/tasks/preflight.d.ts +1 -0
  170. package/dist/commands/tasks/preflight.js +11 -1
  171. package/dist/commands/tasks/rearm-recurrence.js +4 -0
  172. package/dist/commands/tasks/result/show.js +22 -0
  173. package/dist/commands/tasks/result/submit.d.ts +1 -0
  174. package/dist/commands/tasks/result/submit.js +3 -0
  175. package/dist/commands/tasks/retry-receipt.d.ts +15 -0
  176. package/dist/commands/tasks/retry-receipt.js +89 -0
  177. package/dist/commands/tasks/retry-review.d.ts +16 -0
  178. package/dist/commands/tasks/retry-review.js +66 -0
  179. package/dist/commands/tasks/retry.d.ts +4 -8
  180. package/dist/commands/tasks/retry.js +34 -68
  181. package/dist/commands/tasks/runs.d.ts +21 -0
  182. package/dist/commands/tasks/runs.js +95 -0
  183. package/dist/commands/tasks/search.d.ts +1 -0
  184. package/dist/commands/tasks/search.js +9 -0
  185. package/dist/commands/tasks/show.d.ts +2 -0
  186. package/dist/commands/tasks/show.js +15 -1
  187. package/dist/commands/tasks/start-receipt.d.ts +15 -0
  188. package/dist/commands/tasks/start-receipt.js +95 -0
  189. package/dist/commands/tasks/start.d.ts +4 -0
  190. package/dist/commands/tasks/start.js +49 -3
  191. package/dist/commands/tasks/update.js +3 -0
  192. package/dist/commands/tasks/upsert.js +17 -3
  193. package/dist/commands/tasks/watch.d.ts +3 -0
  194. package/dist/commands/tasks/watch.js +85 -20
  195. package/dist/commands/wiki/history.d.ts +33 -0
  196. package/dist/commands/wiki/history.js +134 -0
  197. package/dist/commands/wiki/ls.d.ts +2 -0
  198. package/dist/commands/wiki/ls.js +8 -0
  199. package/dist/commands/wiki/update.d.ts +3 -1
  200. package/dist/commands/wiki/update.js +24 -2
  201. package/dist/commands/wiki/write.d.ts +22 -0
  202. package/dist/commands/wiki/write.js +144 -6
  203. package/dist/generated/agent-guides.json +81 -81
  204. package/dist/generated/managed-instruction-guides.json +1 -1
  205. package/dist/lib/agent-guides.d.ts +9 -0
  206. package/dist/lib/agent-guides.js +23 -1
  207. package/dist/lib/agentic-stream.d.ts +5 -0
  208. package/dist/lib/agentic-stream.js +122 -3
  209. package/dist/lib/api-error-reason.d.ts +2 -0
  210. package/dist/lib/api-error-reason.js +14 -0
  211. package/dist/lib/api-fetch.js +3 -0
  212. package/dist/lib/auth-diagnostics.d.ts +39 -0
  213. package/dist/lib/auth-diagnostics.js +71 -0
  214. package/dist/lib/cli-client-identity.d.ts +6 -0
  215. package/dist/lib/cli-client-identity.js +16 -0
  216. package/dist/lib/cli-installation-diagnostics.d.ts +52 -0
  217. package/dist/lib/cli-installation-diagnostics.js +254 -0
  218. package/dist/lib/command-hygiene.d.ts +45 -0
  219. package/dist/lib/command-hygiene.js +413 -0
  220. package/dist/lib/commitments.d.ts +64 -3
  221. package/dist/lib/commitments.js +34 -6
  222. package/dist/lib/completion-index.d.ts +16 -4
  223. package/dist/lib/completion-index.js +50 -14
  224. package/dist/lib/config.js +7 -1
  225. package/dist/lib/daemon-maintenance-command.d.ts +22 -0
  226. package/dist/lib/daemon-maintenance-command.js +39 -0
  227. package/dist/lib/daemon-maintenance.d.ts +275 -0
  228. package/dist/lib/daemon-maintenance.js +1035 -0
  229. package/dist/lib/dedicated-service.d.ts +3 -2
  230. package/dist/lib/dedicated-service.js +11 -3
  231. package/dist/lib/governed-store.d.ts +8 -0
  232. package/dist/lib/governed-store.js +5 -0
  233. package/dist/lib/hosted-machines.d.ts +33 -2
  234. package/dist/lib/hosted-machines.js +19 -1
  235. package/dist/lib/json-output.d.ts +1 -1
  236. package/dist/lib/machines.d.ts +9 -1
  237. package/dist/lib/machines.js +15 -1
  238. package/dist/lib/node-adapter.js +13 -0
  239. package/dist/lib/record-output.d.ts +22 -0
  240. package/dist/lib/record-output.js +78 -0
  241. package/dist/lib/roles.d.ts +3 -3
  242. package/dist/lib/scoped-memory-command.d.ts +18 -0
  243. package/dist/lib/scoped-memory-command.js +53 -2
  244. package/dist/lib/search-display.d.ts +7 -0
  245. package/dist/lib/search-display.js +31 -0
  246. package/dist/lib/skill-store-pagination.d.ts +8 -0
  247. package/dist/lib/skill-store-pagination.js +35 -0
  248. package/dist/lib/space-charter.d.ts +49 -0
  249. package/dist/lib/space-charter.js +78 -0
  250. package/dist/lib/space-loop-migration.d.ts +18 -0
  251. package/dist/lib/space-loop-migration.js +128 -0
  252. package/dist/lib/space-notebook.d.ts +22 -0
  253. package/dist/lib/space-notebook.js +140 -0
  254. package/dist/lib/space-proposal-decision-command.d.ts +36 -0
  255. package/dist/lib/space-proposal-decision-command.js +108 -0
  256. package/dist/lib/space-proposals.d.ts +44 -0
  257. package/dist/lib/space-proposals.js +186 -0
  258. package/dist/lib/space-templates.d.ts +8 -0
  259. package/dist/lib/space-templates.js +17 -0
  260. package/dist/lib/space-write-policy.d.ts +24 -0
  261. package/dist/lib/space-write-policy.js +103 -0
  262. package/dist/lib/task-agent-context-capabilities.d.ts +10 -0
  263. package/dist/lib/task-agent-context-capabilities.js +22 -0
  264. package/dist/lib/task-agent-context-compatibility.d.ts +9 -0
  265. package/dist/lib/task-agent-context-compatibility.js +30 -0
  266. package/dist/lib/task-asset-upload.d.ts +22 -0
  267. package/dist/lib/task-asset-upload.js +257 -29
  268. package/dist/lib/task-batch.d.ts +15 -1
  269. package/dist/lib/task-batch.js +35 -2
  270. package/dist/lib/task-operation-watch.d.ts +66 -0
  271. package/dist/lib/task-operation-watch.js +159 -0
  272. package/dist/lib/task-resolver.d.ts +1 -1
  273. package/dist/lib/task-resolver.js +4 -3
  274. package/dist/lib/task-work.d.ts +1 -1
  275. package/dist/lib/task-work.js +35 -1
  276. package/dist/lib/task-write-attribution.d.ts +14 -0
  277. package/dist/lib/task-write-attribution.js +35 -2
  278. package/dist/lib/triggers.d.ts +11 -0
  279. package/dist/lib/triggers.js +7 -0
  280. package/dist/lib/wiki-update.d.ts +1 -0
  281. package/dist/lib/wiki-update.js +10 -1
  282. package/dist/lib/wiki.js +4 -1
  283. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +9 -1
  284. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +31 -15
  285. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.d.ts +10 -0
  286. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/credentialEnvelopeBridge.js +147 -0
  287. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.d.ts +20 -0
  288. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonConsentProof.js +72 -0
  289. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +1 -0
  290. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +13 -7
  291. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.d.ts +21 -0
  292. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/desktopDaemonProof.js +96 -0
  293. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +2 -3
  294. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +102 -20
  295. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.d.ts +370 -0
  296. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.js +315 -0
  297. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.d.ts +27 -0
  298. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineReservation.js +161 -0
  299. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.d.ts +26 -0
  300. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/independentMachineScopes.js +41 -0
  301. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
  302. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +15 -0
  303. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/kek/macos.js +20 -4
  304. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.d.ts +178 -0
  305. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.js +262 -0
  306. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.d.ts +51 -0
  307. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.js +106 -0
  308. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +9 -1
  309. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +32 -5
  310. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.d.ts +10 -0
  311. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/credentialEnvelopeBridge.js +172 -1
  312. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.d.ts +20 -0
  313. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonConsentProof.js +43 -0
  314. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +1 -0
  315. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +33 -14
  316. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.d.ts +21 -0
  317. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/desktopDaemonProof.js +62 -0
  318. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +2 -3
  319. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +133 -25
  320. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.d.ts +370 -0
  321. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.js +404 -0
  322. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.d.ts +27 -0
  323. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineReservation.js +132 -0
  324. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.d.ts +26 -0
  325. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/independentMachineScopes.js +15 -0
  326. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
  327. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -1
  328. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/kek/macos.js +28 -15
  329. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.d.ts +178 -0
  330. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.js +342 -0
  331. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.d.ts +51 -0
  332. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.js +138 -0
  333. package/dist/node_modules/@skrr-ai/auth-core/package.json +51 -1
  334. package/dist/node_modules/@skrr-ai/data-provider/index.js +27181 -20293
  335. package/oclif.manifest.json +5745 -1813
  336. package/package.json +20 -2
@@ -31,9 +31,10 @@
31
31
  */
32
32
  import fs from 'node:fs';
33
33
  import path from 'node:path';
34
+ import lockfile from 'proper-lockfile';
34
35
  import { buildProof as buildDeviceProof, generateDeviceKeyPair, jwkThumbprint, } from './deviceKey.js';
35
36
  import { hasCredEnvelopePathConflict, isCredEnvelopeActive, maybeDecryptOnRead, splitKeyConvergencePolicy, wrapIfActiveOrPassthrough, } from './credentialEnvelopeBridge.js';
36
- import { consolidateProfileState, profileStateFilePathsToClear, resolveProfileStateDir, } from './profileStateDir.js';
37
+ import { consolidateProfileState, profileStateFilePathsToClear, profileStateDir, resolveProfileStateDir, } from './profileStateDir.js';
37
38
  import { emitAuthTelemetry, getAuthLogger } from './runtime.js';
38
39
  const PUBLIC_KEY_FILENAME = 'device-public-key.jwk';
39
40
  const PRIVATE_KEY_FILENAME = 'device-private-key';
@@ -56,6 +57,34 @@ const DEVICE_IDENTITY_FILES = [
56
57
  // so every CLI command does not repeat the same guaranteed 404.
57
58
  const DAEMON_NOT_FOUND_BACKOFF_MS = 15 * 60 * 1000;
58
59
  let _state = { kind: 'uninit' };
60
+ let _initializing = null;
61
+ /**
62
+ * The CLI, daemon, and desktop's short-lived signer resolve the same canonical
63
+ * profile directory even when an older private key still lives in the nested
64
+ * legacy directory. Serialize the entire read/consolidate/create transaction;
65
+ * otherwise two first starts can each retain a different key in memory.
66
+ *
67
+ * Never proceed unlocked. The long stale interval favors preserving a pinned
68
+ * identity if a process is paused; a contended caller fails and can retry.
69
+ * Versions that predate this lock do not participate and still require an
70
+ * orderly upgrade before using the new-profile authorization path.
71
+ */
72
+ async function withDeviceIdentityLock(profile, body) {
73
+ const dir = profileStateDir(profile);
74
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
75
+ const release = await lockfile.lock(dir, {
76
+ realpath: false,
77
+ lockfilePath: path.join(dir, '.device-identity.lock'),
78
+ stale: 10 * 60 * 1000,
79
+ retries: { retries: 20, minTimeout: 100, maxTimeout: 500, factor: 1.25 },
80
+ });
81
+ try {
82
+ return await body();
83
+ }
84
+ finally {
85
+ await release();
86
+ }
87
+ }
59
88
  /** @internal Test seam — force-set state. */
60
89
  export function __setStateForTest(next) {
61
90
  const prev = _state;
@@ -72,6 +101,11 @@ export function __getStateForTest() {
72
101
  * that decides which copy the group is read from.
73
102
  */
74
103
  function profileConfigDir(profile) {
104
+ // A reserved independent profile has a server-pinned owner and device key.
105
+ // An older nested copy is not authority to replace or shadow its canonical
106
+ // key, even when generic profile-convergence policy favors that copy.
107
+ if (/^desktop-bg-[a-f0-9]{32}$/i.test(profile))
108
+ return profileStateDir(profile);
75
109
  return resolveProfileStateDir(profile, PRIVATE_KEY_FILENAME).dir;
76
110
  }
77
111
  function publicKeyPath(profile) {
@@ -177,6 +211,50 @@ function persistKeypair(profile, kp) {
177
211
  });
178
212
  return { envelopeProtected };
179
213
  }
214
+ /** Reserved identities are read without following links or accepting an
215
+ * unbounded, shared, unsealed or concurrently replaced key file. */
216
+ function readIndependentIdentityFile(file, sealed) {
217
+ if (typeof fs.constants.O_NOFOLLOW !== 'number' || typeof fs.constants.O_NONBLOCK !== 'number')
218
+ return null;
219
+ let fd;
220
+ try {
221
+ fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
222
+ }
223
+ catch {
224
+ return null;
225
+ }
226
+ try {
227
+ const before = fs.fstatSync(fd);
228
+ if (!before.isFile() || before.nlink !== 1 || before.size > 16_384 || (before.mode & 0o077) !== 0 ||
229
+ (typeof process.getuid === 'function' && before.uid !== process.getuid()))
230
+ return null;
231
+ const bytes = Buffer.alloc(before.size);
232
+ let offset = 0;
233
+ while (offset < bytes.length) {
234
+ const count = fs.readSync(fd, bytes, offset, bytes.length - offset, offset);
235
+ if (count <= 0)
236
+ return null;
237
+ offset += count;
238
+ }
239
+ const after = fs.fstatSync(fd);
240
+ const current = fs.lstatSync(file);
241
+ if (!current.isFile() || current.isSymbolicLink() || current.dev !== before.dev || current.ino !== before.ino ||
242
+ current.size !== before.size || current.nlink !== before.nlink || current.mode !== before.mode ||
243
+ current.uid !== before.uid || current.mtimeMs !== before.mtimeMs || current.ctimeMs !== before.ctimeMs ||
244
+ before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.nlink !== after.nlink ||
245
+ before.mode !== after.mode || before.uid !== after.uid || before.mtimeMs !== after.mtimeMs ||
246
+ before.ctimeMs !== after.ctimeMs)
247
+ return null;
248
+ const value = bytes.toString('utf8');
249
+ return sealed && !value.startsWith('osk-env1:') ? null : value;
250
+ }
251
+ catch {
252
+ return null;
253
+ }
254
+ finally {
255
+ fs.closeSync(fd);
256
+ }
257
+ }
180
258
  /**
181
259
  * Load a previously-persisted keypair. Returns null on missing files,
182
260
  * decrypt failure, public/private mismatch (file-swap defense), or
@@ -185,11 +263,13 @@ function persistKeypair(profile, kp) {
185
263
  function loadKeypair(profile) {
186
264
  const pubPath = publicKeyPath(profile);
187
265
  const privPath = privateKeyPath(profile);
188
- if (!fs.existsSync(pubPath) || !fs.existsSync(privPath)) {
266
+ const independent = /^desktop-bg-[a-f0-9]{32}$/i.test(profile);
267
+ if (!independent && (!fs.existsSync(pubPath) || !fs.existsSync(privPath)))
268
+ return null;
269
+ const pubRaw = independent ? readIndependentIdentityFile(pubPath, false) : fs.readFileSync(pubPath, 'utf-8');
270
+ const privRaw = independent ? readIndependentIdentityFile(privPath, true) : fs.readFileSync(privPath, 'utf-8');
271
+ if (pubRaw === null || privRaw === null)
189
272
  return null;
190
- }
191
- const pubRaw = fs.readFileSync(pubPath, 'utf-8');
192
- const privRaw = fs.readFileSync(privPath, 'utf-8');
193
273
  let publicJwk;
194
274
  let storedThumbprint;
195
275
  try {
@@ -348,23 +428,47 @@ async function consolidateDeviceIdentity(profile) {
348
428
  * `createIfMissing: false` stops before step 2 (disabled+no_stored_key) and
349
429
  * never replaces anything: for a short-lived process that must only READ the
350
430
  * daemon's key while the daemon itself may be creating it (OSK-15990's
351
- * `skrrd device-proof`). The persist below is not an exclusive create, so a
352
- * second creator racing the daemon could leave the daemon holding one key in
353
- * memory and the disk another.
431
+ * `skrrd device-proof`). Readers and writers both take the profile lock so
432
+ * they cannot observe an incomplete write from another updated process.
354
433
  */
355
434
  export async function initDeviceIdentity(profile, options = {}) {
435
+ if (_initializing)
436
+ return _initializing;
356
437
  if (_state.kind !== 'uninit')
357
438
  return;
358
- if (typeof profile !== 'string' || profile.length === 0) {
439
+ if (typeof profile !== 'string' || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(profile)) {
359
440
  _state = { kind: 'disabled', reason: 'init_failed' };
360
- emit('init_failed', false, { reason: 'empty_profile' });
441
+ emit('init_failed', false, { reason: 'invalid_profile' });
361
442
  return;
362
443
  }
444
+ const job = withDeviceIdentityLock(profile, async () => {
445
+ if (_state.kind === 'uninit')
446
+ await initDeviceIdentityLocked(profile, options);
447
+ }).catch((err) => {
448
+ _state = { kind: 'disabled', reason: 'init_failed' };
449
+ emit('init_failed', false, {
450
+ profile,
451
+ reason: 'identity_lock_unavailable',
452
+ message: err instanceof Error ? err.message : 'unknown',
453
+ });
454
+ });
455
+ _initializing = job;
456
+ try {
457
+ await job;
458
+ }
459
+ finally {
460
+ if (_initializing === job)
461
+ _initializing = null;
462
+ }
463
+ }
464
+ async function initDeviceIdentityLocked(profile, options = {}) {
363
465
  // OSK-10314 — converge the two directories this profile may have before
364
466
  // resolving any of the four files. Must run after `initCredEnvelope`, which
365
467
  // both consumers already guarantee, because the private key's readability
366
468
  // depends on which DEK ended up active.
367
- await consolidateDeviceIdentity(profile);
469
+ const independent = /^desktop-bg-[a-f0-9]{32}$/i.test(profile);
470
+ if (!independent)
471
+ await consolidateDeviceIdentity(profile);
368
472
  let keypair = null;
369
473
  try {
370
474
  keypair = loadKeypair(profile);
@@ -394,12 +498,12 @@ export async function initDeviceIdentity(profile, options = {}) {
394
498
  // DEVICE_KEY_MISMATCH. Only an explicit interactive login may replace it;
395
499
  // that login proves the new key and re-pins it before issuing credentials.
396
500
  const hasStoredKey = storedKeyMaterialExists(profile);
397
- if (hasStoredKey && !options.recoverUnreadable) {
501
+ if (hasStoredKey && (!options.recoverUnreadable || independent)) {
398
502
  _state = { kind: 'disabled', reason: 'unreadable_key' };
399
503
  emit('recovery_required', false, { profile, reason: 'stored_key_unreadable' });
400
504
  return;
401
505
  }
402
- if (options.createIfMissing === false) {
506
+ if (options.createIfMissing === false || (independent && options.createIfMissing !== true)) {
403
507
  _state = { kind: 'disabled', reason: 'no_stored_key' };
404
508
  emit('no_stored_key', false, { profile });
405
509
  return;
@@ -448,25 +552,29 @@ export async function initDeviceIdentity(profile, options = {}) {
448
552
  * the daemon's pinned identity.
449
553
  */
450
554
  export async function resetDeviceIdentity(opts) {
555
+ if (_initializing)
556
+ await _initializing;
451
557
  if (opts?.clearOnDisk && opts.profile) {
452
558
  // Every location, not just the resolved one. A reset that cleared only the
453
559
  // canonical copy would leave the pre-consolidation copy for the next init to
454
560
  // adopt — a deleted device key that reappears.
455
561
  const targets = [PUBLIC_KEY_FILENAME, PRIVATE_KEY_FILENAME].flatMap((name) => profileStateFilePathsToClear(opts.profile, name));
456
- for (const p of targets) {
457
- try {
458
- fs.unlinkSync(p);
459
- }
460
- catch (err) {
461
- if (err.code !== 'ENOENT') {
462
- emit('clear_failed', false, {
463
- profile: opts.profile,
464
- file: path.basename(p),
465
- message: err?.message ?? 'unknown',
466
- });
562
+ await withDeviceIdentityLock(opts.profile, async () => {
563
+ for (const p of targets) {
564
+ try {
565
+ fs.unlinkSync(p);
566
+ }
567
+ catch (err) {
568
+ if (err.code !== 'ENOENT') {
569
+ emit('clear_failed', false, {
570
+ profile: opts.profile,
571
+ file: path.basename(p),
572
+ message: err?.message ?? 'unknown',
573
+ });
574
+ }
467
575
  }
468
576
  }
469
- }
577
+ });
470
578
  }
471
579
  _state = { kind: 'uninit' };
472
580
  }
@@ -0,0 +1,370 @@
1
+ /**
2
+ * The pure core of a GUIDED update (OSK-19107, Space OSK-S-524, tasks M51 to M53):
3
+ * hold the machine, wait for it to be quiet, update, verify, and give the machine
4
+ * back. The Desktop app and a standalone `skrr` install both run it, so what they
5
+ * decide on, and the words they use for what is in the way, cannot drift apart.
6
+ *
7
+ * This file is DECISIONS ONLY. It performs no I/O, reads no clock and holds no
8
+ * state, so every rule below is a table test. The orchestration (a persisted flow
9
+ * record, polling, the installer, the supervisor) lives with each caller, because
10
+ * that is the part that differs between a Desktop that quits mid-flow and a CLI that
11
+ * runs in the foreground.
12
+ *
13
+ * ## Why these rules (each names the failure it prevents)
14
+ *
15
+ * - **`prepare` is a probe that briefly refuses admitted work.** While it runs the
16
+ * daemon refuses admitted tool calls with an infra-class error, and a tool-proxy Run
17
+ * that sees one drops its tools to the Hosted Machine for good. So a guided flow
18
+ * calls it ONLY when the server's reading says there is nothing admitted to refuse
19
+ * ({@link prepareBasis}), never to find out whether there is.
20
+ * - **`unknown` is an answer.** A source nobody could read is never zero and never
21
+ * ready. {@link noServerVeto} is deliberately strict: every server source must have
22
+ * been read, and none may list active or unproven work.
23
+ * - **A server source can only veto.** Only the daemon's own positively announced
24
+ * report grants `ready`; the owner's attestation and the daemon's own `prepare`
25
+ * (labelled `no_veto`, D38) are the two other bases, and each is labelled as what
26
+ * it is wherever it is shown.
27
+ * - **Blockers are shown in the words of whoever knows them.** The daemon's blocker
28
+ * `reason` and the server's holder `label` are the vocabulary; nothing here
29
+ * re-spells them. They are bounded and never a raw path, because a diagnostic can
30
+ * carry one.
31
+ * - **A new PID, an exit code or a cloud connection is not a verified update.**
32
+ * {@link verifyReplacement} requires the daemon's own `verified`, from a process
33
+ * other than the one that was replaced, for the build that was asked for, and the
34
+ * same machine identity afterwards. A different identity is the reinstall case: the
35
+ * hold is left in place and shown, never resumed.
36
+ *
37
+ * The shapes are STRUCTURAL SUBSETS of `DaemonMaintenanceView` in
38
+ * `@skrr-ai/data-provider` (`daemonMaintenance.ts`), which is the one vocabulary.
39
+ * This package cannot import it (it sits below it), so `guidedUpdate.test.ts` reads
40
+ * that file and fails when a list mirrored here drifts from it.
41
+ */
42
+ export type GuidedHolderClass = 'active' | 'pending_delivery' | 'unproven' | 'forensic';
43
+ export interface GuidedHolder {
44
+ kind: string;
45
+ class: GuidedHolderClass | string;
46
+ ref?: string;
47
+ label?: string;
48
+ since?: string | null;
49
+ detail?: string;
50
+ }
51
+ export interface GuidedSourceReading {
52
+ source: string;
53
+ read: 'ok' | 'unreadable' | 'not_announced' | string;
54
+ cause?: string;
55
+ readAt?: string;
56
+ total?: number;
57
+ holders?: GuidedHolder[];
58
+ truncated?: boolean;
59
+ }
60
+ export interface GuidedViewLike {
61
+ runtimeKey?: string;
62
+ hold: {
63
+ generation: number;
64
+ lifecycle: 'held' | 'released' | string;
65
+ reason?: string;
66
+ requestedAt?: string;
67
+ heldBy?: {
68
+ kind?: string;
69
+ id?: string;
70
+ name?: string;
71
+ };
72
+ attestation?: {
73
+ at?: string;
74
+ by?: string;
75
+ } | null;
76
+ } | null;
77
+ reading: {
78
+ state: 'draining' | 'ready' | 'unknown' | string;
79
+ cause: string | null;
80
+ readAt?: string;
81
+ sources?: GuidedSourceReading[];
82
+ } | null;
83
+ journey: {
84
+ phase: string;
85
+ modifiers?: string[];
86
+ cause?: string | null;
87
+ controls?: string[];
88
+ };
89
+ }
90
+ /** The server sources that can only veto. `daemon_report` is the only one that grants. */
91
+ export declare const GUIDED_SERVER_SOURCES: readonly ["claims", "receipts", "sessions", "runs", "leases", "custody"];
92
+ /** The refusals the owner API names, mirrored from `DAEMON_MAINTENANCE_REFUSALS`. */
93
+ export declare const GUIDED_REFUSALS: readonly ["managed_machine", "no_stable_identity", "not_found", "ambiguous", "generation_conflict", "not_held", "already_held", "attestation_not_applicable", "not_owner", "held_by_other"];
94
+ export type GuidedRefusal = (typeof GUIDED_REFUSALS)[number];
95
+ export declare function isGuidedRefusal(value: unknown): value is GuidedRefusal;
96
+ /** The daemon handoff phases, mirrored from `daemon/src/update/handoff.ts`. */
97
+ export declare const GUIDED_HANDOFF_PHASES: readonly ["ready", "deferred", "reconnecting", "verified", "failed", "rolled-back"];
98
+ /**
99
+ * What a guided flow may do with the server's answer. The flow reads `reading` and
100
+ * the fence, never the Journey's phase alone: a Journey says `back_held` for any
101
+ * process that registered after the fence, and that masks the reading it would
102
+ * otherwise carry.
103
+ */
104
+ export type Readiness =
105
+ /** No live hold on this machine (it was released, or never existed). */
106
+ {
107
+ kind: 'not_held';
108
+ }
109
+ /** A live hold, but not the generation this flow took. Never adopted. */
110
+ | {
111
+ kind: 'generation_changed';
112
+ currentGeneration: number;
113
+ }
114
+ /** The fence is not confirmed on every row, so no reading means anything yet. */
115
+ | {
116
+ kind: 'pausing';
117
+ }
118
+ /** Nothing admitted is running: the daemon's report said so, or the owner vouched. */
119
+ | {
120
+ kind: 'ready';
121
+ basis: 'evidence' | 'attested';
122
+ }
123
+ /** The daemon cannot report. No server source vetoes. Only the daemon's own check may decide. */
124
+ | {
125
+ kind: 'no_report';
126
+ } | {
127
+ kind: 'draining';
128
+ } | {
129
+ kind: 'unknown';
130
+ cause: string | null;
131
+ };
132
+ /**
133
+ * Whether the server holds a reading that nothing vetoes, for a daemon that cannot
134
+ * report (D38). Strict on purpose: a source that is absent from the list, unreadable,
135
+ * truncated, or that names active or unproven work, is a veto. `daemon_report` must
136
+ * be `not_announced`: any other answer means the reading is not the
137
+ * `no_inventory_capability` case this is for.
138
+ */
139
+ export declare function noServerVeto(sources: readonly GuidedSourceReading[] | undefined): boolean;
140
+ /**
141
+ * Read the owner API's view for a flow that took `generation`.
142
+ *
143
+ * Order matters and is the rule set (each is a test):
144
+ * 1. No live hold: `not_held`. A different live generation: `generation_changed`.
145
+ * 2. The fence is not confirmed (`pausing`): nothing below it means anything.
146
+ * 3. The reading is `ready` (the daemon's own report): `ready` by evidence.
147
+ * 4. `draining`: work is running. Evidence, never overridden.
148
+ * 5. `unknown`: ready by attestation if the Journey says the owner vouched for this
149
+ * cause; `no_report` if the daemon cannot report and no source vetoes; else
150
+ * `unknown`. No reading at all is `unknown (source_unreadable)`, never ready.
151
+ */
152
+ export declare function evaluateReadiness(view: GuidedViewLike | null | undefined, expectedGeneration: number | null): Readiness;
153
+ /** How a `prepare` is justified. Labelled on every surface as exactly this. */
154
+ export type PrepareBasis = 'evidence' | 'attested' | 'no_veto';
155
+ /**
156
+ * Whether a guided flow may call the daemon's `prepare`, and on what basis. `null`
157
+ * means NO: `prepare` is never a way to find out. A legacy daemon never gets one:
158
+ * it has no endpoint, and its restart is the owner's attested decision, not a probe.
159
+ */
160
+ export declare function prepareBasis(readiness: Readiness, opts: {
161
+ legacy: boolean;
162
+ }): PrepareBasis | null;
163
+ /**
164
+ * Whether a legacy daemon (no handoff endpoint) may be restarted by the supervisor.
165
+ * Only on the owner's attestation: the daemon cannot report what it is doing and a
166
+ * SIGTERM ends its wrappers, so nothing but the owner's word can vouch for it.
167
+ */
168
+ export declare function legacyRestartAllowed(readiness: Readiness): boolean;
169
+ export interface DaemonBlockerLike {
170
+ kind: string;
171
+ id?: string;
172
+ reason?: string;
173
+ }
174
+ export interface DesktopWorkLike {
175
+ localSessions: number;
176
+ agentBrowserWindows: number;
177
+ }
178
+ export type OwnerBlockerSource = 'server' | 'daemon' | 'desktop';
179
+ export interface OwnerBlocker {
180
+ source: OwnerBlockerSource;
181
+ /** The holder or blocker kind as its owner spelled it, or a `desktop_*` id for the app's own work. */
182
+ kind: string;
183
+ /** The server's label, or the daemon's reason, verbatim after bounding. Absent when it was unsafe to show. */
184
+ text?: string;
185
+ /** A server label for a daemon blocker the server holds under the same ref. */
186
+ label?: string;
187
+ /** A bounded identifier (a session or request id), never a path. */
188
+ id?: string;
189
+ since?: string | null;
190
+ /** For the app's own work: how many. */
191
+ count?: number;
192
+ }
193
+ export declare const MAX_OWNER_BLOCKERS = 20;
194
+ /** A sentence safe to put in front of the owner, or `undefined` when it is not. */
195
+ export declare function sanitizeBlockerText(value: unknown, max?: number): string | undefined;
196
+ /**
197
+ * Everything in the way, from the three places that know: the server's holders
198
+ * (active and unproven work: `pending_delivery` and `forensic` never block), the
199
+ * daemon's own refusal from `prepare`, and the work the Desktop app itself hosts and
200
+ * ends when it quits (`LocalSessionService` processes and agent browser windows,
201
+ * which the daemon's inventory cannot see).
202
+ *
203
+ * A daemon blocker whose id equals a server holder's ref is the same work seen twice:
204
+ * it is listed once, with the daemon's reason and the server's label. Whether the two
205
+ * ids are in fact comparable was never established (review §6.2 item 2), so a match
206
+ * only MERGES the wording and never hides a blocker that has no match.
207
+ */
208
+ export declare function describeBlockers(input: {
209
+ view?: GuidedViewLike | null;
210
+ daemon?: readonly DaemonBlockerLike[] | null;
211
+ desktop?: DesktopWorkLike | null;
212
+ }): OwnerBlocker[];
213
+ /**
214
+ * A string that changes whenever the set of things in the way changes. The "update
215
+ * now anyway" confirmation carries the one it was shown, and the action is refused
216
+ * when it no longer matches: a confirmation that names what WAS running is not a
217
+ * confirmation of what is.
218
+ */
219
+ export declare function blockersFingerprint(blockers: readonly OwnerBlocker[]): string;
220
+ /**
221
+ * The labels the owner is shown, as the server records them in the outcome
222
+ * (`acknowledged`): each bounded to the server's 160-character limit.
223
+ */
224
+ export declare function acknowledgedLabels(blockers: readonly OwnerBlocker[], max?: number): string[];
225
+ export interface HandoffBlocker {
226
+ kind: string;
227
+ id?: string;
228
+ reason: string;
229
+ }
230
+ export interface HandoffStatusLike {
231
+ protocolVersion: 1;
232
+ handoffId: string;
233
+ phase: 'ready' | 'deferred' | 'reconnecting' | 'verified' | 'failed' | 'rolled-back';
234
+ reason: string;
235
+ sourceProcessId: string;
236
+ sourceVersion: string;
237
+ target: {
238
+ path?: string;
239
+ mtimeMs?: number;
240
+ sha256: string;
241
+ };
242
+ blockers: HandoffBlocker[];
243
+ expectedSessions: unknown[];
244
+ adoptedSessionCount?: number;
245
+ verifiedBy?: {
246
+ processId: string;
247
+ version: string;
248
+ };
249
+ observer?: {
250
+ processId: string;
251
+ version: string;
252
+ buildMatches: boolean;
253
+ ready: boolean;
254
+ };
255
+ }
256
+ /**
257
+ * What a call to the daemon's loopback `/v1/update-handoff` came back as.
258
+ *
259
+ * - `absent`: a 404. The daemon answered and has no such endpoint: it predates the
260
+ * handoff (the installed 0.8.117 answers 404 to every path it does not serve). This
261
+ * is the ONLY reply that means "legacy". A refused connection is not.
262
+ * - `unauthorized`: the bootstrap secret was refused (it rotates when the daemon
263
+ * restarts). Re-read the bootstrap and try again.
264
+ * - `refused`: reachable, speaks the protocol, said no (409: no handoff owned, or the
265
+ * lease expired; 400; 413). A refusal is a value the caller shows.
266
+ * - `unreachable`: no bootstrap file, a connection error, a timeout, a malformed or
267
+ * unexpected body. Unknown: never `absent`, never `ready`.
268
+ */
269
+ export type HandoffReply = {
270
+ kind: 'ok';
271
+ status: HandoffStatusLike;
272
+ } | {
273
+ kind: 'absent';
274
+ } | {
275
+ kind: 'unauthorized';
276
+ } | {
277
+ kind: 'refused';
278
+ code?: string;
279
+ } | {
280
+ kind: 'unreachable';
281
+ };
282
+ export declare function classifyHandoffReply(input: {
283
+ /** `null` when no HTTP response arrived at all. */
284
+ statusCode: number | null;
285
+ /** The parsed JSON body, or `undefined` when there was none or it did not parse. */
286
+ body?: unknown;
287
+ }): HandoffReply;
288
+ export interface ReplacementEvidence {
289
+ /** The daemon's own answer to `status` for the handoff, or null when it did not answer. */
290
+ handoff: Pick<HandoffStatusLike, 'phase' | 'verifiedBy' | 'target' | 'reason' | 'observer'> | null;
291
+ expect: {
292
+ sourceProcessId: string;
293
+ targetSha256: string;
294
+ /** The bundled daemon's version, or null when it could not be read (the hash binds it anyway). */
295
+ version: string | null;
296
+ };
297
+ identity: {
298
+ recordedDaemonId: string;
299
+ recordedRuntimeKey: string;
300
+ /** The daemon id of the process now holding the loopback bootstrap, or null when unreadable. */
301
+ bootstrapDaemonId: string | null;
302
+ /** The runtimeKey the machine payload lists for `recordedDaemonId` now, or null when it lists none or could not be read. */
303
+ runtimeKeyNow: string | null;
304
+ };
305
+ }
306
+ export type ReplacementVerdict =
307
+ /** (1) the daemon's `verified` for this build by another process, and (2) the same identity. */
308
+ {
309
+ kind: 'verified';
310
+ } | {
311
+ kind: 'not_yet';
312
+ missing: 'daemon_status' | 'verified' | 'identity_unreadable';
313
+ }
314
+ /** The daemon id or the machine's runtimeKey changed: a reinstall. The hold is left and shown. */
315
+ | {
316
+ kind: 'reinstall';
317
+ what: 'daemon_id' | 'runtime_key';
318
+ } | {
319
+ kind: 'failed';
320
+ phase: 'failed' | 'rolled-back';
321
+ reason: string;
322
+ sameProcess: boolean;
323
+ };
324
+ /**
325
+ * Decide whether a committed replacement is complete. All of (1) and (2) must hold
326
+ * before anything else is asked of the server (3, {@link serverSawNewProcess}), and
327
+ * nothing here resumes: the verdict only says what was seen.
328
+ *
329
+ * An identity that is merely UNREADABLE (the bootstrap is mid-rotation, the machine
330
+ * payload could not be fetched) is `not_yet`, never a reinstall and never verified.
331
+ */
332
+ export declare function verifyReplacement(evidence: ReplacementEvidence): ReplacementVerdict;
333
+ export interface LegacyRestartEvidence {
334
+ before: {
335
+ startedAt: number | null;
336
+ processId: number | null;
337
+ };
338
+ after: {
339
+ daemonId: string | null;
340
+ version: string | null;
341
+ startedAt: number | null;
342
+ processId: number | null;
343
+ };
344
+ expect: {
345
+ daemonId: string;
346
+ version: string | null;
347
+ };
348
+ runtimeKeyNow: string | null;
349
+ recordedRuntimeKey: string;
350
+ }
351
+ /**
352
+ * The legacy counterpart: a daemon with no handoff cannot say `verified`, so the copy
353
+ * claims only what was seen ("restarted on version X"), never "adopted N sessions".
354
+ * A new process (a different start time and, where known, a different PID), the same
355
+ * identity, and the version it announces equal to the bundled one.
356
+ */
357
+ export declare function verifyLegacyRestart(evidence: LegacyRestartEvidence): ReplacementVerdict;
358
+ /**
359
+ * Whether the server independently saw a new process register under the held key
360
+ * (condition 3). The Journey says `back_held` for ANY process that registered since
361
+ * the fence, so it can only corroborate THIS replacement when it did not already say
362
+ * so before the commit. When it did, the answer is `not_independent`: the flow rests
363
+ * on (1) and (2) and says so.
364
+ */
365
+ export declare function serverSawNewProcess(input: {
366
+ journeyPhase: string | null | undefined;
367
+ backHeldAtCommit: boolean;
368
+ }): 'seen' | 'not_yet' | 'not_independent';
369
+ /** Whether an observed hold is the one this flow took, and still live. */
370
+ export declare function isOurLiveHold(view: GuidedViewLike | null | undefined, generation: number | null): boolean;