@skrr-ai/cli 0.1.119 → 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 (276) hide show
  1. package/dist/base-command.d.ts +31 -0
  2. package/dist/base-command.js +106 -4
  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/spaces/auto-executor/compare.d.ts +18 -0
  86. package/dist/commands/spaces/auto-executor/compare.js +139 -0
  87. package/dist/commands/spaces/auto-executor/runs.d.ts +3 -0
  88. package/dist/commands/spaces/auto-executor/runs.js +13 -1
  89. package/dist/commands/spaces/charter/create-from-description.d.ts +21 -0
  90. package/dist/commands/spaces/charter/create-from-description.js +77 -0
  91. package/dist/commands/spaces/charter/show.d.ts +22 -0
  92. package/dist/commands/spaces/charter/show.js +61 -0
  93. package/dist/commands/spaces/heartbeats/list.d.ts +1 -16
  94. package/dist/commands/spaces/heartbeats/list.js +13 -0
  95. package/dist/commands/spaces/install-template.js +3 -0
  96. package/dist/commands/spaces/loops/migrate.d.ts +32 -0
  97. package/dist/commands/spaces/loops/migrate.js +103 -0
  98. package/dist/commands/spaces/loops/release.d.ts +14 -0
  99. package/dist/commands/spaces/loops/release.js +59 -0
  100. package/dist/commands/spaces/notebook.d.ts +25 -0
  101. package/dist/commands/spaces/notebook.js +75 -0
  102. package/dist/commands/spaces/proposals/discard.d.ts +17 -0
  103. package/dist/commands/spaces/proposals/discard.js +17 -0
  104. package/dist/commands/spaces/proposals/keep.d.ts +17 -0
  105. package/dist/commands/spaces/proposals/keep.js +17 -0
  106. package/dist/commands/spaces/proposals/list.d.ts +24 -0
  107. package/dist/commands/spaces/proposals/list.js +89 -0
  108. package/dist/commands/spaces/skills/propose.d.ts +29 -0
  109. package/dist/commands/spaces/skills/propose.js +82 -0
  110. package/dist/commands/spaces/templates/fork.js +3 -0
  111. package/dist/commands/spaces/triggers/show.js +6 -0
  112. package/dist/commands/spaces/workflows/retry.d.ts +1 -0
  113. package/dist/commands/spaces/workflows/retry.js +8 -1
  114. package/dist/commands/spaces/workflows/triggers/create.js +3 -2
  115. package/dist/commands/spaces/workflows/triggers/fire.js +4 -0
  116. package/dist/commands/spaces/workflows/triggers/update.js +2 -1
  117. package/dist/commands/spaces/write-policy/set.d.ts +22 -0
  118. package/dist/commands/spaces/write-policy/set.js +82 -0
  119. package/dist/commands/spaces/write-policy/show.d.ts +19 -0
  120. package/dist/commands/spaces/write-policy/show.js +49 -0
  121. package/dist/commands/store/artifacts/receipt.d.ts +14 -0
  122. package/dist/commands/store/artifacts/receipt.js +34 -0
  123. package/dist/commands/store/artifacts/release.d.ts +2 -0
  124. package/dist/commands/store/artifacts/release.js +11 -0
  125. package/dist/commands/store/governance/publish-release.d.ts +2 -0
  126. package/dist/commands/store/governance/publish-release.js +30 -1
  127. package/dist/commands/store/governance/release-receipt.d.ts +12 -0
  128. package/dist/commands/store/governance/release-receipt.js +32 -0
  129. package/dist/commands/tasks/approvals/list.d.ts +11 -5
  130. package/dist/commands/tasks/approvals/list.js +13 -6
  131. package/dist/commands/tasks/attachments/facts.d.ts +12 -0
  132. package/dist/commands/tasks/attachments/facts.js +29 -0
  133. package/dist/commands/tasks/attachments/upload.d.ts +1 -0
  134. package/dist/commands/tasks/attachments/upload.js +16 -0
  135. package/dist/commands/tasks/bulk-update.js +35 -1
  136. package/dist/commands/tasks/context.d.ts +15 -0
  137. package/dist/commands/tasks/context.js +69 -0
  138. package/dist/commands/tasks/copy.d.ts +18 -0
  139. package/dist/commands/tasks/copy.js +85 -0
  140. package/dist/commands/tasks/create-tree.js +97 -23
  141. package/dist/commands/tasks/create.js +9 -1
  142. package/dist/commands/tasks/deliverable/add.js +20 -10
  143. package/dist/commands/tasks/move.js +8 -1
  144. package/dist/commands/tasks/operations/show.d.ts +16 -0
  145. package/dist/commands/tasks/operations/show.js +98 -0
  146. package/dist/commands/tasks/operations/wait.d.ts +22 -0
  147. package/dist/commands/tasks/operations/wait.js +114 -0
  148. package/dist/commands/tasks/preflight.d.ts +1 -0
  149. package/dist/commands/tasks/preflight.js +11 -1
  150. package/dist/commands/tasks/rearm-recurrence.js +4 -0
  151. package/dist/commands/tasks/result/show.js +22 -0
  152. package/dist/commands/tasks/result/submit.d.ts +1 -0
  153. package/dist/commands/tasks/result/submit.js +3 -0
  154. package/dist/commands/tasks/retry-receipt.d.ts +15 -0
  155. package/dist/commands/tasks/retry-receipt.js +89 -0
  156. package/dist/commands/tasks/retry-review.d.ts +16 -0
  157. package/dist/commands/tasks/retry-review.js +66 -0
  158. package/dist/commands/tasks/retry.d.ts +4 -8
  159. package/dist/commands/tasks/retry.js +34 -68
  160. package/dist/commands/tasks/runs.d.ts +21 -0
  161. package/dist/commands/tasks/runs.js +95 -0
  162. package/dist/commands/tasks/show.d.ts +2 -0
  163. package/dist/commands/tasks/show.js +15 -1
  164. package/dist/commands/tasks/start-receipt.d.ts +15 -0
  165. package/dist/commands/tasks/start-receipt.js +95 -0
  166. package/dist/commands/tasks/start.d.ts +4 -0
  167. package/dist/commands/tasks/start.js +49 -3
  168. package/dist/commands/tasks/update.js +3 -0
  169. package/dist/commands/tasks/upsert.js +17 -3
  170. package/dist/commands/tasks/watch.d.ts +3 -0
  171. package/dist/commands/tasks/watch.js +85 -20
  172. package/dist/commands/wiki/history.d.ts +33 -0
  173. package/dist/commands/wiki/history.js +134 -0
  174. package/dist/commands/wiki/ls.d.ts +2 -0
  175. package/dist/commands/wiki/ls.js +8 -0
  176. package/dist/commands/wiki/update.d.ts +3 -1
  177. package/dist/commands/wiki/update.js +24 -2
  178. package/dist/commands/wiki/write.d.ts +22 -0
  179. package/dist/commands/wiki/write.js +144 -6
  180. package/dist/generated/agent-guides.json +81 -81
  181. package/dist/generated/managed-instruction-guides.json +1 -1
  182. package/dist/lib/agent-guides.d.ts +9 -0
  183. package/dist/lib/agent-guides.js +23 -1
  184. package/dist/lib/agentic-stream.d.ts +5 -0
  185. package/dist/lib/agentic-stream.js +122 -3
  186. package/dist/lib/auth-diagnostics.d.ts +39 -0
  187. package/dist/lib/auth-diagnostics.js +71 -0
  188. package/dist/lib/cli-client-identity.d.ts +6 -0
  189. package/dist/lib/cli-client-identity.js +16 -0
  190. package/dist/lib/cli-installation-diagnostics.d.ts +52 -0
  191. package/dist/lib/cli-installation-diagnostics.js +254 -0
  192. package/dist/lib/command-hygiene.d.ts +45 -0
  193. package/dist/lib/command-hygiene.js +413 -0
  194. package/dist/lib/commitments.d.ts +64 -3
  195. package/dist/lib/commitments.js +34 -6
  196. package/dist/lib/completion-index.d.ts +16 -4
  197. package/dist/lib/completion-index.js +50 -14
  198. package/dist/lib/config.js +7 -1
  199. package/dist/lib/daemon-maintenance-command.d.ts +22 -0
  200. package/dist/lib/daemon-maintenance-command.js +39 -0
  201. package/dist/lib/daemon-maintenance.d.ts +275 -0
  202. package/dist/lib/daemon-maintenance.js +1035 -0
  203. package/dist/lib/dedicated-service.d.ts +3 -2
  204. package/dist/lib/dedicated-service.js +11 -3
  205. package/dist/lib/governed-store.d.ts +8 -0
  206. package/dist/lib/governed-store.js +5 -0
  207. package/dist/lib/hosted-machines.d.ts +33 -2
  208. package/dist/lib/hosted-machines.js +19 -1
  209. package/dist/lib/json-output.d.ts +1 -1
  210. package/dist/lib/machines.d.ts +9 -1
  211. package/dist/lib/machines.js +15 -1
  212. package/dist/lib/node-adapter.js +13 -0
  213. package/dist/lib/record-output.d.ts +22 -0
  214. package/dist/lib/record-output.js +78 -0
  215. package/dist/lib/roles.d.ts +3 -3
  216. package/dist/lib/scoped-memory-command.d.ts +18 -0
  217. package/dist/lib/scoped-memory-command.js +53 -2
  218. package/dist/lib/space-charter.d.ts +49 -0
  219. package/dist/lib/space-charter.js +78 -0
  220. package/dist/lib/space-loop-migration.d.ts +18 -0
  221. package/dist/lib/space-loop-migration.js +128 -0
  222. package/dist/lib/space-notebook.d.ts +22 -0
  223. package/dist/lib/space-notebook.js +140 -0
  224. package/dist/lib/space-proposal-decision-command.d.ts +36 -0
  225. package/dist/lib/space-proposal-decision-command.js +108 -0
  226. package/dist/lib/space-proposals.d.ts +44 -0
  227. package/dist/lib/space-proposals.js +186 -0
  228. package/dist/lib/space-templates.d.ts +8 -0
  229. package/dist/lib/space-templates.js +17 -0
  230. package/dist/lib/space-write-policy.d.ts +24 -0
  231. package/dist/lib/space-write-policy.js +103 -0
  232. package/dist/lib/task-agent-context-capabilities.d.ts +10 -0
  233. package/dist/lib/task-agent-context-capabilities.js +22 -0
  234. package/dist/lib/task-agent-context-compatibility.d.ts +9 -0
  235. package/dist/lib/task-agent-context-compatibility.js +30 -0
  236. package/dist/lib/task-asset-upload.d.ts +22 -0
  237. package/dist/lib/task-asset-upload.js +257 -29
  238. package/dist/lib/task-batch.d.ts +15 -1
  239. package/dist/lib/task-batch.js +35 -2
  240. package/dist/lib/task-operation-watch.d.ts +66 -0
  241. package/dist/lib/task-operation-watch.js +159 -0
  242. package/dist/lib/task-resolver.d.ts +1 -1
  243. package/dist/lib/task-resolver.js +4 -3
  244. package/dist/lib/task-work.d.ts +1 -1
  245. package/dist/lib/task-work.js +35 -1
  246. package/dist/lib/task-write-attribution.d.ts +14 -0
  247. package/dist/lib/task-write-attribution.js +35 -2
  248. package/dist/lib/triggers.d.ts +11 -0
  249. package/dist/lib/triggers.js +7 -0
  250. package/dist/lib/wiki-update.d.ts +1 -0
  251. package/dist/lib/wiki-update.js +10 -1
  252. package/dist/lib/wiki.js +4 -1
  253. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.d.ts +9 -1
  254. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/computerWire.js +31 -15
  255. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonToolApproval.js +1 -0
  256. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +13 -7
  257. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.d.ts +370 -0
  258. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/guidedUpdate.js +315 -0
  259. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.d.ts +178 -0
  260. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/maintenanceReportWire.js +262 -0
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.d.ts +51 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/toolApprovalSignature.js +106 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.d.ts +9 -1
  264. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/computerWire.js +32 -5
  265. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonToolApproval.js +1 -0
  266. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +33 -14
  267. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.d.ts +370 -0
  268. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/guidedUpdate.js +404 -0
  269. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.d.ts +178 -0
  270. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/maintenanceReportWire.js +342 -0
  271. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.d.ts +51 -0
  272. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/toolApprovalSignature.js +138 -0
  273. package/dist/node_modules/@skrr-ai/auth-core/package.json +20 -0
  274. package/dist/node_modules/@skrr-ai/data-provider/index.js +27085 -20250
  275. package/oclif.manifest.json +10566 -7247
  276. package/package.json +20 -2
