@skrr-ai/cli 0.1.118 → 0.1.120

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 (285) hide show
  1. package/dist/base-command.d.ts +32 -0
  2. package/dist/base-command.js +113 -5
  3. package/dist/commands/commands.js +11 -0
  4. package/dist/commands/commitments/action-proposal.d.ts +13 -0
  5. package/dist/commands/commitments/action-proposal.js +39 -0
  6. package/dist/commands/commitments/action-proposals/decide.js +8 -1
  7. package/dist/commands/commitments/action-proposals/execute.js +11 -1
  8. package/dist/commands/commitments/create.js +2 -2
  9. package/dist/commands/commitments/doctor.d.ts +8 -0
  10. package/dist/commands/commitments/doctor.js +22 -0
  11. package/dist/commands/commitments/execute-remediation.js +12 -1
  12. package/dist/commands/commitments/observations.js +2 -2
  13. package/dist/commands/commitments/pack/bind.d.ts +18 -1
  14. package/dist/commands/commitments/pack/bind.js +66 -8
  15. package/dist/commands/commitments/pack/plan.d.ts +4 -1
  16. package/dist/commands/commitments/pack/plan.js +30 -2
  17. package/dist/commands/commitments/update.js +8 -2
  18. package/dist/commands/completion/index.js +1 -1
  19. package/dist/commands/completion/values.js +1 -1
  20. package/dist/commands/computer-use/audio.d.ts +1 -0
  21. package/dist/commands/computer-use/audio.js +1 -0
  22. package/dist/commands/computer-use/browser.d.ts +1 -0
  23. package/dist/commands/computer-use/browser.js +1 -0
  24. package/dist/commands/computer-use/disable.d.ts +1 -0
  25. package/dist/commands/computer-use/disable.js +1 -0
  26. package/dist/commands/computer-use/enable.d.ts +1 -0
  27. package/dist/commands/computer-use/enable.js +1 -0
  28. package/dist/commands/computer-use/index.d.ts +1 -0
  29. package/dist/commands/computer-use/index.js +1 -0
  30. package/dist/commands/computer-use/install.d.ts +1 -0
  31. package/dist/commands/computer-use/install.js +1 -0
  32. package/dist/commands/computer-use/recover.d.ts +1 -0
  33. package/dist/commands/computer-use/recover.js +1 -0
  34. package/dist/commands/computer-use/status.d.ts +1 -0
  35. package/dist/commands/computer-use/status.js +1 -0
  36. package/dist/commands/computer-use/stop.d.ts +1 -0
  37. package/dist/commands/computer-use/stop.js +1 -0
  38. package/dist/commands/context.js +16 -11
  39. package/dist/commands/convos/recovery.js +1 -1
  40. package/dist/commands/daemon/diagnostics.d.ts +9 -0
  41. package/dist/commands/daemon/diagnostics.js +53 -0
  42. package/dist/commands/daemon/index.js +2 -0
  43. package/dist/commands/daemon/maintenance/attest.d.ts +15 -0
  44. package/dist/commands/daemon/maintenance/attest.js +52 -0
  45. package/dist/commands/daemon/maintenance/hold.d.ts +17 -0
  46. package/dist/commands/daemon/maintenance/hold.js +70 -0
  47. package/dist/commands/daemon/maintenance/index.d.ts +12 -0
  48. package/dist/commands/daemon/maintenance/index.js +30 -0
  49. package/dist/commands/daemon/maintenance/resume.d.ts +13 -0
  50. package/dist/commands/daemon/maintenance/resume.js +36 -0
  51. package/dist/commands/daemon/maintenance/status.d.ts +13 -0
  52. package/dist/commands/daemon/maintenance/status.js +36 -0
  53. package/dist/commands/doctor.d.ts +4 -1
  54. package/dist/commands/doctor.js +50 -6
  55. package/dist/commands/followups/plans/cancel-batch.js +6 -3
  56. package/dist/commands/followups/plans/capacity.js +7 -3
  57. package/dist/commands/followups/plans/observability.js +11 -3
  58. package/dist/commands/followups/plans/preferences/set.js +26 -8
  59. package/dist/commands/followups/plans/preferences.js +1 -1
  60. package/dist/commands/followups/plans/show.js +1 -1
  61. package/dist/commands/goals/key-results/assess.d.ts +36 -0
  62. package/dist/commands/goals/key-results/assess.js +110 -0
  63. package/dist/commands/goals/key-results/history.d.ts +30 -0
  64. package/dist/commands/goals/key-results/history.js +104 -0
  65. package/dist/commands/goals/key-results/list.d.ts +5 -4
  66. package/dist/commands/goals/key-results/list.js +12 -6
  67. package/dist/commands/goals/key-results/show.d.ts +4 -2
  68. package/dist/commands/goals/key-results/show.js +25 -4
  69. package/dist/commands/goals/key-results/update.d.ts +4 -0
  70. package/dist/commands/goals/key-results/update.js +18 -24
  71. package/dist/commands/goals/plan-now.js +12 -1
  72. package/dist/commands/goals/planner-compare.d.ts +30 -0
  73. package/dist/commands/goals/planner-compare.js +136 -0
  74. package/dist/commands/machines/dedicated/service/exec.d.ts +3 -0
  75. package/dist/commands/machines/dedicated/service/exec.js +70 -11
  76. package/dist/commands/machines/hosted/destroy.d.ts +1 -0
  77. package/dist/commands/machines/hosted/destroy.js +11 -1
  78. package/dist/commands/machines/hosted/exec.d.ts +1 -0
  79. package/dist/commands/machines/hosted/exec.js +12 -1
  80. package/dist/commands/machines/hosted/index.js +1 -0
  81. package/dist/commands/machines/hosted/operation.d.ts +14 -0
  82. package/dist/commands/machines/hosted/operation.js +51 -0
  83. package/dist/commands/machines/hosted/pull.js +1 -1
  84. package/dist/commands/machines/hosted/start.js +6 -2
  85. package/dist/commands/memory/versions/list.d.ts +2 -0
  86. package/dist/commands/memory/versions/list.js +3 -2
  87. package/dist/commands/memory/versions/show.d.ts +2 -0
  88. package/dist/commands/memory/versions/show.js +3 -2
  89. package/dist/commands/spaces/auto-executor/compare.d.ts +18 -0
  90. package/dist/commands/spaces/auto-executor/compare.js +139 -0
  91. package/dist/commands/spaces/auto-executor/runs.d.ts +3 -0
  92. package/dist/commands/spaces/auto-executor/runs.js +13 -1
  93. package/dist/commands/spaces/charter/create-from-description.d.ts +21 -0
  94. package/dist/commands/spaces/charter/create-from-description.js +77 -0
  95. package/dist/commands/spaces/charter/show.d.ts +22 -0
  96. package/dist/commands/spaces/charter/show.js +61 -0
  97. package/dist/commands/spaces/heartbeats/list.d.ts +1 -16
  98. package/dist/commands/spaces/heartbeats/list.js +13 -0
  99. package/dist/commands/spaces/install-template.js +3 -0
  100. package/dist/commands/spaces/loops/migrate.d.ts +32 -0
  101. package/dist/commands/spaces/loops/migrate.js +103 -0
  102. package/dist/commands/spaces/loops/release.d.ts +14 -0
  103. package/dist/commands/spaces/loops/release.js +59 -0
  104. package/dist/commands/spaces/notebook.d.ts +25 -0
  105. package/dist/commands/spaces/notebook.js +75 -0
  106. package/dist/commands/spaces/proposals/discard.d.ts +17 -0
  107. package/dist/commands/spaces/proposals/discard.js +17 -0
  108. package/dist/commands/spaces/proposals/keep.d.ts +17 -0
  109. package/dist/commands/spaces/proposals/keep.js +17 -0
  110. package/dist/commands/spaces/proposals/list.d.ts +24 -0
  111. package/dist/commands/spaces/proposals/list.js +89 -0
  112. package/dist/commands/spaces/skills/propose.d.ts +29 -0
  113. package/dist/commands/spaces/skills/propose.js +82 -0
  114. package/dist/commands/spaces/templates/fork.js +3 -0
  115. package/dist/commands/spaces/triggers/show.js +6 -0
  116. package/dist/commands/spaces/workflows/retry.d.ts +1 -0
  117. package/dist/commands/spaces/workflows/retry.js +8 -1
  118. package/dist/commands/spaces/workflows/triggers/create.js +3 -2
  119. package/dist/commands/spaces/workflows/triggers/fire.js +4 -0
  120. package/dist/commands/spaces/workflows/triggers/update.js +2 -1
  121. package/dist/commands/spaces/write-policy/set.d.ts +22 -0
  122. package/dist/commands/spaces/write-policy/set.js +82 -0
  123. package/dist/commands/spaces/write-policy/show.d.ts +19 -0
  124. package/dist/commands/spaces/write-policy/show.js +49 -0
  125. package/dist/commands/store/artifacts/receipt.d.ts +14 -0
  126. package/dist/commands/store/artifacts/receipt.js +34 -0
  127. package/dist/commands/store/artifacts/release.d.ts +2 -0
  128. package/dist/commands/store/artifacts/release.js +11 -0
  129. package/dist/commands/store/governance/publish-release.d.ts +2 -0
  130. package/dist/commands/store/governance/publish-release.js +30 -1
  131. package/dist/commands/store/governance/release-receipt.d.ts +12 -0
  132. package/dist/commands/store/governance/release-receipt.js +32 -0
  133. package/dist/commands/tasks/approvals/list.d.ts +11 -5
  134. package/dist/commands/tasks/approvals/list.js +13 -6
  135. package/dist/commands/tasks/attachments/facts.d.ts +12 -0
  136. package/dist/commands/tasks/attachments/facts.js +29 -0
  137. package/dist/commands/tasks/attachments/upload.d.ts +1 -0
  138. package/dist/commands/tasks/attachments/upload.js +16 -0
  139. package/dist/commands/tasks/bulk-update.js +35 -1
  140. package/dist/commands/tasks/context.d.ts +15 -0
  141. package/dist/commands/tasks/context.js +69 -0
  142. package/dist/commands/tasks/copy.d.ts +18 -0
  143. package/dist/commands/tasks/copy.js +85 -0
  144. package/dist/commands/tasks/create-tree.js +97 -23
  145. package/dist/commands/tasks/create.js +9 -1
  146. package/dist/commands/tasks/deliverable/add.js +20 -10
  147. package/dist/commands/tasks/move.js +8 -1
  148. package/dist/commands/tasks/operations/show.d.ts +16 -0
  149. package/dist/commands/tasks/operations/show.js +98 -0
  150. package/dist/commands/tasks/operations/wait.d.ts +22 -0
  151. package/dist/commands/tasks/operations/wait.js +114 -0
  152. package/dist/commands/tasks/preflight.d.ts +1 -0
  153. package/dist/commands/tasks/preflight.js +11 -1
  154. package/dist/commands/tasks/rearm-recurrence.js +4 -0
  155. package/dist/commands/tasks/result/show.js +22 -0
  156. package/dist/commands/tasks/result/submit.d.ts +1 -0
  157. package/dist/commands/tasks/result/submit.js +3 -0
  158. package/dist/commands/tasks/retry-receipt.d.ts +15 -0
  159. package/dist/commands/tasks/retry-receipt.js +89 -0
  160. package/dist/commands/tasks/retry-review.d.ts +16 -0
  161. package/dist/commands/tasks/retry-review.js +66 -0
  162. package/dist/commands/tasks/retry.d.ts +4 -8
  163. package/dist/commands/tasks/retry.js +34 -68
  164. package/dist/commands/tasks/runs.d.ts +21 -0
  165. package/dist/commands/tasks/runs.js +95 -0
  166. package/dist/commands/tasks/show.d.ts +2 -0
  167. package/dist/commands/tasks/show.js +15 -1
  168. package/dist/commands/tasks/start-receipt.d.ts +15 -0
  169. package/dist/commands/tasks/start-receipt.js +95 -0
  170. package/dist/commands/tasks/start.d.ts +4 -0
  171. package/dist/commands/tasks/start.js +49 -3
  172. package/dist/commands/tasks/update.js +3 -0
  173. package/dist/commands/tasks/upsert.js +17 -3
  174. package/dist/commands/tasks/watch.d.ts +3 -0
  175. package/dist/commands/tasks/watch.js +85 -20
  176. package/dist/commands/wiki/history.d.ts +33 -0
  177. package/dist/commands/wiki/history.js +134 -0
  178. package/dist/commands/wiki/ls.d.ts +2 -0
  179. package/dist/commands/wiki/ls.js +8 -0
  180. package/dist/commands/wiki/update.d.ts +3 -1
  181. package/dist/commands/wiki/update.js +24 -2
  182. package/dist/commands/wiki/write.d.ts +22 -0
  183. package/dist/commands/wiki/write.js +144 -6
  184. package/dist/generated/agent-guides.json +81 -81
  185. package/dist/generated/managed-instruction-guides.json +1 -1
  186. package/dist/lib/agent-guides.d.ts +9 -0
  187. package/dist/lib/agent-guides.js +23 -1
  188. package/dist/lib/agentic-stream.d.ts +5 -0
  189. package/dist/lib/agentic-stream.js +122 -3
  190. package/dist/lib/api-fetch.d.ts +4 -0
  191. package/dist/lib/api-fetch.js +25 -7
  192. package/dist/lib/auth-diagnostics.d.ts +39 -0
  193. package/dist/lib/auth-diagnostics.js +71 -0
  194. package/dist/lib/cli-client-identity.d.ts +6 -0
  195. package/dist/lib/cli-client-identity.js +16 -0
  196. package/dist/lib/cli-installation-diagnostics.d.ts +52 -0
  197. package/dist/lib/cli-installation-diagnostics.js +254 -0
  198. package/dist/lib/command-hygiene.d.ts +45 -0
  199. package/dist/lib/command-hygiene.js +413 -0
  200. package/dist/lib/commitments.d.ts +64 -3
  201. package/dist/lib/commitments.js +34 -6
  202. package/dist/lib/completion-index.d.ts +16 -4
  203. package/dist/lib/completion-index.js +50 -14
  204. package/dist/lib/config.js +7 -1
  205. package/dist/lib/daemon-maintenance-command.d.ts +22 -0
  206. package/dist/lib/daemon-maintenance-command.js +39 -0
  207. package/dist/lib/daemon-maintenance.d.ts +275 -0
  208. package/dist/lib/daemon-maintenance.js +1035 -0
  209. package/dist/lib/dedicated-service.d.ts +3 -2
  210. package/dist/lib/dedicated-service.js +11 -3
  211. package/dist/lib/governed-store.d.ts +8 -0
  212. package/dist/lib/governed-store.js +5 -0
  213. package/dist/lib/hosted-machines.d.ts +33 -2
  214. package/dist/lib/hosted-machines.js +19 -1
  215. package/dist/lib/json-output.d.ts +1 -1
  216. package/dist/lib/machines.d.ts +9 -1
  217. package/dist/lib/machines.js +15 -1
  218. package/dist/lib/memory-history-session.d.ts +14 -0
  219. package/dist/lib/memory-history-session.js +60 -0
  220. package/dist/lib/node-adapter.js +13 -0
  221. package/dist/lib/record-output.d.ts +22 -0
  222. package/dist/lib/record-output.js +78 -0
  223. package/dist/lib/roles.d.ts +3 -3
  224. package/dist/lib/scoped-memory-command.d.ts +24 -0
  225. package/dist/lib/scoped-memory-command.js +87 -5
  226. package/dist/lib/space-charter.d.ts +49 -0
  227. package/dist/lib/space-charter.js +78 -0
  228. package/dist/lib/space-loop-migration.d.ts +18 -0
  229. package/dist/lib/space-loop-migration.js +128 -0
  230. package/dist/lib/space-notebook.d.ts +22 -0
  231. package/dist/lib/space-notebook.js +140 -0
  232. package/dist/lib/space-proposal-decision-command.d.ts +36 -0
  233. package/dist/lib/space-proposal-decision-command.js +108 -0
  234. package/dist/lib/space-proposals.d.ts +44 -0
  235. package/dist/lib/space-proposals.js +186 -0
  236. package/dist/lib/space-templates.d.ts +8 -0
  237. package/dist/lib/space-templates.js +17 -0
  238. package/dist/lib/space-write-policy.d.ts +24 -0
  239. package/dist/lib/space-write-policy.js +103 -0
  240. package/dist/lib/task-agent-context-capabilities.d.ts +10 -0
  241. package/dist/lib/task-agent-context-capabilities.js +22 -0
  242. package/dist/lib/task-agent-context-compatibility.d.ts +9 -0
  243. package/dist/lib/task-agent-context-compatibility.js +30 -0
  244. package/dist/lib/task-asset-upload.d.ts +22 -0
  245. package/dist/lib/task-asset-upload.js +257 -29
  246. package/dist/lib/task-batch.d.ts +15 -1
  247. package/dist/lib/task-batch.js +35 -2
  248. package/dist/lib/task-operation-watch.d.ts +66 -0
  249. package/dist/lib/task-operation-watch.js +159 -0
  250. package/dist/lib/task-resolver.d.ts +1 -1
  251. package/dist/lib/task-resolver.js +4 -3
  252. package/dist/lib/task-runtime-headers.js +13 -3
  253. package/dist/lib/task-work.d.ts +1 -1
  254. package/dist/lib/task-work.js +35 -1
  255. package/dist/lib/task-write-attribution.d.ts +14 -0
  256. package/dist/lib/task-write-attribution.js +35 -2
  257. package/dist/lib/triggers.d.ts +11 -0
  258. package/dist/lib/triggers.js +7 -0
  259. package/dist/lib/wiki-update.d.ts +1 -0
  260. package/dist/lib/wiki-update.js +10 -1
  261. package/dist/lib/wiki.js +4 -1
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +9 -1
  263. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +31 -15
  264. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +1 -0
  265. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +13 -7
  266. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.d.ts +370 -0
  267. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.js +315 -0
  268. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.d.ts +178 -0
  269. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.js +262 -0
  270. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.d.ts +51 -0
  271. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.js +106 -0
  272. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +9 -1
  273. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +32 -5
  274. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +1 -0
  275. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +33 -14
  276. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.d.ts +370 -0
  277. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.js +404 -0
  278. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.d.ts +178 -0
  279. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.js +342 -0
  280. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.d.ts +51 -0
  281. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.js +138 -0
  282. package/dist/node_modules/@skrr-ai/auth-core/package.json +20 -0
  283. package/dist/node_modules/@skrr-ai/data-provider/index.js +27085 -20250
  284. package/oclif.manifest.json +10570 -7247
  285. package/package.json +20 -2