@@ -2,7 +2,7 @@
2
2
  "oversky-task-management": {
3
3
  "key": "oversky-task-management",
4
4
  "name": "skrr Task Management",
5
- "templateVersion": 22,
5
+ "templateVersion": 23,
6
6
  "body": "## OverSky Task Management\n\nUse `skrr` as the durable record for work that must outlive this conversation.\n\n### One concept, one record\n\n- **Task** says what is owed and where it stands.\n- **Expectation** says what must be true before the Task can close.\n- **Update** says what meaningfully changed during execution.\n- **Resume Point** says where one Run can safely continue after interruption.\n- **Result** says what the work concluded or returned.\n- **Deliverable** identifies a concrete Resource version produced by the work.\n- **Review** says whether another actor accepted one exact Result.\n- **Comment** is replyable collaboration.\n- **Activity** is the immutable audit projection. A Run transcript is detailed telemetry, not Activity.\n\nDo not put a Result in a Comment, use an Update as a transcript, or use review words such as\n“approved” for a domain conclusion such as NO-SHIP.\n\nThe platform derives Human, Agent, Run, session, and recording provenance from the authenticated\nruntime. Never supply or invent an Agent identity. An Update has one author; a multi-Agent summary\nmay cite contributors and their source Runs or Updates.\n\n### During execution: write Updates at meaningful boundaries\n\nDo not log routine reads, tool calls, heartbeats, token counts, or repeated “still working” narration.\nRecord an Update only when a future reader or replacement Run needs to know that shared state changed:\n\nEach type has a REQUIRED `--data` shape, and the array fields are required even when empty.\nOptional fields are marked `?`. The server rejects a missing required field, so guessing from the\nprose costs a round trip:\n\n- `progress`: a material part is complete.\n `{completedSinceLast: string[], nextActions: string[], currentFocus?}`\n- `finding`: a newly discovered fact. `impact` is optional; the evidence references are not.\n `{finding, evidenceRefs: [], impact?, severity?}`\n- `decision`: a choice now constrains the work.\n `{decision, rationale, alternativesConsidered?: string[]}`\n- `blocker`: work cannot continue. `nextOwner` is a principal OBJECT, not a name.\n `{cause, requiredAction, nextOwner: {kind: \"human\", userId}}`\n- `verification`: a claim was checked. `result` is one of `passed`, `failed`, `inconclusive`.\n `{claim, result, evidenceRefs: []}`\n- `ready_for_review`: created by Result submission; do not author it separately.\n\n`--reference <kind>:<id>` sets the Update's OWN references. It does not populate\n`data.evidenceRefs`, which belongs inside `--data`.\n\nUse one stable idempotency key only for retries of the identical write. Reusing it for different\ncontent is rejected.\n\nExample:\n\n```bash\nskrr tasks updates add <task-id> \\\n --type progress \\\n --summary \"Migration review complete\" \\\n --data '{\"completedSinceLast\":[\"Checked migrations 001-014\"],\"currentFocus\":\"Authorization review\",\"nextActions\":[\"Inspect legacy adapters\"]}' \\\n --idempotency-key \"<task-id>:migration-review\"\n```\n\nFor a decision:\n\n```bash\nskrr tasks updates add <task-id> \\\n --type decision \\\n --summary \"Use a first-class Resource identity\" \\\n --data '{\"decision\":\"Use WorkResource plus immutable versions\",\"rationale\":\"A URL can move and cannot identify the version a reviewer accepted\"}' \\\n --idempotency-key \"<task-id>:resource-model\"\n```\n\nFor a finding — `evidenceRefs` is required and may be empty:\n\n```bash\nskrr tasks updates add <task-id> \\\n --type finding \\\n --summary \"The closure gate reads assessments by exact specVersion\" \\\n --data '{\"finding\":\"A cosmetic title edit bumps specVersion, so every prior assessment stops counting\",\"impact\":\"A task with expectations cannot be closed after a rename\",\"evidenceRefs\":[{\"kind\":\"code\",\"uri\":\"api/server/services/TaskClosurePolicy/index.js\"}]}' \\\n --idempotency-key \"<task-id>:spec-bump-revocation\"\n```\n\nFor a blocker — `nextOwner` is a principal object:\n\n```bash\nskrr tasks updates add <task-id> \\\n --type blocker \\\n --summary \"Waiting on production database credentials\" \\\n --data '{\"cause\":\"The migration needs write access to the prod cluster\",\"requiredAction\":\"Grant the deploy role write access, or run the migration yourself\",\"nextOwner\":{\"kind\":\"human\",\"userId\":\"<user-id>\"}}' \\\n --idempotency-key \"<task-id>:needs-prod-credentials\"\n```\n\nFor a verification:\n\n```bash\nskrr tasks updates add <task-id> \\\n --type verification \\\n --summary \"Re-ran the closure flow end to end after the fix\" \\\n --data '{\"claim\":\"A renamed task with one assessed expectation can close\",\"result\":\"passed\",\"evidenceRefs\":[{\"kind\":\"task\",\"id\":\"<task-id>\"}]}' \\\n --idempotency-key \"<task-id>:closure-reverify\"\n```\n\n### Media: screenshots and recordings\n\n`--attach <path>` (repeatable) on `updates add`, `result submit`, `complete`, or `comments add`\nuploads a file as a Task asset and cites it; identical bytes dedupe. In-run proof goes on a\n`verification` or `progress` Update, the final before/after on the Result, never the description.\nAn upload is `self_reported` evidence and counts toward the storage quota. Images keep full\nresolution minus metadata (Runs get a model-sized copy); undecodable bytes get 422, a type the bytes\ncontradict 415, and SVG downloads as a file. An agent uploads evidence (the default) or\n`--role deliverable`; `input` is refused (403): ask a person to run `attachments set-role`. Cite a\ndeliverable as `--evidence task_attachment:<asset-id>` when you assess its Expectation.\n\n### Recovery: Update delta and Resume Point snapshot\n\nWhen a semantic boundary should also be resumable, add `--run-id` and `--resume` to the same Update\ncommand. The server commits the visible Update and the Run-bound Resume Point together. The snapshot\nmust be self-contained enough for another capable Agent to continue without replaying the transcript:\ncompleted work, current focus, next actions, open questions, and references to decisions, evidence,\nDeliverables, and durable workspace state.\n\nThe runtime may save a silent crash-safety snapshot with:\n\n```bash\nskrr tasks resume save <task-id> --run-id <run-id> --snapshot '<resume-json>'\n```\n\nNever put credentials, secrets, permission grants, bearer tokens, or leases in a Resume Point. A\nresumed Run reloads the current Task Revision, re-authorizes, validates referenced files/commits, and\nreads Updates recorded after the snapshot:\n\n```bash\nskrr tasks resume show <task-id> --run-id <run-id>\n```\n\n### Deliverables: lightweight input, first-class identity\n\nA user may provide one coordinate, but the platform records a WorkResource, its exact version, the\nTask relationship, and an evidence-backed availability observation. External reference is the\ndefault; OverSky-managed content is used only when retention, stable access, preview/search, or\npolicy requires a copy.\n\n```bash\nskrr tasks deliverable add <task-id> \\\n file/markdown:docs/reviews/report.md@docs/reviews/report.md \\\n --status in_review\n```\n\nA recording made by its producer is always self-reported; the caller cannot promote itself to a\nHuman confirmation, system observation, or provider receipt. A local path is only local availability. A commit, provider revision, digest, or managed object\nfreezes the exact version. Do not call “in the conversation” a Deliverable.\n\nA Human confirms the exact linked version, not a retyped URL or name:\n\n```bash\nskrr tasks deliverable confirm <task-id> <task-resource-link-id> --reason \"Opened and checked this exact version\"\n```\n\n### Result and Review are separate\n\nSubmit the finished answer once:\n\n```bash\nskrr tasks result submit <task-id> \\\n --summary \"NO-SHIP until remediation Gates A-D pass\" \\\n --conclusion-type release_recommendation \\\n --conclusion-json '{\"posture\":\"no_ship\"}' \\\n --deliverable <task-resource-link-id>\n```\n\nAn amendment creates a new Result version and explicitly supersedes the previous one. There is one\ncurrent Result; old versions remain in Activity and are not ordinary Updates.\n\nOnly another actor records acceptance, against that exact Result:\n\n```bash\nskrr tasks review <task-id> --result-id <result-id> --decision accepted --rationale \"Evidence checked\"\n```\n\n“Accepted” means the submitted work was accepted. NO-SHIP, BUY, REJECT, root cause, experiment\ndecision, and similar subject-matter conclusions belong in `Result.conclusion`, never in Review.\n\nUse `skrr tasks complete` only when submitting the Result and advancing workflow in one closeout.\nDo not call `tasks report` before or after it; `tasks complete` already creates the canonical Result.\n\nSubmitting a Result is not the same as closing the Task, and being an Agent is not by itself a\nreason to stop. Closing is always the closer's call — unmet Expectations or a Changes-requested\nReview do not prevent it; they are RECORDED on the close (`resolution.closure`), so closing over\nthem hides nothing and settles nothing. The one exception is an owner-declared required Review\n(`--review-policy required`): a non-owner cannot close without it — unless they close with\n`--force`, which records the override on the resolution — and the owner always can.\n\nSo do not predict the outcome. `result submit` reports where the Task stands after the write —\nread that, and if you need to ask again later:\n\n```bash\nskrr tasks expectations <task-id> # what is owed, and why the Result cannot be accepted yet\n```\n\nSay “ready for review” only when something you read actually said so.\n\n### Expectations\n\nExpectations are free text first. A typed check explains how the statement can be assessed; it never\nreplaces the sentence.\n\nAn Expectation is an obligation, not a lock: it does not stop a close, it is recorded on one — and\nclosing over it settles nothing. Write each one so it can\nbe judged — say what would satisfy it and how that could be checked — because an Expectation authored\nwithout a thought about verification is one you will later be unable to settle honestly. When you\ncreate several across several Tasks, say how many you created and who they fall to; a set of gates\nnobody was told about is work assigned by accident. A Deliverable requirement is one typed Expectation, not a second\n`requiredDeliverables` list. Actual Deliverables are recorded only after something exists.\n\nRead what is owed, then settle it — never rewrite the declaration to make it settleable:\n\n```bash\nskrr tasks expectations <task-id> # what is owed, and how far each one got\nskrr tasks expectations assess <task-id> <expectation-id> \\\n --decision met \\\n --reason \"Ran the suite named in the statement; 49/49 pass\" \\\n --evidence code:<commit>\nskrr tasks expectations assess <task-id> --all \\\n --decision met --reason \"...\" # every one still awaiting a judgement\n```\n\n**You may assess any Expectation that takes a judgement.** A `deliverable` Expectation is not one —\nit settles through its linked Resource and evidence, so record the Deliverable rather than\nassessing it. The assessment is recorded as YOURS — every surface shows `met (agent)` against\n`met (person)` — and a human can override it. The control is attribution, not permission, so what\nyou owe is not restraint but evidence: cite what you actually checked, and prefer `--evidence` and\n`--resource-link-id` over prose.\n\nAssess what you VERIFIED. Not what you intended, not what you inferred from having finished the\nwork. “I could not check this” is a real and useful answer — record it `unmet` with the reason\nrather than leaving it silent, because an unassessed Expectation and one you tried and failed to\nverify send the next reader to different places.\n\nA waiver is a judgement about scope — that this is no longer owed — and it stays a person's call:\nif the honest answer is \"this should be waived,\" say so in an Update or Comment and let a person\nrecord it. Do not delete the Expectation to hide that it was owed.\n\nChanging an Expectation's wording advances the Task specification and **revokes every assessment\nalready recorded** — fix the statement before people start settling it, not after.\n\n### Labels — say what PROGRAM this belongs to\n\nA label is cross-cutting classification: which recurring program a Space or Task\nbelongs to (`dogfood`, `incident`, `experiment`, `migration`), not what state it\nis in. Status, priority and assignee are first-class fields and already answer\ntheir own questions — a label that restates one is noise that will drift out of\nsync with the field it copies.\n\nThe vocabulary is DECLARED, not invented. Read it before you use it:\n\n```bash\nskrr labels list\n```\n\nAttach from that list, never a name you made up. A second spelling of an\nexisting label (`dog-food` beside `dogfood`) is worse than no label at all,\nbecause the whole value of one is that filtering on it returns everything:\n\n```bash\nskrr spaces labels add <space-id> --label <label-id>\nskrr tasks labels attach <task-id> --label <label-id>\n```\n\nCreate a label only when the program it names is real and recurring, and say\nwhich entities it applies to — a label created without `--applicable-to space`\ncannot be attached to a Space, which is how a vocabulary comes to exist that\nnothing can use:\n\n```bash\nskrr labels create --name <name> --applicable-to space --applicable-to task\n```\n\nWhen you create a Space or Task for a recurring program, label it as you create\nit (`--label <id>` on `spaces`, `tasks` and `goals create`). The create\nresponse carries a `_labelGuidance` hint naming the applicable vocabulary WITH\nITS IDS when you attached none — read it, and attach in one follow-up call if\none of them fits. It may also carry `suggested` — a proposal, not a decision;\nattach is your call. `skrr tasks labels suggest <task-id>` asks it of a Task.\n\nIf nothing in the vocabulary fits, attach nothing. \"No label\" is a correct\nanswer and a wrong label is not.\n\n### Comments and blockers\n\nUse a Comment only for a question, answer, or review discussion where a reply is useful. For a\nblocker, first record the typed blocker Update, then move the Task to blocked. Add a Comment only if\nsomeone actually needs to respond.\n\n### Creating and placing Tasks\n\nCreate a Task without asking when work needs tracking across meaningful steps, sessions, or\nfollow-up. Avoid duplicates and do not create Tasks for trivial actions that finish immediately.\n\n```bash\nskrr tasks list --q \"<relevant title>\"\nskrr tasks create --title \"<concise requested result>\" --description \"<scope and constraints>\" --json\nskrr tasks move <task-id> --status in_progress\n```\n\nA Space is a durable ownership, collaboration, and authorization boundary. Use an identified Space\nwhen the work clearly belongs there; otherwise create the Task unscoped. Ask before creating a new\nSpace. An Agent works a Task in a Space only as a Space member, or when it is shared with no one\nelse and its owner assigns and starts it; anything else is refused. Do not widen Agent membership\nmerely to force one task assignment.\n\n### Closing a Task out\n\nThe model above says what each record IS. This is the order they go in, and what each step can\nrefuse:\n\n1. **Record what exists** — `skrr tasks deliverable add`, binding it to the Expectation it answers\n with `--expectation <id>` whenever one applies. Evidence that arrives bound is evidence the next\n reader does not have to reconstruct.\n2. **Submit the answer** — `skrr tasks result submit`. This does not close the Task. Read what it\n reports back.\n3. **Read what is still owed** — `skrr tasks expectations <task-id>`.\n4. **Settle what you verified** — `skrr tasks expectations assess`, citing evidence. `--all` covers\n every Expectation still awaiting a judgement.\n5. **Read the Task state again and report THAT** — not what you intended it to be.\n\nTwo things still matter regardless of how complete the work is: an unmet Expectation, and a\n`changes_requested` Review. Neither blocks a close — but both are stamped onto it\n(`resolution.closure`), so closing over them is a recorded choice, not a clean finish. Neither is\na failure state; both are somebody's turn.\n\nStep 5 is the one that goes wrong quietly, and not only here: before reporting a check as passed,\nconfirm you measured what you think you measured. A pipeline's exit status is the LAST command's, so\n`cmd | tail` reports on `tail`. A filter may match something other than what you meant — a path\npattern can match the directory you are working in. When a result will be reported to a person as\nfact, re-run it in the simplest form that can only mean one thing.\n\nAcross a whole Space, `skrr tasks review-queue --space <space-id>` names every outstanding\njudgement at once, with its evidence and the command that settles it.\n\n### Share readable links and close the loop\n\nShare clickable Task, Space, Goal, and Commitment URLs, never bare IDs. Use the URL returned by the\nCLI; do not construct one. If needed:\n\n```bash\nskrr tasks show <task-id> --json\nskrr spaces show <space-id> --json\nskrr goals show <goal-id> --json\nskrr commitments show <commitment-id> --json\n```\n\nBefore your final response, verify every Task you created or touched reflects reality. If work is\nready for independent review, leave it in review. If blocked, record the blocker and required action.\nIf genuinely complete and accepted under the Task's policy, close it. Include the Task URL in the\nfinal handoff. Never claim a write succeeded unless the CLI returned success.\n\n### Coming back later\n\nYou can arrange to return to something. Four shapes, and picking the right one\nmatters more than the wording:\n\n**Reminder** — the user asked to be reminded. Keep their words; a reminder you\nhave rephrased into your own voice is no longer the thing they asked for.\n\n**Watch** — something has an asynchronous tail: CI, a review, an approval, a\nlong job, a metric. If the condition is machine-checkable (an endpoint plus a\npredicate), say so — that costs nothing and is guaranteed to terminate. Only\nwrite a prose check recipe when the check genuinely needs your judgement or your\ntools, because that one burns a full turn every time it runs. Always give it an\nabort condition: a watch that cannot stop is a leak.\n\n**Check-in** — you decided the conversation is worth returning to. This is\npermission AND restraint. Come back when you will have something worth saying —\na result that will have arrived, a decision that will be ripe, a thread you left\ngenuinely open. When you see one, arrange the return yourself and say in one\nline when you will look again. Do not ask whether you should: that hands the\nperson a decision they did not come to make. Do not come back to ask how it is\ngoing. A legacy check-in holds one pending return per conversation. For a sequence\nof checks, use one scheduled lookback plan with 1–10 lifetime steps. When you\nwake, read the original goal, current state and earlier outcomes. Saying\nnothing is a legitimate outcome and often the right one. Stop or cancel\nremaining checks when the work resolves, becomes obsolete or the person cancels.\n\n**Act** — a specific action you already know how to take and the user has already\nagreed to. Not for anything you would still need to ask about.\n\nSettle what you arranged. When you wake and the thing resolved, say so and close\nit out; when it did not, either come back once more with a reason or hand it to\nthe person. Leaving one armed forever is the failure mode here.\n\nFrom a local session, arrange these with `skrr followups` —\n`remind`, `watch`, `checkin`, `act` — and settle them with\n`skrr followups resolve <id> --outcome resolved`.\nFor a bounded lookback plan, use `skrr followups plans create --from-json plan.json`\nand inspect, revise or cancel it with `skrr followups plans` (read `--help`).\nUse 1–10 concrete checks, relative offsets from the completed reply or explicit\nabsolute/local times, and a stop condition. Omit a plan when none is useful.\nA saved plan is not necessarily armed; read the returned receipt before promising a return.\n",
7
7
  "hash": "a4bcb9d4c39e33e8a1dcd12b8a669efe80c67d81c3a8ad90119be73f8352c8b6"
8
8
  }
@@ -22,6 +22,15 @@ type AgentGuideReadFlags = {
22
22
  token?: string;
23
23
  bare?: boolean;
24
24
  };
25
+ /**
26
+ * An opaque correlation hint for guide-read telemetry. It is not
27
+ * authentication or product provenance; the server hashes it before logging
28
+ * and never uses it to authorize or change a response. A daemon supplies
29
+ * `OVERSKY_SESSION_ID`; harnesses that have a more precise turn/run boundary
30
+ * may supply `OVERSKY_GUIDE_READ_CONTEXT` instead.
31
+ */
32
+ export declare const GUIDE_READ_CONTEXT_HEADER = "X-Oversky-Guide-Read-Context";
33
+ export declare function guideReadTelemetryHeaders(env?: Readonly<Record<string, string | undefined>>): Record<string, string> | undefined;
25
34
  /**
26
35
  * The one read path behind `skrr guide` and the legacy
27
36
  * `instructions guide task-management`: the server when a credential answers,
@@ -3,6 +3,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.GUIDE_READ_CONTEXT_HEADER = void 0;
7
+ exports.guideReadTelemetryHeaders = guideReadTelemetryHeaders;
6
8
  exports.readAgentGuide = readAgentGuide;
7
9
  exports.agentGuideJson = agentGuideJson;
8
10
  exports.agentGuideFallbackNotice = agentGuideFallbackNotice;
@@ -18,6 +20,22 @@ const keychain_1 = require("./keychain");
18
20
  const config_1 = require("./config");
19
21
  const node_adapter_1 = require("./node-adapter");
20
22
  const bundle = agent_guides_json_1.default;
23
+ /**
24
+ * An opaque correlation hint for guide-read telemetry. It is not
25
+ * authentication or product provenance; the server hashes it before logging
26
+ * and never uses it to authorize or change a response. A daemon supplies
27
+ * `OVERSKY_SESSION_ID`; harnesses that have a more precise turn/run boundary
28
+ * may supply `OVERSKY_GUIDE_READ_CONTEXT` instead.
29
+ */
30
+ exports.GUIDE_READ_CONTEXT_HEADER = 'X-Oversky-Guide-Read-Context';
31
+ const GUIDE_READ_CONTEXT_MAX_CHARS = 256;
32
+ function guideReadTelemetryHeaders(env = process.env) {
33
+ const context = (env.OVERSKY_GUIDE_READ_CONTEXT ?? env.OVERSKY_SESSION_ID ?? '').trim();
34
+ if (!context || context.length > GUIDE_READ_CONTEXT_MAX_CHARS || /[\r\n]/.test(context)) {
35
+ return undefined;
36
+ }
37
+ return { [exports.GUIDE_READ_CONTEXT_HEADER]: context };
38
+ }
21
39
  function responseHashMatches(response) {
22
40
  return ((0, node_crypto_1.createHash)('sha256').update((0, data_provider_1.renderAgentGuideText)(response), 'utf8').digest('hex') ===
23
41
  response.hash);
@@ -97,7 +115,11 @@ async function readAgentGuide(request, flags) {
97
115
  (0, data_provider_1.setBaseURL)(baseURL);
98
116
  (0, node_adapter_1.setAdapterCredentialOverride)(credential);
99
117
  (0, data_provider_1.setHttpAdapter)((0, node_adapter_1.createNodeAdapter)());
100
- const options = { signal: AbortSignal.timeout(3_000) };
118
+ const telemetryHeaders = guideReadTelemetryHeaders();
119
+ const options = {
120
+ signal: AbortSignal.timeout(3_000),
121
+ ...(telemetryHeaders ? { headers: telemetryHeaders } : {}),
122
+ };
101
123
  response = await readServerGuide(request, options);
102
124
  if (!responseHashMatches(response)) {
103
125
  response = undefined;
@@ -569,6 +569,8 @@ export declare class AgenticStreamClient {
569
569
  */
570
570
  private readonly openPermissionPrompts;
571
571
  private readonly browserPermissionRequests;
572
+ private readonly permissionGenerations;
573
+ private readonly permissionEpochs;
572
574
  /**
573
575
  * Question prompts on the terminal still waiting for an answer, by
574
576
  * questionId, so a server `question:cancel` can close the one it names
@@ -729,6 +731,9 @@ export declare class AgenticStreamClient {
729
731
  private applySessionOutput;
730
732
  private installHandlers;
731
733
  private handlePermission;
734
+ private replayPermissionDecision;
735
+ /** Protected asks record an exact response; neither a click nor its receipt proves execution. */
736
+ private sendBoundPermissionDecision;
732
737
  private handleQuestion;
733
738
  /**
734
739
  * The server closed a permission request (OSK-12197): it expired unanswered,
@@ -163,6 +163,9 @@ async function discardTypeAhead(input, windowMs = exports.TYPE_AHEAD_DISCARD_MS)
163
163
  if (sawInterrupt)
164
164
  process.kill(process.pid, 'SIGINT');
165
165
  }
166
+ const permissionGeneration = (event) => JSON.stringify([
167
+ event.sessionId, event.toolUseId, event.permissionBinding?.epoch, event.permissionBinding?.requestId,
168
+ ]);
166
169
  const PERMISSION_DETAIL_MAX = 400;
167
170
  /**
168
171
  * What a permission request would DO, in one line a person can judge.
@@ -794,6 +797,8 @@ class AgenticStreamClient {
794
797
  */
795
798
  openPermissionPrompts = new Map();
796
799
  browserPermissionRequests = new Set();
800
+ permissionGenerations = new Map();
801
+ permissionEpochs = new Map();
797
802
  /**
798
803
  * Question prompts on the terminal still waiting for an answer, by
799
804
  * questionId, so a server `question:cancel` can close the one it names
@@ -1010,6 +1015,8 @@ class AgenticStreamClient {
1010
1015
  this.stopHandoverTimers();
1011
1016
  this.pendingEnvelopes.clear();
1012
1017
  this.browserPermissionRequests.clear();
1018
+ this.permissionGenerations.clear();
1019
+ this.permissionEpochs.clear();
1013
1020
  this.abandonConnectWait?.();
1014
1021
  const socket = this.socket;
1015
1022
  this.socket = null;
@@ -1627,6 +1634,9 @@ class AgenticStreamClient {
1627
1634
  socket.on('execution:aborted', (event) => {
1628
1635
  if (!match(event))
1629
1636
  return;
1637
+ if (typeof event.permissionEpoch === 'string' && this.permissionEpochs.has(this.options.sessionId) &&
1638
+ this.permissionEpochs.get(this.options.sessionId) !== event.permissionEpoch)
1639
+ return;
1630
1640
  if (this.handover)
1631
1641
  this.requestHandoverRead();
1632
1642
  else
@@ -1635,6 +1645,9 @@ class AgenticStreamClient {
1635
1645
  socket.on('execution:cancelled', (event) => {
1636
1646
  if (!match(event))
1637
1647
  return;
1648
+ if (typeof event.permissionEpoch === 'string' && this.permissionEpochs.has(this.options.sessionId) &&
1649
+ this.permissionEpochs.get(this.options.sessionId) !== event.permissionEpoch)
1650
+ return;
1638
1651
  if (this.handover)
1639
1652
  this.requestHandoverRead();
1640
1653
  else
@@ -1652,13 +1665,23 @@ class AgenticStreamClient {
1652
1665
  socket.on('permission:request', (event) => {
1653
1666
  if (!match(event))
1654
1667
  return;
1668
+ // Authoritative receipt replays can arrive while this generation's TTY
1669
+ // prompt is open. Retire it before the ordinary request dedup check.
1670
+ if (this.replayPermissionDecision(event))
1671
+ return;
1655
1672
  // One ask, one answer (OSK-11796). The same `permission:request` can
1656
1673
  // arrive twice — a `session:join` replay overlapping the live emit, or
1657
1674
  // fan-out through a second room — and each copy used to emit its own
1658
1675
  // `permission.requested` AND send its own approve/deny, so a duplicate
1659
1676
  // delivery produced a duplicate prompt and a racing second decision.
1660
- if (!this.firstTimeSeen('permission', event.toolUseId))
1677
+ if (!this.firstTimeSeen('permission', event.permissionBinding ? permissionGeneration(event) : event.toolUseId))
1661
1678
  return;
1679
+ if (event.toolUseId) {
1680
+ this.openPermissionPrompts.get(event.toolUseId)?.abort();
1681
+ this.permissionGenerations.set(event.toolUseId, { key: permissionGeneration(event), bound: !!event.permissionBinding });
1682
+ if (event.permissionBinding)
1683
+ this.permissionEpochs.set(this.options.sessionId, event.permissionBinding.epoch);
1684
+ }
1662
1685
  void this.handlePermission(event);
1663
1686
  });
1664
1687
  socket.on('permission:cancel', (event) => {
@@ -1683,6 +1706,8 @@ class AgenticStreamClient {
1683
1706
  async handlePermission(event) {
1684
1707
  if (this.closed)
1685
1708
  return;
1709
+ if (this.replayPermissionDecision(event))
1710
+ return;
1686
1711
  this.emit({ ...event, type: 'permission.requested' });
1687
1712
  // Runtime-authored application consent is exclusively an interactive app
1688
1713
  // decision. A TTY or --approve-tools is not that authenticated human lane.
@@ -1721,6 +1746,8 @@ class AgenticStreamClient {
1721
1746
  }
1722
1747
  if (!this.options.approveTools || !this.socket) {
1723
1748
  if (event.toolUseId) {
1749
+ if (await this.sendBoundPermissionDecision(event, false, { reason: 'CLI was not started with --approve-tools', automatic: true }))
1750
+ return;
1724
1751
  this.socket?.emit('permission:deny', {
1725
1752
  sessionId: this.options.sessionId,
1726
1753
  toolUseId: event.toolUseId,
@@ -1755,6 +1782,8 @@ class AgenticStreamClient {
1755
1782
  */
1756
1783
  const interactive = this.options.input !== undefined || Boolean(process.stdin.isTTY);
1757
1784
  if (!interactive) {
1785
+ if (await this.sendBoundPermissionDecision(event, true))
1786
+ return;
1758
1787
  this.socket.emit('permission:approve', {
1759
1788
  sessionId: this.options.sessionId,
1760
1789
  toolUseId: event.toolUseId,
@@ -1772,13 +1801,15 @@ class AgenticStreamClient {
1772
1801
  if (event.toolUseId)
1773
1802
  this.openPermissionPrompts.set(event.toolUseId, prompt);
1774
1803
  const answer = await this.ask(`Allow ${describePermission(event)}? [y/N] `, prompt.signal);
1775
- if (event.toolUseId)
1804
+ if (event.toolUseId && this.openPermissionPrompts.get(event.toolUseId) === prompt)
1776
1805
  this.openPermissionPrompts.delete(event.toolUseId);
1777
1806
  // Closed by the server (it stopped waiting, or another surface answered):
1778
1807
  // there is nothing left to answer, and a late "no" would read as a person's.
1779
1808
  if (prompt.signal.aborted)
1780
1809
  return;
1781
1810
  const approved = /^y(es)?$/i.test(answer.trim());
1811
+ if (await this.sendBoundPermissionDecision(event, approved, approved ? {} : { reason: 'Declined by CLI user' }))
1812
+ return;
1782
1813
  this.socket.emit(approved ? 'permission:approve' : 'permission:deny', {
1783
1814
  sessionId: this.options.sessionId,
1784
1815
  toolUseId: event.toolUseId,
@@ -1787,6 +1818,86 @@ class AgenticStreamClient {
1787
1818
  });
1788
1819
  this.emit({ ...event, type: approved ? 'permission.approved' : 'permission.denied' });
1789
1820
  }
1821
+ replayPermissionDecision(event) {
1822
+ if (!event.permissionBinding || event.decisionStatus === undefined || event.purpose === 'task-command-once' ||
1823
+ event.purpose === 'runtime-elicitation' || event.runtimeElicitation !== undefined || event.toolName === 'ComputerUse')
1824
+ return false;
1825
+ const generation = permissionGeneration(event);
1826
+ if (event.toolUseId) {
1827
+ this.openPermissionPrompts.get(event.toolUseId)?.abort();
1828
+ this.openPermissionPrompts.delete(event.toolUseId);
1829
+ this.permissionGenerations.set(event.toolUseId, { key: generation, bound: true, readonly: true });
1830
+ this.permissionEpochs.set(this.options.sessionId, event.permissionBinding.epoch);
1831
+ }
1832
+ this.firstTimeSeen('permission', generation);
1833
+ const valid = event.decisionStatus === 'recorded' || event.decisionStatus === 'delivery_unconfirmed';
1834
+ const status = event.decisionStatus === 'recorded' ? 'recorded' : 'delivery_unconfirmed';
1835
+ if (this.firstTimeSeen('permission-receipt', `${generation}:${valid ? status : 'unknown'}`))
1836
+ this.emit({
1837
+ ...event, type: status === 'recorded' ? 'permission.response_recorded' : 'permission.delivery_unconfirmed',
1838
+ status, previouslyRecorded: valid, executionConfirmed: false, readonly: true,
1839
+ message: status === 'recorded'
1840
+ ? 'Response recorded. Waiting for execution to admit this response; no tool execution or continuing permission grant is confirmed.'
1841
+ : valid ? 'Response recorded; delivery unconfirmed. Check execution or Stop before starting a new turn.'
1842
+ : 'Your response could not be confirmed. Check execution or Stop before starting a new turn.',
1843
+ });
1844
+ return true;
1845
+ }
1846
+ /** Protected asks record an exact response; neither a click nor its receipt proves execution. */
1847
+ async sendBoundPermissionDecision(event, approved, extra = {}) {
1848
+ if (!event.permissionBinding)
1849
+ return false;
1850
+ const generation = permissionGeneration(event);
1851
+ const current = () => !this.closed && !!event.toolUseId && this.permissionGenerations.get(event.toolUseId)?.key === generation &&
1852
+ !this.permissionGenerations.get(event.toolUseId)?.readonly;
1853
+ if (!current())
1854
+ return true;
1855
+ const unknown = 'Your response could not be confirmed. Check execution or Stop before starting a new turn.';
1856
+ const result = await new Promise((resolve) => {
1857
+ let settled = false;
1858
+ const finish = (receipt) => {
1859
+ if (settled)
1860
+ return;
1861
+ settled = true;
1862
+ clearTimeout(timer);
1863
+ resolve(receipt);
1864
+ };
1865
+ const timer = setTimeout(() => finish({ type: 'permission.delivery_unconfirmed', message: unknown }), 10_000);
1866
+ try {
1867
+ if (!this.socket?.connected) {
1868
+ finish({ type: 'permission.response_refused', message: 'The connection is offline. No response was sent.' });
1869
+ return;
1870
+ }
1871
+ this.socket.emit(approved ? 'permission:approve' : 'permission:deny', {
1872
+ sessionId: this.options.sessionId, toolUseId: event.toolUseId, toolName: event.toolName,
1873
+ permissionBinding: event.permissionBinding, ...extra,
1874
+ }, (receipt) => {
1875
+ const result = receipt?.result;
1876
+ if (receipt?.ok === false && ['invalid', 'unauthorized'].includes(receipt.error?.code || ''))
1877
+ finish({ type: 'permission.response_refused', message: receipt.error?.message || 'The response was refused. No new permission was granted.' });
1878
+ else if (receipt?.ok === false)
1879
+ finish({ type: 'permission.delivery_unconfirmed', message: unknown });
1880
+ else if (!result || result.toolUseId !== event.toolUseId)
1881
+ finish({ type: 'permission.delivery_unconfirmed', message: unknown });
1882
+ else if (result.status === 'closed')
1883
+ finish({ type: 'permission.response_refused', status: 'closed', message: 'This approval request is closed. No new permission was granted.' });
1884
+ else if (result.status === 'delivery_unconfirmed')
1885
+ finish({ type: 'permission.delivery_unconfirmed', status: result.status, previouslyRecorded: result.previouslyRecorded === true,
1886
+ message: result.previouslyRecorded === true ? 'Response recorded; delivery unconfirmed. Check execution or Stop before starting a new turn.' : unknown });
1887
+ else if (receipt.ok === true && result.accepted === true && ['recorded', 'admitted'].includes(result.status || ''))
1888
+ finish({ type: 'permission.response_recorded', status: result.status, message: 'Response recorded. This does not confirm tool execution or a continuing permission grant.' });
1889
+ else
1890
+ finish({ type: 'permission.delivery_unconfirmed', message: unknown });
1891
+ });
1892
+ }
1893
+ catch {
1894
+ finish({ type: 'permission.delivery_unconfirmed', message: unknown });
1895
+ }
1896
+ });
1897
+ if (current())
1898
+ this.emit({ ...event, ...result, decision: approved ? 'allow' : 'deny', executionConfirmed: false });
1899
+ return true;
1900
+ }
1790
1901
  async handleQuestion(event) {
1791
1902
  if (this.closed)
1792
1903
  return;
@@ -1851,13 +1962,17 @@ class AgenticStreamClient {
1851
1962
  */
1852
1963
  handlePermissionCancel(event) {
1853
1964
  const toolUseId = typeof event.toolUseId === 'string' ? event.toolUseId : undefined;
1965
+ const generation = toolUseId ? this.permissionGenerations.get(toolUseId) : undefined;
1966
+ if (generation && (generation.bound || event.permissionBinding) &&
1967
+ (!event.permissionBinding || generation.key !== permissionGeneration(event)))
1968
+ return;
1854
1969
  const open = toolUseId ? this.openPermissionPrompts.get(toolUseId) : undefined;
1855
1970
  // Every answer is echoed to the whole session as `answered` — including
1856
1971
  // this client's own. Only one that closes a prompt still open HERE is news.
1857
1972
  const browserWaiting = toolUseId ? this.browserPermissionRequests.has(toolUseId) : false;
1858
1973
  if (event.reason === 'answered' && !open && !browserWaiting)
1859
1974
  return;
1860
- if (!this.firstTimeSeen('permission-cancel', toolUseId))
1975
+ if (!this.firstTimeSeen('permission-cancel', event.permissionBinding ? permissionGeneration(event) : toolUseId))
1861
1976
  return;
1862
1977
  if (toolUseId) {
1863
1978
  this.openPermissionPrompts.delete(toolUseId);
@@ -2052,6 +2167,10 @@ class AgenticStreamClient {
2052
2167
  output.write(`\n[${event.type}] ${describePermission(event)}\n`);
2053
2168
  this.renderedText = this.answerText();
2054
2169
  }
2170
+ else if (['permission.response_recorded', 'permission.delivery_unconfirmed', 'permission.response_refused'].includes(event.type)) {
2171
+ output.write(`\n[permission] ${String(event.message ?? '')}\n`);
2172
+ this.renderedText = this.answerText();
2173
+ }
2055
2174
  else if (event.type === 'permission.cancelled') {
2056
2175
  // A request the run stopped waiting for (OSK-12197). Without this line
2057
2176
  // an expired approval looked exactly like one still being asked.
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Stable, secret-safe auth diagnostics.
3
+ *
4
+ * Only metadata crosses this boundary: callers must never pass a token, an
5
+ * auth-helper result, or a raw configured URL into an error document.
6
+ */
7
+ export type ServerConfigurationSource = 'OVERSKY_BASE_URL' | 'SKRR_BASE_URL' | 'OVERSKY_SERVER_URL' | 'SKRR_SERVER_URL' | 'configuration';
8
+ /** Matches config.ts precedence while exposing only the selected variable name. */
9
+ export declare function serverConfigurationSource(env?: Record<string, string | undefined>): ServerConfigurationSource;
10
+ /** Scheme + host (+ port) only. URL userinfo, query, path and fragment are unsafe diagnostics. */
11
+ export declare function safeServerOrigin(value: string | null | undefined): string | null;
12
+ export type CredentialResolutionMetadata = {
13
+ token: string | null | undefined;
14
+ source: string | null | undefined;
15
+ kind?: string | null;
16
+ };
17
+ export type AuthResolutionDiagnostic = {
18
+ state: 'resolved' | 'missing';
19
+ source: string | null;
20
+ kind: string | null;
21
+ bareMode: boolean;
22
+ localCredentialStore: 'checked' | 'skipped-by-bare-mode' | 'unavailable-in-managed-runtime';
23
+ authHelper: 'configured' | 'not-configured';
24
+ server: {
25
+ origin: string | null;
26
+ source: ServerConfigurationSource;
27
+ };
28
+ suggestion: string | null;
29
+ };
30
+ export declare function authResolutionDiagnostic({ credential, bareMode, managedRuntime, baseURL, bin, env, }: {
31
+ credential: CredentialResolutionMetadata;
32
+ bareMode: boolean;
33
+ managedRuntime?: boolean;
34
+ baseURL: string | null | undefined;
35
+ bin: string;
36
+ env?: Record<string, string | undefined>;
37
+ }): AuthResolutionDiagnostic;
38
+ /** Human form of a missing-credential diagnostic. Never echoes a raw URL. */
39
+ export declare function describeMissingCredential(diagnostic: AuthResolutionDiagnostic, bin: string): string;
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ /**
3
+ * Stable, secret-safe auth diagnostics.
4
+ *
5
+ * Only metadata crosses this boundary: callers must never pass a token, an
6
+ * auth-helper result, or a raw configured URL into an error document.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.serverConfigurationSource = serverConfigurationSource;
10
+ exports.safeServerOrigin = safeServerOrigin;
11
+ exports.authResolutionDiagnostic = authResolutionDiagnostic;
12
+ exports.describeMissingCredential = describeMissingCredential;
13
+ const SERVER_ENV_PRECEDENCE = [
14
+ 'OVERSKY_BASE_URL',
15
+ 'SKRR_BASE_URL',
16
+ 'OVERSKY_SERVER_URL',
17
+ 'SKRR_SERVER_URL',
18
+ ];
19
+ /** Matches config.ts precedence while exposing only the selected variable name. */
20
+ function serverConfigurationSource(env = process.env) {
21
+ return SERVER_ENV_PRECEDENCE.find((name) => Boolean(env[name]?.trim())) ?? 'configuration';
22
+ }
23
+ /** Scheme + host (+ port) only. URL userinfo, query, path and fragment are unsafe diagnostics. */
24
+ function safeServerOrigin(value) {
25
+ if (!value)
26
+ return null;
27
+ try {
28
+ const url = new URL(value);
29
+ return url.origin && url.origin !== 'null' ? url.origin : null;
30
+ }
31
+ catch {
32
+ return null;
33
+ }
34
+ }
35
+ function authResolutionDiagnostic({ credential, bareMode, managedRuntime = false, baseURL, bin, env = process.env, }) {
36
+ const resolved = Boolean(credential.token);
37
+ const suggestion = resolved
38
+ ? null
39
+ : bareMode
40
+ ? 'Set OVERSKY_TOKEN or SKRR_TOKEN, pass --token, or omit --bare to use a saved session.'
41
+ : managedRuntime
42
+ ? 'This managed runtime accepts only its session credential. Start a new managed session or run the command in your own terminal.'
43
+ : `Run \`${bin} login\` to create a local session.`;
44
+ return {
45
+ state: resolved ? 'resolved' : 'missing',
46
+ source: resolved ? credential.source ?? null : null,
47
+ kind: resolved ? credential.kind ?? null : null,
48
+ bareMode,
49
+ localCredentialStore: bareMode
50
+ ? 'skipped-by-bare-mode'
51
+ : managedRuntime
52
+ ? 'unavailable-in-managed-runtime'
53
+ : 'checked',
54
+ authHelper: env.OVERSKY_AUTH_HELPER?.trim() ? 'configured' : 'not-configured',
55
+ server: { origin: safeServerOrigin(baseURL), source: serverConfigurationSource(env) },
56
+ suggestion,
57
+ };
58
+ }
59
+ /** Human form of a missing-credential diagnostic. Never echoes a raw URL. */
60
+ function describeMissingCredential(diagnostic, bin) {
61
+ const server = diagnostic.server.origin ?? 'the configured server (invalid URL)';
62
+ const source = diagnostic.server.source === 'configuration'
63
+ ? 'saved CLI configuration or its default'
64
+ : diagnostic.server.source;
65
+ const localStore = diagnostic.localCredentialStore === 'skipped-by-bare-mode'
66
+ ? ' `--bare` skips Keychain and file credentials.'
67
+ : diagnostic.localCredentialStore === 'unavailable-in-managed-runtime'
68
+ ? ' This managed runtime cannot use the local credential store.'
69
+ : '';
70
+ return `No usable credential for ${server} (selected by ${source}).${localStore} ${diagnostic.suggestion ?? `Run \`${bin} login\` to sign in.`}`;
71
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Advertise the executable that is actually running. This identity is useful
3
+ * for compatibility presentation only; server-side authorization must derive
4
+ * from authenticated principals and durable Task state instead.
5
+ */
6
+ export declare function configureCliClientIdentity(packageVersion: unknown, platform?: NodeJS.Platform): void;
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.configureCliClientIdentity = configureCliClientIdentity;
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ /**
6
+ * Advertise the executable that is actually running. This identity is useful
7
+ * for compatibility presentation only; server-side authorization must derive
8
+ * from authenticated principals and durable Task state instead.
9
+ */
10
+ function configureCliClientIdentity(packageVersion, platform = process.platform) {
11
+ (0, data_provider_1.setClientIdentity)({
12
+ platform: 'cli',
13
+ version: typeof packageVersion === 'string' && packageVersion.trim() ? packageVersion.trim() : 'unknown',
14
+ os: (0, data_provider_1.normalizeOs)(platform),
15
+ });
16
+ }
@@ -0,0 +1,52 @@
1
+ import { type CliInstallKind } from './update-check';
2
+ type InstallationSource = 'active' | 'path' | 'well_known';
3
+ export type CliInstallationCandidate = {
4
+ /** Every executable spelling that resolves to this one package root. */
5
+ paths: string[];
6
+ /** The resolved executable, when the filesystem made it available. */
7
+ realPath: string | null;
8
+ packageRoot: string;
9
+ version: string;
10
+ owner: CliInstallKind;
11
+ updateCommand: string;
12
+ sources: InstallationSource[];
13
+ /** Zero is the command shell's first PATH hit; null means not on PATH. */
14
+ pathPrecedence: number | null;
15
+ active: boolean;
16
+ };
17
+ export type UnrecognisedCliExecutable = {
18
+ path: string;
19
+ realPath: string | null;
20
+ source: InstallationSource;
21
+ pathPrecedence: number | null;
22
+ };
23
+ export type CliInstallationDiagnostics = {
24
+ active: CliInstallationCandidate | null;
25
+ candidates: CliInstallationCandidate[];
26
+ unrecognisedExecutables: UnrecognisedCliExecutable[];
27
+ /** Distinct versions that a shell can choose directly from PATH. */
28
+ pathVersions: string[];
29
+ hasVersionDrift: boolean;
30
+ remedy: string | null;
31
+ };
32
+ export type CliInstallationDiagnosticsOptions = {
33
+ /** The package root used by the currently executing CLI. */
34
+ activeRoot?: string;
35
+ /** The process entry point, when it is available. */
36
+ activeEntry?: string;
37
+ env?: NodeJS.ProcessEnv;
38
+ platform?: NodeJS.Platform;
39
+ /** Lets focused tests avoid inspecting this machine's global install paths. */
40
+ wellKnownPaths?: readonly string[];
41
+ };
42
+ /**
43
+ * Inspect executable paths without ever starting another CLI.
44
+ *
45
+ * `doctor` is often invoked because PATH is already suspect. Running each
46
+ * discovered `skrr --version` would make the report execute an arbitrary
47
+ * shadowing binary. This reader follows symlinks and reads package metadata
48
+ * only; a candidate that cannot prove it belongs to this CLI is reported as
49
+ * unrecognised rather than trusted.
50
+ */
51
+ export declare function inspectCliInstallations(options?: CliInstallationDiagnosticsOptions): CliInstallationDiagnostics;
52
+ export {};