@@ -0,0 +1,178 @@
1
+ /**
2
+ * The maintenance report wire contract (Machine Maintenance, OSK-19107, task M24;
3
+ * `docs/architecture/machine-maintenance/implementation-plan-2026-10-24.md` §3.5,
4
+ * `phase0/m04-quiescence-evidence.md` §2.5 and §2.6).
5
+ *
6
+ * Declared ONCE, here. The daemon is published and cannot import the API, so the
7
+ * capability name, the payload shape and its bounds live in this package, which
8
+ * every end can import (`@skrr-ai/auth-core/maintenance-report-wire`).
9
+ * `maintenanceReportWire.test.ts` fails on a second spelling of the capability
10
+ * anywhere else in the repo. The shape follows `dedicatedRuntimeBusyWire.ts`, the
11
+ * precedent for a positively announced, fail-closed report, and differs from it
12
+ * only where the owner's pause needs more.
13
+ *
14
+ * WHAT THE REPORT SAYS
15
+ * ---------------------------------------------------------------------------
16
+ * "What does this daemon process hold right now that an update would take from
17
+ * the owner, and has anything been admitted since the last time you asked?" It is
18
+ * a LOCAL read summarised into counts, the handoff's own restart blockers, and
19
+ * the daemon's own update handoff phase.
20
+ *
21
+ * It is EVIDENCE, never authority. The fence does not depend on it: a daemon that
22
+ * lies, or one that sends nothing, cannot make a held machine accept work. Only
23
+ * this report can grant `ready` (a server-side source can only veto it), and a
24
+ * daemon that never announces the capability can only reach `ready` by the
25
+ * owner's attestation.
26
+ *
27
+ * FAIL CLOSED, in three places
28
+ * ---------------------------------------------------------------------------
29
+ * 1. A count is `0` only when the daemon positively counted. A source that could
30
+ * not be read is `complete: 'unknown'`; a daemon that is leaving is
31
+ * `complete: false`. A reader MUST treat both as busy, whatever the counts
32
+ * say (`maintenanceReportIsQuiet` is the one place that says so).
33
+ * 2. A report that is malformed in ANY field is `null` from
34
+ * `parseMaintenanceReport`, and the caller treats the machine as not quiet.
35
+ * A count that is missing, negative, fractional or not a safe integer is not
36
+ * "zero": it is a different, broken report. A version this parser does not
37
+ * know is `null` too: a newer shape needs its own announced capability.
38
+ * 3. Bounds. The blocker list and every string are clamped, so one report can
39
+ * never be large enough to matter, and a field that is NOT in the contract is
40
+ * never copied into what the parser returns (the result is built from a
41
+ * fresh object, so `__proto__` and its kin cannot ride along).
42
+ *
43
+ * `admittedTotal`
44
+ * ---------------------------------------------------------------------------
45
+ * A MONOTONIC counter of admissions since this process started. The server proves
46
+ * "nothing was admitted between two reports" by comparing two values, without
47
+ * trusting the daemon's clock. It counts at every place the daemon admits work
48
+ * and OVER-counts on purpose (one admission may be noted at the gate and again
49
+ * where the work registers): a second increment can only make the proof harder,
50
+ * never easier. A path that the daemon cannot count must say so in
51
+ * `daemon/src/maintenance-report.ts`, not hide in a zero.
52
+ */
53
+ export declare const MAINTENANCE_REPORT_CAPABILITY = "maintenance_report_v1";
54
+ /** The heartbeat (and register) key the report rides under. Sent only by a daemon that announced the capability. */
55
+ export declare const MAINTENANCE_REPORT_HEARTBEAT_KEY = "maintenanceReport";
56
+ export declare const MAINTENANCE_REPORT_VERSION = 1;
57
+ /**
58
+ * The daemon's own update handoff phases (`daemon/src/update/handoff.ts`), and the
59
+ * members of `DaemonMaintenanceHandoff.phase` in `@skrr-ai/data-provider`. A phase
60
+ * outside this list is a newer protocol and makes the whole report `null`.
61
+ */
62
+ export declare const MAINTENANCE_REPORT_HANDOFF_PHASES: readonly ["ready", "deferred", "reconnecting", "verified", "failed", "rolled-back"];
63
+ export type MaintenanceReportHandoffPhase = (typeof MAINTENANCE_REPORT_HANDOFF_PHASES)[number];
64
+ export declare const MAINTENANCE_REPORT_LIMITS: {
65
+ /** The handoff's blockers carried for the "waiting on" text; the counts, not this list, decide busy. */
66
+ readonly maxBlockers: 32;
67
+ readonly maxKindLength: 32;
68
+ readonly maxIdLength: 128;
69
+ readonly maxReasonLength: 240;
70
+ readonly maxHandoffIdLength: 64;
71
+ readonly maxHandoffReasonLength: 64;
72
+ readonly maxProcessStartIdLength: 64;
73
+ /** Hard ceiling on the serialized report. The clamps above keep a valid one well under it. */
74
+ readonly maxSerializedBytes: 20000;
75
+ };
76
+ /** What `complete` can be. Anything but `true` means the counts prove nothing. */
77
+ export type MaintenanceReportComplete = boolean | 'unknown';
78
+ /** The handoff's own `RestartBlocker` shape (`daemon/src/update/handoff.ts`). Reason text is the handoff's, never re-spelled. */
79
+ export interface MaintenanceReportBlocker {
80
+ kind: string;
81
+ id?: string;
82
+ reason: string;
83
+ }
84
+ export interface MaintenanceReportInflight {
85
+ /** Provider sessions and their wrapper processes: in-process turns, preparing dispatches, live wrappers. */
86
+ sessions: number;
87
+ /** Tool calls running, plus background commands still running. */
88
+ tools: number;
89
+ /** Terminals the daemon holds, preservable or not. A person's open shell is work an update would interrupt. */
90
+ terminals: number;
91
+ /** Provider turns that still depend on credentials, permissions, a browser or a model connection the service owns. */
92
+ providerDependencies: number;
93
+ /** Write-ahead entries whose durability the daemon has not confirmed. */
94
+ walUnconfirmed: number;
95
+ }
96
+ export interface MaintenanceReportHandoff {
97
+ phase: MaintenanceReportHandoffPhase;
98
+ handoffId: string;
99
+ reason?: string;
100
+ /** ISO time the daemon's lease on this phase ends. */
101
+ expiresAt?: string;
102
+ }
103
+ export interface MaintenanceReport {
104
+ version: typeof MAINTENANCE_REPORT_VERSION;
105
+ /** The daemon process that read this. The server drops a report that is not its registered process's. */
106
+ processStartId: string;
107
+ /** ISO time of the daemon's own read. Informational: freshness is the server's receipt time. */
108
+ readAt: string;
109
+ /** `true`: every source was read. `'unknown'`: one could not be. `false`: the process is leaving. */
110
+ complete: MaintenanceReportComplete;
111
+ /** Monotonic count of admissions since this process started. */
112
+ admittedTotal: number;
113
+ inflight: MaintenanceReportInflight;
114
+ blockers: MaintenanceReportBlocker[];
115
+ /** True when the daemon had more blockers than it sent. */
116
+ blockersTruncated: boolean;
117
+ /** The daemon's own update handoff, only while it is live or unresolved; otherwise `null`. */
118
+ handoff: MaintenanceReportHandoff | null;
119
+ }
120
+ /**
121
+ * The reader's view of a daemon's heartbeat section. Anything malformed, from a
122
+ * daemon that did not announce the capability, an unknown version, or oversized
123
+ * is `null`, and the caller must then treat the machine as NOT quiet. Never
124
+ * trusts a field it did not validate and never copies one it does not know.
125
+ */
126
+ export declare function parseMaintenanceReport(raw: unknown): MaintenanceReport | null;
127
+ /**
128
+ * A report as it was stored (JSON text on the presence row). Anything that does
129
+ * not parse, or does not validate, is `null`: a stored value is an input like any
130
+ * other and is read with the same suspicion.
131
+ */
132
+ export declare function parseStoredMaintenanceReport(stored: unknown): MaintenanceReport | null;
133
+ /**
134
+ * The one definition of a POSITIVE ZERO: the daemon read every source and found
135
+ * nothing. `complete: 'unknown'` and `complete: false` are never zero whatever
136
+ * the counts say, because the counts were not (all) read.
137
+ *
138
+ * A blocker that is not work (a missing restart owner, say) does not make a
139
+ * report non-quiet; readiness is "nothing is running", not "the daemon can
140
+ * restart itself".
141
+ */
142
+ export declare function maintenanceReportIsQuiet(report: MaintenanceReport | null): boolean;
143
+ export interface BuildMaintenanceReportInput {
144
+ processStartId: string;
145
+ /** Epoch ms of the daemon's own read. */
146
+ readAtMs: number;
147
+ complete: MaintenanceReportComplete;
148
+ admittedTotal: number;
149
+ inflight: MaintenanceReportInflight;
150
+ blockers: ReadonlyArray<{
151
+ kind: string;
152
+ id?: string;
153
+ reason: string;
154
+ }>;
155
+ /** `expiresAtMs` is epoch ms; the wire carries ISO. */
156
+ handoff: {
157
+ phase: MaintenanceReportHandoffPhase;
158
+ handoffId: string;
159
+ reason?: string;
160
+ expiresAtMs?: number;
161
+ } | null;
162
+ }
163
+ /**
164
+ * A report that says nothing was counted. The shape the daemon sends when it
165
+ * could not read an inventory source at all, and the shape `buildMaintenanceReport`
166
+ * falls back to when it was handed something the contract cannot carry.
167
+ */
168
+ export declare function unknownMaintenanceReport(processStartId: string, readAtMs: number, admittedTotal?: number): MaintenanceReport;
169
+ /**
170
+ * Build the wire report from what the daemon read. Input the contract cannot
171
+ * carry (a negative count, a blocker with no kind, a time that is not a time)
172
+ * becomes an `unknown` report rather than a throw or a report that parses to
173
+ * something else, so the result parses whenever `processStartId` is a real id (the
174
+ * daemon's always is). A builder that threw would turn a bad read into silence,
175
+ * and silence is the absence of a report, which the server reads the same way,
176
+ * but later.
177
+ */
178
+ export declare function buildMaintenanceReport(input: BuildMaintenanceReportInput): MaintenanceReport;
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var maintenanceReportWire_exports = {};
20
+ __export(maintenanceReportWire_exports, {
21
+ MAINTENANCE_REPORT_CAPABILITY: () => MAINTENANCE_REPORT_CAPABILITY,
22
+ MAINTENANCE_REPORT_HANDOFF_PHASES: () => MAINTENANCE_REPORT_HANDOFF_PHASES,
23
+ MAINTENANCE_REPORT_HEARTBEAT_KEY: () => MAINTENANCE_REPORT_HEARTBEAT_KEY,
24
+ MAINTENANCE_REPORT_LIMITS: () => MAINTENANCE_REPORT_LIMITS,
25
+ MAINTENANCE_REPORT_VERSION: () => MAINTENANCE_REPORT_VERSION,
26
+ buildMaintenanceReport: () => buildMaintenanceReport,
27
+ maintenanceReportIsQuiet: () => maintenanceReportIsQuiet,
28
+ parseMaintenanceReport: () => parseMaintenanceReport,
29
+ parseStoredMaintenanceReport: () => parseStoredMaintenanceReport,
30
+ unknownMaintenanceReport: () => unknownMaintenanceReport
31
+ });
32
+ module.exports = __toCommonJS(maintenanceReportWire_exports);
33
+ const MAINTENANCE_REPORT_CAPABILITY = "maintenance_report_v1";
34
+ const MAINTENANCE_REPORT_HEARTBEAT_KEY = "maintenanceReport";
35
+ const MAINTENANCE_REPORT_VERSION = 1;
36
+ const MAINTENANCE_REPORT_HANDOFF_PHASES = [
37
+ "ready",
38
+ "deferred",
39
+ "reconnecting",
40
+ "verified",
41
+ "failed",
42
+ "rolled-back"
43
+ ];
44
+ const MAINTENANCE_REPORT_LIMITS = {
45
+ /** The handoff's blockers carried for the "waiting on" text; the counts, not this list, decide busy. */
46
+ maxBlockers: 32,
47
+ maxKindLength: 32,
48
+ maxIdLength: 128,
49
+ maxReasonLength: 240,
50
+ maxHandoffIdLength: 64,
51
+ maxHandoffReasonLength: 64,
52
+ maxProcessStartIdLength: 64,
53
+ /** Hard ceiling on the serialized report. The clamps above keep a valid one well under it. */
54
+ maxSerializedBytes: 2e4
55
+ };
56
+ const PROCESS_START_ID_PATTERN = /^[A-Za-z0-9_.:-]+$/;
57
+ const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/g;
58
+ function isRecord(value) {
59
+ return typeof value === "object" && value !== null && !Array.isArray(value);
60
+ }
61
+ function field(record, key) {
62
+ return Object.prototype.hasOwnProperty.call(record, key) ? record[key] : void 0;
63
+ }
64
+ function isoFromMs(ms) {
65
+ if (typeof ms !== "number" || !Number.isFinite(ms))
66
+ return "";
67
+ const date = new Date(ms);
68
+ return Number.isNaN(date.getTime()) ? "" : date.toISOString();
69
+ }
70
+ function count(value) {
71
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : null;
72
+ }
73
+ function clampText(value, max) {
74
+ if (typeof value !== "string")
75
+ return null;
76
+ const clean = value.replace(CONTROL_CHARACTERS, " ");
77
+ return clean.length > max ? clean.slice(0, max) : clean;
78
+ }
79
+ function isoOf(value) {
80
+ if (typeof value !== "string")
81
+ return null;
82
+ const ms = Date.parse(value);
83
+ if (!Number.isFinite(ms))
84
+ return null;
85
+ try {
86
+ return new Date(ms).toISOString();
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+ function completeOf(value) {
92
+ return value === true || value === false || value === "unknown" ? value : void 0;
93
+ }
94
+ function parseMaintenanceReport(raw) {
95
+ if (!isRecord(raw))
96
+ return null;
97
+ if (field(raw, "version") !== MAINTENANCE_REPORT_VERSION)
98
+ return null;
99
+ const processStartId = field(raw, "processStartId");
100
+ if (typeof processStartId !== "string" || processStartId.length < 1 || processStartId.length > MAINTENANCE_REPORT_LIMITS.maxProcessStartIdLength || !PROCESS_START_ID_PATTERN.test(processStartId)) {
101
+ return null;
102
+ }
103
+ const readAt = isoOf(field(raw, "readAt"));
104
+ if (readAt === null)
105
+ return null;
106
+ const complete = completeOf(field(raw, "complete"));
107
+ if (complete === void 0)
108
+ return null;
109
+ const admittedTotal = count(field(raw, "admittedTotal"));
110
+ if (admittedTotal === null)
111
+ return null;
112
+ const rawInflight = field(raw, "inflight");
113
+ if (!isRecord(rawInflight))
114
+ return null;
115
+ const sessions = count(field(rawInflight, "sessions"));
116
+ const tools = count(field(rawInflight, "tools"));
117
+ const terminals = count(field(rawInflight, "terminals"));
118
+ const providerDependencies = count(field(rawInflight, "providerDependencies"));
119
+ const walUnconfirmed = count(field(rawInflight, "walUnconfirmed"));
120
+ if (sessions === null || tools === null || terminals === null || providerDependencies === null || walUnconfirmed === null) {
121
+ return null;
122
+ }
123
+ const rawBlockers = field(raw, "blockers");
124
+ if (!Array.isArray(rawBlockers))
125
+ return null;
126
+ const rawTruncated = field(raw, "blockersTruncated");
127
+ if (rawTruncated !== void 0 && typeof rawTruncated !== "boolean")
128
+ return null;
129
+ const limit = MAINTENANCE_REPORT_LIMITS.maxBlockers;
130
+ const blockers = [];
131
+ for (let index = 0; index < rawBlockers.length && blockers.length < limit; index += 1) {
132
+ const element = rawBlockers[index];
133
+ if (!isRecord(element))
134
+ return null;
135
+ const kind = clampText(field(element, "kind"), MAINTENANCE_REPORT_LIMITS.maxKindLength);
136
+ const reason = clampText(field(element, "reason"), MAINTENANCE_REPORT_LIMITS.maxReasonLength);
137
+ if (kind === null || kind === "" || reason === null)
138
+ return null;
139
+ const blocker = { kind, reason };
140
+ const rawId = field(element, "id");
141
+ if (rawId !== void 0) {
142
+ const id = clampText(rawId, MAINTENANCE_REPORT_LIMITS.maxIdLength);
143
+ if (id === null)
144
+ return null;
145
+ blocker.id = id;
146
+ }
147
+ blockers.push(blocker);
148
+ }
149
+ const blockersTruncated = rawBlockers.length > limit || rawTruncated === true;
150
+ const rawHandoff = field(raw, "handoff");
151
+ if (rawHandoff === void 0)
152
+ return null;
153
+ let handoff = null;
154
+ if (rawHandoff !== null) {
155
+ if (!isRecord(rawHandoff))
156
+ return null;
157
+ const phase = field(rawHandoff, "phase");
158
+ if (!MAINTENANCE_REPORT_HANDOFF_PHASES.includes(phase))
159
+ return null;
160
+ const handoffId = clampText(field(rawHandoff, "handoffId"), MAINTENANCE_REPORT_LIMITS.maxHandoffIdLength);
161
+ if (handoffId === null || handoffId === "")
162
+ return null;
163
+ handoff = { phase, handoffId };
164
+ const rawReason = field(rawHandoff, "reason");
165
+ if (rawReason !== void 0) {
166
+ const reason = clampText(rawReason, MAINTENANCE_REPORT_LIMITS.maxHandoffReasonLength);
167
+ if (reason === null)
168
+ return null;
169
+ handoff.reason = reason;
170
+ }
171
+ const rawExpiresAt = field(rawHandoff, "expiresAt");
172
+ if (rawExpiresAt !== void 0) {
173
+ const expiresAt = isoOf(rawExpiresAt);
174
+ if (expiresAt === null)
175
+ return null;
176
+ handoff.expiresAt = expiresAt;
177
+ }
178
+ }
179
+ const report = {
180
+ version: MAINTENANCE_REPORT_VERSION,
181
+ processStartId,
182
+ readAt,
183
+ complete,
184
+ admittedTotal,
185
+ inflight: { sessions, tools, terminals, providerDependencies, walUnconfirmed },
186
+ blockers,
187
+ blockersTruncated,
188
+ handoff
189
+ };
190
+ if (JSON.stringify(report).length > MAINTENANCE_REPORT_LIMITS.maxSerializedBytes)
191
+ return null;
192
+ return report;
193
+ }
194
+ function parseStoredMaintenanceReport(stored) {
195
+ if (typeof stored !== "string" || stored === "")
196
+ return null;
197
+ if (stored.length > MAINTENANCE_REPORT_LIMITS.maxSerializedBytes)
198
+ return null;
199
+ try {
200
+ return parseMaintenanceReport(JSON.parse(stored));
201
+ } catch {
202
+ return null;
203
+ }
204
+ }
205
+ function maintenanceReportIsQuiet(report) {
206
+ if (!report)
207
+ return false;
208
+ if (report.complete !== true)
209
+ return false;
210
+ const { sessions, tools, terminals, providerDependencies, walUnconfirmed } = report.inflight;
211
+ return sessions === 0 && tools === 0 && terminals === 0 && providerDependencies === 0 && walUnconfirmed === 0;
212
+ }
213
+ function unknownMaintenanceReport(processStartId, readAtMs, admittedTotal = 0) {
214
+ return {
215
+ version: MAINTENANCE_REPORT_VERSION,
216
+ processStartId,
217
+ readAt: isoFromMs(readAtMs) || (/* @__PURE__ */ new Date(0)).toISOString(),
218
+ complete: "unknown",
219
+ admittedTotal: count(admittedTotal) ?? 0,
220
+ inflight: { sessions: 0, tools: 0, terminals: 0, providerDependencies: 0, walUnconfirmed: 0 },
221
+ blockers: [],
222
+ blockersTruncated: false,
223
+ handoff: null
224
+ };
225
+ }
226
+ function buildMaintenanceReport(input) {
227
+ const blockers = input.blockers.slice(0, MAINTENANCE_REPORT_LIMITS.maxBlockers).map((blocker) => ({
228
+ kind: blocker.kind,
229
+ ...blocker.id !== void 0 ? { id: blocker.id } : {},
230
+ reason: blocker.reason
231
+ }));
232
+ const candidate = {
233
+ version: MAINTENANCE_REPORT_VERSION,
234
+ processStartId: input.processStartId,
235
+ readAt: isoFromMs(input.readAtMs),
236
+ complete: input.complete,
237
+ admittedTotal: input.admittedTotal,
238
+ inflight: { ...input.inflight },
239
+ blockers,
240
+ blockersTruncated: input.blockers.length > MAINTENANCE_REPORT_LIMITS.maxBlockers,
241
+ handoff: input.handoff ? {
242
+ phase: input.handoff.phase,
243
+ handoffId: input.handoff.handoffId,
244
+ ...input.handoff.reason !== void 0 ? { reason: input.handoff.reason } : {},
245
+ ...input.handoff.expiresAtMs !== void 0 ? { expiresAt: isoFromMs(input.handoff.expiresAtMs) } : {}
246
+ } : null
247
+ };
248
+ return parseMaintenanceReport(candidate) ?? unknownMaintenanceReport(input.processStartId, input.readAtMs, input.admittedTotal);
249
+ }
250
+ // Annotate the CommonJS export names for ESM import in node:
251
+ 0 && (module.exports = {
252
+ MAINTENANCE_REPORT_CAPABILITY,
253
+ MAINTENANCE_REPORT_HANDOFF_PHASES,
254
+ MAINTENANCE_REPORT_HEARTBEAT_KEY,
255
+ MAINTENANCE_REPORT_LIMITS,
256
+ MAINTENANCE_REPORT_VERSION,
257
+ buildMaintenanceReport,
258
+ maintenanceReportIsQuiet,
259
+ parseMaintenanceReport,
260
+ parseStoredMaintenanceReport,
261
+ unknownMaintenanceReport
262
+ });
@@ -177,6 +177,57 @@ export declare function signToolApproval(args: {
177
177
  now?: number;
178
178
  ttlMs?: number;
179
179
  }): SignedToolApproval;
180
+ /** A short-lived platform-signed assertion for exactly one local executor. */
181
+ export interface PersonalDataExecutorLeasePayload {
182
+ v: 1;
183
+ leaseId: string;
184
+ /** The daemon identity registered for this machine. */
185
+ daemonId: string;
186
+ /** Fresh random daemon-start nonce; restart invalidates every prior lease. */
187
+ daemonInstanceId: string;
188
+ /** Account whose interactive session requested this local executor. */
189
+ ownerUserId: string;
190
+ /** Fixed, sorted set of operations this executor may make available. */
191
+ capabilities: string[];
192
+ issuedAt: number;
193
+ expiresAt: number;
194
+ }
195
+ export interface SignedPersonalDataExecutorLease {
196
+ v: 1;
197
+ payload: string;
198
+ kid: string;
199
+ sig: string;
200
+ }
201
+ export type PersonalDataExecutorLeaseVerdict = {
202
+ ok: true;
203
+ leaseId: string;
204
+ ownerUserId: string;
205
+ expiresAt: number;
206
+ } | {
207
+ ok: false;
208
+ reason: 'absent' | 'malformed' | 'unknown_key' | 'signature' | 'daemon_binding' | 'capabilities' | 'expired' | 'window';
209
+ };
210
+ /** Server-side mint. The caller must authorize the requesting user separately. */
211
+ export declare function signPersonalDataExecutorLease(args: {
212
+ leaseId: string;
213
+ daemonId: string;
214
+ daemonInstanceId: string;
215
+ ownerUserId: string;
216
+ capabilities: readonly string[];
217
+ signer: ApprovalSigner;
218
+ now?: number;
219
+ ttlMs?: number;
220
+ }): SignedPersonalDataExecutorLease | null;
221
+ /**
222
+ * Daemon-side verifier. It trusts the same independently pinned platform
223
+ * keyset as v2 tool approvals, but a separate signature domain and schema.
224
+ */
225
+ export declare function verifyPersonalDataExecutorLease(lease: unknown, expected: {
226
+ daemonId: string;
227
+ daemonInstanceId: string;
228
+ capabilities: readonly string[];
229
+ now?: number;
230
+ }, keyset: ApprovalKeyset): PersonalDataExecutorLeaseVerdict;
180
231
  export interface SignedToolApprovalCall {
181
232
  toolUseId: string;
182
233
  toolName: string;
@@ -24,8 +24,10 @@ __export(toolApprovalSignature_exports, {
24
24
  canonicalApprovalKeysetText: () => canonicalApprovalKeysetText,
25
25
  endorseApprovalKeysetText: () => endorseApprovalKeysetText,
26
26
  parseApprovalKeysetText: () => parseApprovalKeysetText,
27
+ signPersonalDataExecutorLease: () => signPersonalDataExecutorLease,
27
28
  signToolApproval: () => signToolApproval,
28
29
  verifyApprovalKeysetDocument: () => verifyApprovalKeysetDocument,
30
+ verifyPersonalDataExecutorLease: () => verifyPersonalDataExecutorLease,
29
31
  verifySignedToolApproval: () => verifySignedToolApproval,
30
32
  verifyToolApprovalAttestation: () => verifyToolApprovalAttestation
31
33
  });
@@ -35,11 +37,17 @@ var import_daemonToolApproval = require("./daemonToolApproval.js");
35
37
  const TOOL_APPROVAL_SIGNED_CAPABILITY = "tool_approval_signed_v1";
36
38
  const ATTESTATION_SIGNING_DOMAIN = "skrr-tool-approval-v2\n";
37
39
  const KEYSET_SIGNING_DOMAIN = "skrr-tool-approval-keyset-v1\n";
40
+ const PERSONAL_DATA_EXECUTOR_LEASE_SIGNING_DOMAIN = "skrr-personal-data-executor-lease-v1\n";
38
41
  const KID_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
39
42
  const MAX_PAYLOAD_BYTES = 4096;
40
43
  const MAX_KEYSET_BYTES = 16 * 1024;
41
44
  const MAX_KEYSET_KEYS = 16;
42
45
  const MAX_KEYSET_SIGNATURES = 16;
46
+ const MAX_EXECUTOR_LEASE_CAPABILITIES = 8;
47
+ const MAX_EXECUTOR_LEASE_TTL_MS = 5 * 60 * 1e3;
48
+ const EXECUTOR_LEASE_CLOCK_SKEW_MS = 3e4;
49
+ const EXECUTOR_LEASE_ID_PATTERN = /^[A-Za-z0-9_-]{16,128}$/;
50
+ const EXECUTOR_CAPABILITY_PATTERN = /^[a-z0-9_.:-]{1,128}$/;
43
51
  function approvalPublicKeyFromKeyObject(kid, key) {
44
52
  const publicKey = key.type === "private" ? (0, import_node_crypto.createPublicKey)(key) : key;
45
53
  const der = publicKey.export({ format: "der", type: "spki" });
@@ -70,6 +78,24 @@ function verifySignature(domain, message, sigB64, key) {
70
78
  function signMessage(domain, message, privateKey) {
71
79
  return (0, import_node_crypto.sign)(null, Buffer.from(domain + message, "utf8"), privateKey).toString("base64");
72
80
  }
81
+ function isExecutorLeaseId(value) {
82
+ return typeof value === "string" && EXECUTOR_LEASE_ID_PATTERN.test(value);
83
+ }
84
+ function isExecutorLeaseText(value, max = 128) {
85
+ return typeof value === "string" && value.length > 0 && value.length <= max && !/[\u0000-\u001f]/.test(value);
86
+ }
87
+ function canonicalExecutorCapabilities(value) {
88
+ if (!Array.isArray(value) || value.length === 0 || value.length > MAX_EXECUTOR_LEASE_CAPABILITIES) {
89
+ return null;
90
+ }
91
+ const capabilities = value.map((entry) => typeof entry === "string" ? entry : "");
92
+ if (capabilities.some((entry) => !EXECUTOR_CAPABILITY_PATTERN.test(entry)))
93
+ return null;
94
+ const sorted = [...capabilities].sort();
95
+ if (new Set(sorted).size !== sorted.length)
96
+ return null;
97
+ return sorted;
98
+ }
73
99
  function compareKid(a, b) {
74
100
  if (a === b)
75
101
  return 0;
@@ -199,6 +225,84 @@ function signToolApproval(args) {
199
225
  sig: signMessage(ATTESTATION_SIGNING_DOMAIN, text, args.signer.privateKey)
200
226
  };
201
227
  }
228
+ function signPersonalDataExecutorLease(args) {
229
+ const capabilities = canonicalExecutorCapabilities(args.capabilities);
230
+ const ttlMs = args.ttlMs ?? MAX_EXECUTOR_LEASE_TTL_MS;
231
+ const now = args.now ?? Date.now();
232
+ if (!isExecutorLeaseId(args.leaseId) || !isExecutorLeaseText(args.daemonId) || !isExecutorLeaseId(args.daemonInstanceId) || !isExecutorLeaseText(args.ownerUserId) || !capabilities || !Number.isSafeInteger(now) || !Number.isSafeInteger(ttlMs) || ttlMs <= 0 || ttlMs > MAX_EXECUTOR_LEASE_TTL_MS) {
233
+ return null;
234
+ }
235
+ const payload = {
236
+ v: 1,
237
+ leaseId: args.leaseId,
238
+ daemonId: args.daemonId,
239
+ daemonInstanceId: args.daemonInstanceId,
240
+ ownerUserId: args.ownerUserId,
241
+ capabilities,
242
+ issuedAt: now,
243
+ expiresAt: now + ttlMs
244
+ };
245
+ const text = (0, import_daemonToolApproval.canonicalApprovalJson)(payload);
246
+ return {
247
+ v: 1,
248
+ payload: text,
249
+ kid: args.signer.kid,
250
+ sig: signMessage(PERSONAL_DATA_EXECUTOR_LEASE_SIGNING_DOMAIN, text, args.signer.privateKey)
251
+ };
252
+ }
253
+ function verifyPersonalDataExecutorLease(lease, expected, keyset) {
254
+ if (lease === void 0 || lease === null)
255
+ return { ok: false, reason: "absent" };
256
+ if (!lease || typeof lease !== "object" || Array.isArray(lease)) {
257
+ return { ok: false, reason: "malformed" };
258
+ }
259
+ const wire = lease;
260
+ if (wire.v !== 1 || typeof wire.payload !== "string" || wire.payload.length === 0 || wire.payload.length > MAX_PAYLOAD_BYTES || typeof wire.kid !== "string" || typeof wire.sig !== "string") {
261
+ return { ok: false, reason: "malformed" };
262
+ }
263
+ const pinnedKey = keyset.keys.find((key2) => key2.kid === wire.kid);
264
+ const key = pinnedKey ? ed25519PublicKey(pinnedKey.publicKeySpkiB64) : null;
265
+ if (!key)
266
+ return { ok: false, reason: "unknown_key" };
267
+ if (!verifySignature(PERSONAL_DATA_EXECUTOR_LEASE_SIGNING_DOMAIN, wire.payload, wire.sig, key)) {
268
+ return { ok: false, reason: "signature" };
269
+ }
270
+ let parsed;
271
+ try {
272
+ parsed = JSON.parse(wire.payload);
273
+ } catch {
274
+ return { ok: false, reason: "malformed" };
275
+ }
276
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
277
+ return { ok: false, reason: "malformed" };
278
+ }
279
+ const payload = parsed;
280
+ const issuedAt = payload.issuedAt;
281
+ const expiresAt = payload.expiresAt;
282
+ const capabilities = canonicalExecutorCapabilities(payload.capabilities);
283
+ if (payload.v !== 1 || !isExecutorLeaseId(payload.leaseId) || !isExecutorLeaseText(payload.daemonId) || !isExecutorLeaseId(payload.daemonInstanceId) || !isExecutorLeaseText(payload.ownerUserId) || !capabilities || typeof issuedAt !== "number" || typeof expiresAt !== "number" || !Number.isSafeInteger(issuedAt) || !Number.isSafeInteger(expiresAt)) {
284
+ return { ok: false, reason: "malformed" };
285
+ }
286
+ const expectedCapabilities = canonicalExecutorCapabilities(expected.capabilities);
287
+ if (!expectedCapabilities || capabilities.join("\0") !== expectedCapabilities.join("\0")) {
288
+ return { ok: false, reason: "capabilities" };
289
+ }
290
+ if (payload.daemonId !== expected.daemonId || payload.daemonInstanceId !== expected.daemonInstanceId) {
291
+ return { ok: false, reason: "daemon_binding" };
292
+ }
293
+ const now = expected.now ?? Date.now();
294
+ if (expiresAt <= issuedAt || expiresAt - issuedAt > MAX_EXECUTOR_LEASE_TTL_MS || issuedAt > now + EXECUTOR_LEASE_CLOCK_SKEW_MS) {
295
+ return { ok: false, reason: "window" };
296
+ }
297
+ if (expiresAt <= now)
298
+ return { ok: false, reason: "expired" };
299
+ return {
300
+ ok: true,
301
+ leaseId: payload.leaseId,
302
+ ownerUserId: payload.ownerUserId,
303
+ expiresAt
304
+ };
305
+ }
202
306
  function verifySignedToolApproval(approval, call, keyset) {
203
307
  if (approval === void 0 || approval === null)
204
308
  return { ok: false, reason: "absent" };
@@ -271,8 +375,10 @@ function verifyToolApprovalAttestation(approval, call, pin) {
271
375
  canonicalApprovalKeysetText,
272
376
  endorseApprovalKeysetText,
273
377
  parseApprovalKeysetText,
378
+ signPersonalDataExecutorLease,
274
379
  signToolApproval,
275
380
  verifyApprovalKeysetDocument,
381
+ verifyPersonalDataExecutorLease,
276
382
  verifySignedToolApproval,
277
383
  verifyToolApprovalAttestation
278
384
  });
@@ -185,6 +185,14 @@ export type DaemonComputerEventKey = keyof typeof DAEMON_COMPUTER_EVENTS;
185
185
  export type DaemonComputerEventName = (typeof DAEMON_COMPUTER_EVENTS)[DaemonComputerEventKey];
186
186
  export declare const COMPUTER_EVENT_NAMES: readonly ComputerEventName[];
187
187
  export declare const DAEMON_COMPUTER_EVENT_NAMES: readonly DaemonComputerEventName[];
188
+ /**
189
+ * API → daemon: an authorized person observes, configures, stops or recovers
190
+ * the machine's Computer Use provider. Answered with `daemon:computer:ack`. It
191
+ * is deliberately outside `DAEMON_COMPUTER_EVENTS`, which lists the events the
192
+ * attachment protocol itself speaks, so the registry parity over that table
193
+ * does not move.
194
+ */
195
+ export declare const DAEMON_COMPUTER_USE_CONTROL_EVENT: "daemon:computer:use-control";
188
196
  /** The daemon → API events; every other `daemon:computer:*` event flows API → daemon. */
189
197
  export declare const DAEMON_COMPUTER_REPORT_EVENT_KEYS: readonly ["ack", "manifest", "data", "activity", "detached", "filesChanged", "diagnostics"];
190
198
  /** The API → client events; every other `computer:*` event flows client → API. */
@@ -362,7 +370,7 @@ export declare const COMPUTER_CAPABILITY_VALUES: [ComputerCapability, ...Compute
362
370
  * Every refusal and limit the computer protocol can answer with. Callers branch
363
371
  * on the code; a message beside it is free text and may change.
364
372
  */
365
- export declare const COMPUTER_REFUSAL_CODES: readonly ["human_control", "stale_epoch", "stale_generation", "file_changed", "capability_missing", "unauthorized", "control_denied", "not_holder", "attachment_limit", "observer_limit", "watch_limit", "grant_limit", "browser_limit", "rate_limited", "not_found", "surface_ended", "machine_unavailable", "daemon_unreachable", "relay_error", "timeout", "invalid_request", "consent_missing", "path_withheld", "too_large", "service_limit"];
373
+ export declare const COMPUTER_REFUSAL_CODES: readonly ["human_control", "stale_epoch", "stale_generation", "file_changed", "capability_missing", "unauthorized", "control_denied", "not_holder", "attachment_limit", "observer_limit", "watch_limit", "grant_limit", "browser_limit", "rate_limited", "not_found", "surface_ended", "machine_unavailable", "daemon_unreachable", "relay_error", "timeout", "invalid_request", "consent_missing", "path_withheld", "too_large", "service_limit", "maintenance_hold"];
366
374
  export type ComputerRefusalCode = (typeof COMPUTER_REFUSAL_CODES)[number];
367
375
  /**
368
376
  * The code when it is a computer refusal, else `null`. There is one spelling