@skrr-ai/cli 0.1.94 → 0.1.96

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 (298) hide show
  1. package/README.md +175 -165
  2. package/bin/run.js +32 -0
  3. package/bin/update-npm-runner.cjs +33 -0
  4. package/bin/update-session.cjs +194 -0
  5. package/bin/update-worker.cjs +208 -0
  6. package/dist/base-command.js +0 -15
  7. package/dist/commands/agent/message/send.d.ts +2 -0
  8. package/dist/commands/agent/message/send.js +75 -5
  9. package/dist/commands/agents/chat.js +1 -1
  10. package/dist/commands/agents/create.d.ts +0 -3
  11. package/dist/commands/agents/create.js +0 -26
  12. package/dist/commands/agents/egress.d.ts +17 -0
  13. package/dist/commands/agents/egress.js +84 -0
  14. package/dist/commands/agents/mailbox/messages.d.ts +14 -0
  15. package/dist/commands/agents/mailbox/messages.js +52 -0
  16. package/dist/commands/agents/mailbox/show.d.ts +13 -0
  17. package/dist/commands/agents/mailbox/show.js +52 -0
  18. package/dist/commands/agents/operating-plan.js +8 -0
  19. package/dist/commands/agents/roles/capture.d.ts +16 -0
  20. package/dist/commands/agents/roles/capture.js +53 -0
  21. package/dist/commands/agents/roles/install.d.ts +1 -0
  22. package/dist/commands/agents/roles/install.js +8 -1
  23. package/dist/commands/agents/roles/preview.d.ts +2 -0
  24. package/dist/commands/agents/roles/preview.js +27 -3
  25. package/dist/commands/agents/update.d.ts +0 -3
  26. package/dist/commands/agents/update.js +1 -31
  27. package/dist/commands/agents/usage-cap.d.ts +4 -1
  28. package/dist/commands/agents/usage-cap.js +49 -10
  29. package/dist/commands/code/doctor.d.ts +1 -0
  30. package/dist/commands/code/doctor.js +6 -0
  31. package/dist/commands/code/index.js +4 -0
  32. package/dist/commands/code/install.d.ts +5 -62
  33. package/dist/commands/code/install.js +86 -279
  34. package/dist/commands/code/jobs/show.js +12 -0
  35. package/dist/commands/code/rollback.d.ts +8 -0
  36. package/dist/commands/code/rollback.js +20 -0
  37. package/dist/commands/code/update.d.ts +8 -37
  38. package/dist/commands/code/update.js +134 -213
  39. package/dist/commands/commitments/action-proposals/decide.d.ts +2 -0
  40. package/dist/commands/commitments/action-proposals/decide.js +12 -0
  41. package/dist/commands/commitments/action-proposals.js +7 -0
  42. package/dist/commands/commitments/create.d.ts +1 -0
  43. package/dist/commands/commitments/create.js +17 -1
  44. package/dist/commands/commitments/delivery.d.ts +1 -0
  45. package/dist/commands/commitments/delivery.js +11 -1
  46. package/dist/commands/commitments/doctor.js +4 -0
  47. package/dist/commands/commitments/effective-policy.js +26 -0
  48. package/dist/commands/commitments/endpoints/create.js +5 -2
  49. package/dist/commands/commitments/observations.d.ts +10 -0
  50. package/dist/commands/commitments/observations.js +88 -0
  51. package/dist/commands/commitments/pause.d.ts +1 -1
  52. package/dist/commands/commitments/pause.js +4 -3
  53. package/dist/commands/commitments/preflight.js +2 -1
  54. package/dist/commands/commitments/reports.d.ts +2 -0
  55. package/dist/commands/commitments/reports.js +22 -4
  56. package/dist/commands/commitments/update.d.ts +1 -0
  57. package/dist/commands/commitments/update.js +35 -5
  58. package/dist/commands/daemon/install.js +4 -0
  59. package/dist/commands/generate-recovery-code.d.ts +13 -1
  60. package/dist/commands/generate-recovery-code.js +146 -13
  61. package/dist/commands/grants/create.d.ts +1 -0
  62. package/dist/commands/grants/create.js +8 -0
  63. package/dist/commands/grants/rotate.d.ts +5 -0
  64. package/dist/commands/grants/rotate.js +13 -1
  65. package/dist/commands/grants/update.d.ts +12 -0
  66. package/dist/commands/grants/update.js +27 -0
  67. package/dist/commands/instructions/guide.d.ts +20 -0
  68. package/dist/commands/instructions/guide.js +84 -0
  69. package/dist/commands/instructions/install.js +57 -33
  70. package/dist/commands/instructions/setup.d.ts +14 -0
  71. package/dist/commands/instructions/setup.js +72 -0
  72. package/dist/commands/integrations/execute.js +1 -1
  73. package/dist/commands/login.d.ts +5 -6
  74. package/dist/commands/login.js +27 -11
  75. package/dist/commands/logout.js +7 -7
  76. package/dist/commands/machines/hosted/start.js +7 -1
  77. package/dist/commands/machines/list.js +4 -4
  78. package/dist/commands/measures/confirm.d.ts +1 -7
  79. package/dist/commands/measures/confirm.js +15 -14
  80. package/dist/commands/measures/list.d.ts +1 -7
  81. package/dist/commands/measures/list.js +1 -7
  82. package/dist/commands/measures/record.js +4 -4
  83. package/dist/commands/measures/show.d.ts +11 -0
  84. package/dist/commands/measures/show.js +26 -0
  85. package/dist/commands/northstars/governance/configure.d.ts +1 -0
  86. package/dist/commands/northstars/governance/configure.js +8 -1
  87. package/dist/commands/proposals/discard.d.ts +5 -0
  88. package/dist/commands/proposals/discard.js +8 -0
  89. package/dist/commands/proposals/keep.d.ts +5 -0
  90. package/dist/commands/proposals/keep.js +8 -0
  91. package/dist/commands/proposals/list.d.ts +15 -0
  92. package/dist/commands/proposals/list.js +74 -0
  93. package/dist/commands/proposals/show.d.ts +12 -0
  94. package/dist/commands/proposals/show.js +27 -0
  95. package/dist/commands/proposals/snooze.d.ts +5 -0
  96. package/dist/commands/proposals/snooze.js +8 -0
  97. package/dist/commands/security/overview.d.ts +11 -0
  98. package/dist/commands/security/overview.js +158 -0
  99. package/dist/commands/signals/monitors/create.d.ts +9 -0
  100. package/dist/commands/signals/monitors/create.js +29 -0
  101. package/dist/commands/signals/monitors/list.d.ts +8 -0
  102. package/dist/commands/signals/monitors/list.js +16 -0
  103. package/dist/commands/signals/monitors/pause.d.ts +11 -0
  104. package/dist/commands/signals/monitors/pause.js +17 -0
  105. package/dist/commands/signals/monitors/resume.d.ts +11 -0
  106. package/dist/commands/signals/monitors/resume.js +17 -0
  107. package/dist/commands/signals/monitors/revise.d.ts +12 -0
  108. package/dist/commands/signals/monitors/revise.js +25 -0
  109. package/dist/commands/signals/monitors/show.d.ts +11 -0
  110. package/dist/commands/signals/monitors/show.js +17 -0
  111. package/dist/commands/signals/monitors/test.d.ts +12 -0
  112. package/dist/commands/signals/monitors/test.js +25 -0
  113. package/dist/commands/signals/monitors/transitions.d.ts +11 -0
  114. package/dist/commands/signals/monitors/transitions.js +17 -0
  115. package/dist/commands/signals/monitors/validate.d.ts +9 -0
  116. package/dist/commands/signals/monitors/validate.js +24 -0
  117. package/dist/commands/signals/schemas/list.d.ts +8 -0
  118. package/dist/commands/signals/schemas/list.js +16 -0
  119. package/dist/commands/signals/schemas/show.d.ts +11 -0
  120. package/dist/commands/signals/schemas/show.js +17 -0
  121. package/dist/commands/signals/sources/list.d.ts +8 -0
  122. package/dist/commands/signals/sources/list.js +16 -0
  123. package/dist/commands/signals/sources/receipts.d.ts +11 -0
  124. package/dist/commands/signals/sources/receipts.js +17 -0
  125. package/dist/commands/signals/sources/show.d.ts +11 -0
  126. package/dist/commands/signals/sources/show.js +17 -0
  127. package/dist/commands/spaces/claim.d.ts +12 -0
  128. package/dist/commands/spaces/claim.js +42 -0
  129. package/dist/commands/spaces/ownership.d.ts +12 -0
  130. package/dist/commands/spaces/ownership.js +46 -0
  131. package/dist/commands/spaces/usage.d.ts +12 -0
  132. package/dist/commands/spaces/usage.js +28 -0
  133. package/dist/commands/spaces/workflows/run.js +3 -0
  134. package/dist/commands/spaces/workflows/runs.js +2 -1
  135. package/dist/commands/spaces/workflows/update.d.ts +6 -1
  136. package/dist/commands/spaces/workflows/update.js +34 -2
  137. package/dist/commands/tasks/cancel-cascade.js +1 -1
  138. package/dist/commands/tasks/cascade-status.d.ts +2 -0
  139. package/dist/commands/tasks/cascade-status.js +27 -1
  140. package/dist/commands/tasks/create-tree.d.ts +5 -1
  141. package/dist/commands/tasks/create-tree.js +35 -7
  142. package/dist/commands/tasks/create.d.ts +4 -0
  143. package/dist/commands/tasks/create.js +16 -0
  144. package/dist/commands/tasks/deliverable/add.js +12 -3
  145. package/dist/commands/tasks/economics-compare.d.ts +15 -0
  146. package/dist/commands/tasks/economics-compare.js +60 -0
  147. package/dist/commands/tasks/expectations/evaluate.d.ts +13 -0
  148. package/dist/commands/tasks/expectations/evaluate.js +43 -0
  149. package/dist/commands/tasks/interrupt.js +2 -2
  150. package/dist/commands/tasks/list.d.ts +1 -0
  151. package/dist/commands/tasks/list.js +44 -1
  152. package/dist/commands/tasks/pause-schedule.js +1 -1
  153. package/dist/commands/tasks/show.d.ts +5 -0
  154. package/dist/commands/tasks/show.js +17 -0
  155. package/dist/commands/tasks/update.d.ts +4 -0
  156. package/dist/commands/tasks/update.js +9 -0
  157. package/dist/commands/tasks/workflow-findings.js +2 -2
  158. package/dist/commands/token/list.d.ts +3 -5
  159. package/dist/commands/token/list.js +99 -57
  160. package/dist/commands/triggers/create.d.ts +2 -0
  161. package/dist/commands/triggers/create.js +15 -0
  162. package/dist/commands/update.d.ts +14 -0
  163. package/dist/commands/update.js +61 -0
  164. package/dist/commands/workspaces/grants.d.ts +12 -0
  165. package/dist/commands/workspaces/grants.js +31 -0
  166. package/dist/commands/workspaces/stewardship.d.ts +13 -0
  167. package/dist/commands/workspaces/stewardship.js +34 -0
  168. package/dist/generated/managed-instruction-guides.json +9 -0
  169. package/dist/hooks/init.d.ts +5 -0
  170. package/dist/hooks/init.js +27 -0
  171. package/dist/lib/agent-config.d.ts +0 -12
  172. package/dist/lib/agent-config.js +1 -27
  173. package/dist/lib/agent-mailbox.d.ts +35 -0
  174. package/dist/lib/agent-mailbox.js +55 -0
  175. package/dist/lib/commitment-endpoints.d.ts +2 -1
  176. package/dist/lib/commitment-endpoints.js +2 -5
  177. package/dist/lib/commitment-product.d.ts +3 -1
  178. package/dist/lib/commitment-product.js +10 -0
  179. package/dist/lib/commitment-terms.js +1 -1
  180. package/dist/lib/commitments.d.ts +17 -15
  181. package/dist/lib/commitments.js +173 -137
  182. package/dist/lib/compasses.d.ts +14 -0
  183. package/dist/lib/compasses.js +25 -11
  184. package/dist/lib/daemon-installer.d.ts +2 -0
  185. package/dist/lib/daemon-installer.js +100 -24
  186. package/dist/lib/first-party-harness-doctor.js +31 -1
  187. package/dist/lib/first-party-harness-remote-install.d.ts +71 -0
  188. package/dist/lib/first-party-harness-remote-install.js +310 -0
  189. package/dist/lib/first-party-harness-remote-update.d.ts +46 -0
  190. package/dist/lib/first-party-harness-remote-update.js +242 -0
  191. package/dist/lib/first-party-harness-startup-update.d.ts +26 -0
  192. package/dist/lib/first-party-harness-startup-update.js +166 -0
  193. package/dist/lib/first-party-harness-update.d.ts +46 -0
  194. package/dist/lib/first-party-harness-update.js +289 -0
  195. package/dist/lib/first-party-harness.js +7 -4
  196. package/dist/lib/grants.d.ts +5 -1
  197. package/dist/lib/grants.js +9 -1
  198. package/dist/lib/hosted-machines.d.ts +7 -0
  199. package/dist/lib/mailbox-human-command.d.ts +11 -0
  200. package/dist/lib/mailbox-human-command.js +41 -0
  201. package/dist/lib/managed-instruction-local.d.ts +12 -0
  202. package/dist/lib/managed-instruction-local.js +58 -0
  203. package/dist/lib/managed-instruction-setup.d.ts +35 -0
  204. package/dist/lib/managed-instruction-setup.js +306 -0
  205. package/dist/lib/measures.d.ts +28 -2
  206. package/dist/lib/measures.js +38 -14
  207. package/dist/lib/memory-sync.d.ts +1 -0
  208. package/dist/lib/memory-sync.js +279 -68
  209. package/dist/lib/message-intent.d.ts +6 -2
  210. package/dist/lib/message-intent.js +1 -0
  211. package/dist/lib/northstars.js +2 -1
  212. package/dist/lib/platform-update-local.d.ts +2 -0
  213. package/dist/lib/platform-update-local.js +113 -0
  214. package/dist/lib/platform-update.d.ts +39 -0
  215. package/dist/lib/platform-update.js +84 -0
  216. package/dist/lib/profile-machine-identity-guard.d.ts +2 -0
  217. package/dist/lib/profile-machine-identity-guard.js +13 -0
  218. package/dist/lib/prompt.d.ts +1 -0
  219. package/dist/lib/prompt.js +4 -3
  220. package/dist/lib/proposal-decision-command.d.ts +19 -0
  221. package/dist/lib/proposal-decision-command.js +64 -0
  222. package/dist/lib/proposals.d.ts +8 -0
  223. package/dist/lib/proposals.js +22 -0
  224. package/dist/lib/roles.d.ts +141 -6
  225. package/dist/lib/roles.js +8 -2
  226. package/dist/lib/signals.d.ts +20 -0
  227. package/dist/lib/signals.js +24 -0
  228. package/dist/lib/space-usage.d.ts +3 -0
  229. package/dist/lib/space-usage.js +17 -0
  230. package/dist/lib/stewardship-output.d.ts +4 -0
  231. package/dist/lib/stewardship-output.js +82 -0
  232. package/dist/lib/task-asset-upload.d.ts +3 -1
  233. package/dist/lib/task-asset-upload.js +1 -0
  234. package/dist/lib/task-batch.d.ts +9 -0
  235. package/dist/lib/task-batch.js +94 -17
  236. package/dist/lib/task-economics.d.ts +16 -0
  237. package/dist/lib/task-economics.js +22 -0
  238. package/dist/lib/task-instruction-offer.d.ts +1 -4
  239. package/dist/lib/task-instruction-offer.js +3 -129
  240. package/dist/lib/triggers.d.ts +11 -3
  241. package/dist/lib/triggers.js +58 -1
  242. package/dist/lib/update-check.d.ts +61 -53
  243. package/dist/lib/update-check.js +453 -314
  244. package/dist/lib/usage-discovery.d.ts +10 -0
  245. package/dist/lib/usage-discovery.js +24 -3
  246. package/dist/lib/workflow-run-budget.d.ts +12 -0
  247. package/dist/lib/workflow-run-budget.js +37 -0
  248. package/dist/lib/workspaces.d.ts +48 -0
  249. package/dist/lib/workspaces.js +8 -0
  250. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +4 -0
  251. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +14 -1
  252. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.d.ts +3 -4
  253. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessChannels.js +3 -4
  254. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.d.ts +82 -0
  255. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessReleaseKeys.js +105 -0
  256. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.d.ts +110 -0
  257. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarnessUpdater.js +945 -0
  258. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.d.ts +9 -0
  259. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.js +49 -0
  260. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.d.ts +32 -0
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessPermissionControls.js +62 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +4 -1
  264. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/machineUuid.js +35 -5
  265. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.d.ts +63 -0
  266. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releasePolicy.js +169 -0
  267. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.d.ts +2 -0
  268. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/releaseVersions.js +44 -0
  269. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +4 -0
  270. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +12 -0
  271. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.d.ts +3 -4
  272. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessChannels.js +3 -4
  273. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.d.ts +82 -0
  274. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessReleaseKeys.js +102 -0
  275. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.d.ts +110 -0
  276. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarnessUpdater.js +923 -0
  277. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.d.ts +9 -0
  278. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.js +44 -0
  279. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.d.ts +32 -0
  280. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessPermissionControls.js +59 -0
  281. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -0
  282. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  283. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/machineUuid.js +35 -5
  284. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.d.ts +63 -0
  285. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releasePolicy.js +163 -0
  286. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.d.ts +2 -0
  287. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/releaseVersions.js +41 -0
  288. package/dist/node_modules/@skrr-ai/auth-core/package.json +41 -1
  289. package/dist/node_modules/@skrr-ai/data-provider/index.js +8069 -7121
  290. package/docs/v1/README.md +28 -0
  291. package/docs/v1/api.md +55 -0
  292. package/docs/v1/changelog.md +16 -0
  293. package/docs/v1/check-examples.mjs +178 -0
  294. package/docs/v1/examples/api-readonly.mjs +144 -0
  295. package/docs/v1/operations.md +87 -0
  296. package/docs/v1/quickstart.md +71 -0
  297. package/oclif.manifest.json +49424 -46174
  298. package/package.json +34 -11
@@ -0,0 +1,194 @@
1
+ 'use strict';
2
+
3
+ // This bootstrap uses only Node built-ins: npm may replace the package while
4
+ // the updater runs, so admission and the worker must not depend on its modules.
5
+ const fs = require('node:fs');
6
+ const os = require('node:os');
7
+ const path = require('node:path');
8
+ const crypto = require('node:crypto');
9
+ const { execFileSync } = require('node:child_process');
10
+ // Protocol 1: bakery admission lock, immutable PID/birth/token leases, and
11
+ // process-group mutation ownership. Its wire/locking contract is immutable.
12
+ // Future protocols must coexist with these sessions or require an external
13
+ // package-manager update; selecting a separate lock must never bypass them.
14
+ const COORDINATION_VERSION = 1;
15
+
16
+ function runtimeDir(packageRoot) {
17
+ const key = crypto
18
+ .createHash('sha256')
19
+ .update(path.resolve(packageRoot))
20
+ .digest('hex')
21
+ .slice(0, 24);
22
+ // Installation leases deliberately ignore SKRR_CONFIG_DIR: two profiles or
23
+ // config roots still share the same npm installation.
24
+ return path.join(os.homedir(), '.skrr', 'cli-update-runtime', key);
25
+ }
26
+
27
+ function readJson(file) {
28
+ try {
29
+ return JSON.parse(fs.readFileSync(file, 'utf8'));
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ function writeJson(file, value) {
36
+ fs.mkdirSync(path.dirname(file), { recursive: true, mode: 0o700 });
37
+ const temporary = `${file}.${process.pid}.${crypto.randomBytes(6).toString('hex')}.tmp`;
38
+ try {
39
+ fs.writeFileSync(temporary, JSON.stringify(value), { mode: 0o600, flag: 'wx' });
40
+ fs.renameSync(temporary, file);
41
+ } finally {
42
+ fs.rmSync(temporary, { force: true });
43
+ }
44
+ }
45
+
46
+ function processIdentity(pid) {
47
+ if (process.platform === 'win32') return null;
48
+ try {
49
+ return (
50
+ execFileSync('ps', ['-p', String(pid), '-o', 'lstart='], {
51
+ timeout: 500,
52
+ encoding: 'utf8',
53
+ stdio: ['ignore', 'pipe', 'ignore'],
54
+ }).trim() || null
55
+ );
56
+ } catch {
57
+ return null;
58
+ }
59
+ }
60
+
61
+ function isAlive(owner) {
62
+ if (!owner || !Number.isSafeInteger(owner.pid) || owner.pid <= 0) return false;
63
+ try {
64
+ process.kill(owner.group && process.platform !== 'win32' ? -owner.pid : owner.pid, 0);
65
+ } catch (error) {
66
+ return error.code !== 'ESRCH';
67
+ }
68
+ // A recycled PID is not the process that created this lease. If the OS
69
+ // cannot disclose its identity, remain conservative rather than expiring a
70
+ // live session on a wall-clock timeout.
71
+ const identity = owner.identity && processIdentity(owner.pid);
72
+ return !identity || identity === owner.identity;
73
+ }
74
+
75
+ function wait(milliseconds) {
76
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, milliseconds);
77
+ }
78
+
79
+ function acquireLock(directory, timeoutMs = 1000) {
80
+ // Lamport's bakery algorithm, with one immutable participant filename per
81
+ // acquisition. Reaping a crashed participant never removes a replacement
82
+ // owner's lock (the stale-directory removal race of mkdir mutexes).
83
+ const deadline = Date.now() + timeoutMs;
84
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
85
+ const owner = {
86
+ pid: process.pid,
87
+ identity: processIdentity(process.pid),
88
+ token: crypto.randomUUID(),
89
+ };
90
+ const participant = path.join(directory, `${owner.pid}-${owner.token}.json`);
91
+ const participants = () => {
92
+ const active = [];
93
+ for (const file of fs.readdirSync(directory)) {
94
+ if (!file.endsWith('.json')) continue;
95
+ const target = path.join(directory, file);
96
+ const record = readJson(target);
97
+ if (record && isAlive(record)) active.push(record);
98
+ else if (record) fs.rmSync(target, { force: true });
99
+ }
100
+ return active;
101
+ };
102
+ try {
103
+ writeJson(participant, { ...owner, choosing: true, ticket: 0 });
104
+ const ticket = Math.max(0, ...participants().map((record) => record.ticket || 0)) + 1;
105
+ writeJson(participant, { ...owner, choosing: false, ticket });
106
+ while (true) {
107
+ const blocked = participants().some(
108
+ (other) =>
109
+ other.token !== owner.token &&
110
+ (other.choosing ||
111
+ (other.ticket > 0 &&
112
+ (other.ticket < ticket || (other.ticket === ticket && other.token < owner.token)))),
113
+ );
114
+ if (!blocked)
115
+ return () => {
116
+ fs.rmSync(participant, { force: true });
117
+ };
118
+ if (Date.now() >= deadline)
119
+ throw Object.assign(new Error('CLI update lock is busy'), { code: 'CLI_UPDATE_BUSY' });
120
+ wait(25);
121
+ }
122
+ } catch (error) {
123
+ fs.rmSync(participant, { force: true });
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ function liveLeases(directory) {
129
+ const leaseDir = path.join(directory, 'sessions');
130
+ fs.mkdirSync(leaseDir, { recursive: true, mode: 0o700 });
131
+ const live = [];
132
+ for (const file of fs.readdirSync(leaseDir)) {
133
+ const target = path.join(leaseDir, file);
134
+ const lease = readJson(target);
135
+ if (lease && isAlive(lease)) live.push(lease);
136
+ else fs.rmSync(target, { force: true });
137
+ }
138
+ return live;
139
+ }
140
+
141
+ function registerSession(packageRoot, timeoutMs = 60000) {
142
+ const directory = runtimeDir(packageRoot);
143
+ const deadline = Date.now() + timeoutMs;
144
+ while (true) {
145
+ const release = acquireLock(path.join(directory, 'admission.lock'), 1000);
146
+ try {
147
+ const mutationFile = path.join(directory, 'mutation.json');
148
+ const mutation = readJson(mutationFile);
149
+ if (!mutation || !isAlive(mutation)) {
150
+ if (mutation) fs.rmSync(mutationFile, { force: true });
151
+ const token = crypto.randomUUID();
152
+ const leaseFile = path.join(directory, 'sessions', `${process.pid}-${token}.json`);
153
+ writeJson(leaseFile, {
154
+ pid: process.pid,
155
+ identity: processIdentity(process.pid),
156
+ token,
157
+ coordinationVersion: COORDINATION_VERSION,
158
+ });
159
+ const cleanup = () => {
160
+ try {
161
+ fs.rmSync(leaseFile, { force: true });
162
+ } catch {
163
+ /* read-only home */
164
+ }
165
+ };
166
+ process.once('exit', cleanup);
167
+ return cleanup;
168
+ }
169
+ } finally {
170
+ release();
171
+ }
172
+ if (Date.now() >= deadline) {
173
+ throw Object.assign(
174
+ new Error(
175
+ 'skrr is being updated by npm. Retry shortly; inspect `skrr update --check` after the update finishes.',
176
+ ),
177
+ { code: 'CLI_UPDATE_BUSY' },
178
+ );
179
+ }
180
+ wait(100);
181
+ }
182
+ }
183
+
184
+ module.exports = {
185
+ COORDINATION_VERSION,
186
+ runtimeDir,
187
+ readJson,
188
+ writeJson,
189
+ processIdentity,
190
+ isAlive,
191
+ acquireLock,
192
+ liveLeases,
193
+ registerSession,
194
+ };
@@ -0,0 +1,208 @@
1
+ 'use strict';
2
+
3
+ // Copied next to update-session.cjs outside the npm package before launch.
4
+ const fs = require('node:fs');
5
+ const path = require('node:path');
6
+ const crypto = require('node:crypto');
7
+ const { execFile, spawn } = require('node:child_process');
8
+ const { promisify } = require('node:util');
9
+ const runtime = require('./update-session.cjs');
10
+ const execFileAsync = promisify(execFile);
11
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
12
+ const MAX_WAIT_MS = 5 * 60 * 1000;
13
+ const INSTALL_TIMEOUT_MS = 10 * 60 * 1000;
14
+ const MAX_LOG_BYTES = 256 * 1024;
15
+
16
+ async function run(directory, options = {}) {
17
+ let releaseWorker;
18
+ try {
19
+ releaseWorker = runtime.acquireLock(path.join(directory, 'worker.lock'), 1000);
20
+ } catch (error) {
21
+ if (error.code === 'CLI_UPDATE_BUSY') return;
22
+ throw error;
23
+ }
24
+ const stateFile = path.join(directory, 'result.json');
25
+ let mutation = false;
26
+ let mutationChild = null;
27
+ let attempt = runtime.readJson(stateFile);
28
+ const persist = (changes) => {
29
+ attempt = { ...attempt, ...changes, updatedAt: Date.now() };
30
+ runtime.writeJson(stateFile, attempt);
31
+ };
32
+ try {
33
+ if (!attempt || !['queued', 'running'].includes(attempt.status)) return;
34
+ if (
35
+ attempt.coordinationVersion != null &&
36
+ attempt.coordinationVersion !== runtime.COORDINATION_VERSION
37
+ )
38
+ throw new Error(
39
+ 'Incompatible CLI update coordination protocol; use the owning package manager',
40
+ );
41
+ if (
42
+ !/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/.test(attempt.version) ||
43
+ !path.isAbsolute(attempt.packageRoot) ||
44
+ !path.isAbsolute(attempt.npmPath)
45
+ ) {
46
+ throw new Error('Invalid persisted CLI update request');
47
+ }
48
+ // Serialize admission and the empty-session check. Newly starting CLIs
49
+ // then wait before loading oclif/package modules until npm has finished.
50
+ const deadline = Date.now() + (options.maxWaitMs ?? MAX_WAIT_MS);
51
+ while (true) {
52
+ const releaseAdmission = runtime.acquireLock(path.join(directory, 'admission.lock'), 1000);
53
+ try {
54
+ const previousMutation = runtime.readJson(path.join(directory, 'mutation.json'));
55
+ if (
56
+ (!previousMutation || !runtime.isAlive(previousMutation)) &&
57
+ runtime.liveLeases(directory).length === 0
58
+ ) {
59
+ runtime.writeJson(path.join(directory, 'mutation.json'), {
60
+ pid: process.pid,
61
+ identity: runtime.processIdentity(process.pid),
62
+ });
63
+ mutation = true;
64
+ break;
65
+ }
66
+ } finally {
67
+ releaseAdmission();
68
+ }
69
+ if (Date.now() >= deadline) {
70
+ persist({
71
+ status: 'queued',
72
+ message: 'Waiting for active CLI sessions; the next invocation resumes this request.',
73
+ });
74
+ return;
75
+ }
76
+ await delay(250);
77
+ }
78
+ if (
79
+ runtime.readJson(path.join(attempt.packageRoot, 'package.json'))?.version === attempt.version
80
+ ) {
81
+ persist({
82
+ status: 'succeeded',
83
+ finishedAt: Date.now(),
84
+ installedVersion: attempt.version,
85
+ message: 'The requested CLI version is already installed.',
86
+ });
87
+ return;
88
+ }
89
+ const rootResult = await execFileAsync(attempt.npmPath, ['root', '-g'], {
90
+ timeout: 1500,
91
+ maxBuffer: 64 * 1024,
92
+ shell: process.platform === 'win32',
93
+ });
94
+ const npmRoot = fs.realpathSync(rootResult.stdout.trim());
95
+ const expectedPackage = path.join(npmRoot, '@skrr-ai', 'cli');
96
+ if (fs.realpathSync(expectedPackage) !== fs.realpathSync(attempt.packageRoot)) {
97
+ throw new Error(
98
+ 'npm global prefix changed; run the update with the package manager that owns this CLI',
99
+ );
100
+ }
101
+ persist({
102
+ status: 'running',
103
+ startedAt: Date.now(),
104
+ workerPid: process.pid,
105
+ message: 'npm is installing the requested CLI version.',
106
+ });
107
+ const logFile = path.join(directory, 'update.log');
108
+ fs.writeFileSync(logFile, '', { mode: 0o600 });
109
+ let logBytes = 0;
110
+ const log = (chunk) => {
111
+ const bytes = Buffer.from(chunk);
112
+ if (logBytes >= MAX_LOG_BYTES) return;
113
+ const bounded = bytes.subarray(0, MAX_LOG_BYTES - logBytes);
114
+ fs.appendFileSync(logFile, bounded);
115
+ logBytes += bounded.length;
116
+ };
117
+ await new Promise((resolve, reject) => {
118
+ const requestFile = path.join(directory, `runner-${crypto.randomUUID()}.json`);
119
+ const token = crypto.randomUUID();
120
+ runtime.writeJson(requestFile, { token, npmPath: attempt.npmPath, version: attempt.version });
121
+ const child = spawn(
122
+ process.execPath,
123
+ [path.join(__dirname, 'update-npm-runner.cjs'), requestFile],
124
+ {
125
+ detached: process.platform !== 'win32',
126
+ stdio: ['ignore', 'pipe', 'pipe'],
127
+ },
128
+ );
129
+ if (child.pid) {
130
+ mutationChild = { pid: child.pid, group: process.platform !== 'win32' };
131
+ runtime.writeJson(path.join(directory, 'mutation.json'), mutationChild);
132
+ runtime.writeJson(`${requestFile}.go`, { pid: child.pid, token });
133
+ }
134
+ child.stdout.on('data', log);
135
+ child.stderr.on('data', log);
136
+ let timedOut = false;
137
+ let forceTimer;
138
+ const signal = (name) => {
139
+ try {
140
+ if (process.platform !== 'win32' && child.pid) process.kill(-child.pid, name);
141
+ else child.kill(name);
142
+ } catch {
143
+ /* already exited */
144
+ }
145
+ };
146
+ const timer = setTimeout(() => {
147
+ timedOut = true;
148
+ signal('SIGTERM');
149
+ forceTimer = setTimeout(() => {
150
+ signal('SIGKILL');
151
+ }, 5000);
152
+ }, options.installTimeoutMs ?? INSTALL_TIMEOUT_MS);
153
+ child.once('error', (error) => {
154
+ clearTimeout(timer);
155
+ clearTimeout(forceTimer);
156
+ reject(error);
157
+ });
158
+ child.once('exit', async (code, exitSignal) => {
159
+ clearTimeout(timer);
160
+ if (timedOut) {
161
+ // npm can exit on TERM while a descendant keeps writing. Complete
162
+ // KILL escalation for the whole group before releasing admission.
163
+ signal('SIGKILL');
164
+ const stopDeadline = Date.now() + 5000;
165
+ while (mutationChild && runtime.isAlive(mutationChild) && Date.now() < stopDeadline)
166
+ await delay(50);
167
+ }
168
+ clearTimeout(forceTimer);
169
+ fs.rmSync(requestFile, { force: true });
170
+ fs.rmSync(`${requestFile}.go`, { force: true });
171
+ if (timedOut) reject(new Error('npm update exceeded the 10-minute time limit'));
172
+ else if (code === 0) resolve();
173
+ else reject(new Error(`npm update failed (${exitSignal || code}); see ${logFile}`));
174
+ });
175
+ });
176
+ const installedVersion = runtime.readJson(
177
+ path.join(attempt.packageRoot, 'package.json'),
178
+ )?.version;
179
+ if (installedVersion !== attempt.version)
180
+ throw new Error(
181
+ `npm completed but installed ${installedVersion || 'an unknown version'} instead of ${attempt.version}`,
182
+ );
183
+ persist({
184
+ status: 'succeeded',
185
+ finishedAt: Date.now(),
186
+ installedVersion,
187
+ message: 'The new CLI version is ready for the next invocation.',
188
+ });
189
+ } catch (error) {
190
+ persist({ status: 'failed', finishedAt: Date.now(), message: String(error.message || error) });
191
+ } finally {
192
+ // A crashed worker/terminated npm parent can leave a mutating process
193
+ // group alive. Keep the gate until the group is confirmed gone.
194
+ if (mutation && (!mutationChild || !runtime.isAlive(mutationChild)))
195
+ fs.rmSync(path.join(directory, 'mutation.json'), { force: true });
196
+ releaseWorker();
197
+ }
198
+ }
199
+
200
+ if (require.main === module) {
201
+ const directory = process.argv[2];
202
+ if (!directory || !path.isAbsolute(directory)) process.exitCode = 1;
203
+ else
204
+ run(directory).catch(() => {
205
+ process.exitCode = 1;
206
+ });
207
+ }
208
+ module.exports = { run };
@@ -52,7 +52,6 @@ const daemonBroker_1 = require("./lib/daemonBroker");
52
52
  const daemonBrokerRefusal_1 = require("./lib/daemonBrokerRefusal");
53
53
  const refresh_1 = require("./lib/refresh");
54
54
  const auth_storage_1 = require("./lib/auth-storage");
55
- const update_check_1 = require("./lib/update-check");
56
55
  const web_url_1 = require("./lib/web-url");
57
56
  const edge_error_page_1 = require("./lib/edge-error-page");
58
57
  const step_up_1 = require("./lib/step-up");
@@ -541,20 +540,6 @@ class BaseCommand extends core_1.Command {
541
540
  }
542
541
  (0, node_adapter_1.setAdapterCredentialOverride)(this.resolvedCredential);
543
542
  (0, data_provider_1.setHttpAdapter)((0, node_adapter_1.createNodeAdapter)());
544
- // Non-blocking "a newer skrr is available" advisory. Decides from a ~4h
545
- // on-disk cache (zero added latency) and refreshes in the background.
546
- // Writes at most one line to stderr, never stdout, so --json consumers
547
- // are unaffected; self-suppresses in bundled/non-TTY/opted-out contexts.
548
- // Wrapped so an advisory can never break the command it precedes.
549
- try {
550
- (0, update_check_1.maybeWarnOutdatedCli)({
551
- currentVersion: this.config.version,
552
- baseURL: this.cliConfig.baseURL,
553
- });
554
- }
555
- catch {
556
- /* advisory is strictly best-effort */
557
- }
558
543
  }
559
544
  /**
560
545
  * Read only the two global base flags. A narrow parser — oclif's
@@ -85,6 +85,8 @@ export default class AgentMessageSend extends BaseCommand {
85
85
  part: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
86
86
  'message-lint': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
87
87
  destination: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
88
+ to: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
89
+ subject: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
88
90
  channel: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
89
91
  'thread-ts': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
90
92
  'thread-root': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -114,6 +114,11 @@ class AgentMessageSend extends base_command_1.BaseCommand {
114
114
  description: 'Destination kind for the canonical intent',
115
115
  options: [...message_intent_1.CANONICAL_DESTINATION_KINDS],
116
116
  }),
117
+ to: core_1.Flags.string({
118
+ description: 'Recipient email address; repeat for up to 5 recipients',
119
+ multiple: true,
120
+ }),
121
+ subject: core_1.Flags.string({ description: 'Plain email subject (agent_email only)' }),
117
122
  channel: core_1.Flags.string({
118
123
  description: 'Slack channel ID (only meaningful with --destination slack_channel)',
119
124
  }),
@@ -178,6 +183,8 @@ class AgentMessageSend extends base_command_1.BaseCommand {
178
183
  agentId: flags.agent,
179
184
  text: flags.text,
180
185
  destinationKind: flags.destination,
186
+ recipients: flags.to,
187
+ subject: flags.subject,
181
188
  channelId: flags.channel,
182
189
  threadTs: flags['thread-ts'],
183
190
  threadRootMessageId: flags['thread-root'],
@@ -497,6 +504,9 @@ class AgentMessageSend extends base_command_1.BaseCommand {
497
504
  },
498
505
  });
499
506
  }
507
+ if (request.destination.kind === 'agent_email') {
508
+ this.error('Agent email is available only through an authorized Agent session.');
509
+ }
500
510
  // slack_owner_dm is a Gateway-only destination: it applies workspace-scoped
501
511
  // owner routing that no legacy human transport can enact.
502
512
  this.failWithCliError({
@@ -538,6 +548,14 @@ function messageSegmentationAdvisory({ destination, text, followingParts, }) {
538
548
  * from one explicit semantic idempotency key.
539
549
  */
540
550
  function buildSequencedRequests(request, followingParts, hasExplicitIdempotencyKey) {
551
+ if (request.destination.kind === 'agent_email') {
552
+ if (followingParts.length)
553
+ throw invalidIntent('Email accepts one plain-text body; --part is not supported.');
554
+ requireText(request.content.text, 'content.text', 20 * 1024);
555
+ if (Buffer.byteLength(request.content.text, 'utf8') > 20 * 1024)
556
+ throw invalidIntent('Email body exceeds 20 KiB.');
557
+ return [request];
558
+ }
541
559
  const parts = [request.content.text, ...followingParts].map((part) => requireText(part, 'part'));
542
560
  if (parts.length === 1)
543
561
  return [request];
@@ -586,6 +604,8 @@ const DELIVERY_FAILURE_STATUSES = new Set([
586
604
  'dropped',
587
605
  'expired',
588
606
  'cancelled',
607
+ 'unknown',
608
+ 'refused',
589
609
  ]);
590
610
  /**
591
611
  * Exit codes for a submitted-but-undelivered message.
@@ -723,8 +743,18 @@ function normalizeIntentPayload({ agentId, jsonBody, merged, }) {
723
743
  bodyDestination,
724
744
  overlayChannel,
725
745
  overlayThreadTs,
746
+ overlayRecipients: merged.recipients,
747
+ overlaySubject: merged.subject,
726
748
  });
727
- const text = requireText(readTextField(jsonBody, merged), 'content.text');
749
+ const text = requireText(readTextField(jsonBody, merged), 'content.text', destination.kind === 'agent_email' ? 20 * 1024 : MAX_TEXT_CHARS);
750
+ if (destination.kind === 'agent_email') {
751
+ if (Buffer.byteLength(text, 'utf8') > 20 * 1024)
752
+ throw invalidIntent('Email body exceeds 20 KiB.');
753
+ const forbidden = ['from', 'cc', 'bcc', 'html', 'attachments', 'headers', 'replyTo'];
754
+ const content = jsonBody.content;
755
+ if (forbidden.some((key) => jsonBody[key] !== undefined || content?.[key] !== undefined))
756
+ throw invalidIntent('Agent email accepts plain text only; sender and headers are server-owned.');
757
+ }
728
758
  const idempotencyKey = optionalIdempotencyKey(merged.idempotencyKey ??
729
759
  jsonBody.idempotencyKey ??
730
760
  jsonBody['idempotency_key'] ??
@@ -778,10 +808,38 @@ function pickDestinationKind({ bodyDestination, overlayKind, }) {
778
808
  }
779
809
  throw invalidIntent(`destination.kind must be one of ${message_intent_1.CANONICAL_DESTINATION_KINDS.join(', ')} (got "${normalized}").`);
780
810
  }
781
- function buildDestination({ kind, bodyDestination, overlayChannel, overlayThreadTs, }) {
811
+ function buildDestination({ kind, bodyDestination, overlayChannel, overlayThreadTs, overlayRecipients, overlaySubject, }) {
782
812
  if (kind === 'app_owner_dm' || kind === 'slack_owner_dm') {
783
813
  return { kind };
784
814
  }
815
+ if (kind === 'agent_email') {
816
+ const source = bodyDestination;
817
+ if (source &&
818
+ Object.keys(source).some((key) => !['kind', 'recipients', 'subject'].includes(key))) {
819
+ throw invalidIntent('agent_email accepts only recipients and subject.');
820
+ }
821
+ const rawRecipients = overlayRecipients ?? source?.recipients;
822
+ if (!Array.isArray(rawRecipients) || rawRecipients.length < 1 || rawRecipients.length > 5) {
823
+ throw invalidIntent('agent_email requires 1 to 5 recipients.');
824
+ }
825
+ const recipients = rawRecipients.map((value) => {
826
+ if (typeof value !== 'string' ||
827
+ value.length > 254 ||
828
+ !/^[^\s@<>]+@[^\s@<>]+\.[^\s@<>]+$/.test(value))
829
+ throw invalidIntent('Invalid email recipient.');
830
+ return value.toLowerCase();
831
+ });
832
+ if (new Set(recipients).size !== recipients.length)
833
+ throw invalidIntent('Email recipients must be distinct.');
834
+ const subject = overlaySubject ?? source?.subject;
835
+ if (typeof subject !== 'string' ||
836
+ !subject.trim() ||
837
+ subject.length > 200 ||
838
+ // eslint-disable-next-line no-control-regex -- Reject control characters in mail headers.
839
+ /[\r\n\x00-\x1f\x7f]/.test(subject))
840
+ throw invalidIntent('Email subject must be a single line of at most 200 characters.');
841
+ return { kind, recipients: recipients.sort(), subject: subject.trim() };
842
+ }
785
843
  const channelId = optionalString(overlayChannel ?? bodyDestination?.channelId);
786
844
  if (!channelId) {
787
845
  throw invalidIntent('slack_channel destinations require destination.channelId.');
@@ -905,6 +963,16 @@ function booleanFlagInput(argv, name, parsedValue) {
905
963
  }
906
964
  function canonicalIntentFromDestination({ agentId, idempotencyKey, destination, text, presentation, notificationPolicy, context, threadRootMessageId, embeds, }) {
907
965
  switch (destination.kind) {
966
+ case 'agent_email':
967
+ return {
968
+ protocolVersion: 1,
969
+ agentId,
970
+ intentType: 'status_update',
971
+ destination,
972
+ content: { text },
973
+ idempotencyKey,
974
+ ...(context ? { context } : {}),
975
+ };
908
976
  case 'app_owner_dm':
909
977
  return (0, message_intent_1.canonicalAppOwnerDmRequest)({
910
978
  agentId,
@@ -947,13 +1015,13 @@ function resolveAgentInput(argValue, flagValue, merged, jsonBody) {
947
1015
  { label: 'agentId in JSON input', value: fromBody ?? fromMerged },
948
1016
  ]);
949
1017
  }
950
- function requireText(value, field) {
1018
+ function requireText(value, field, maxChars = MAX_TEXT_CHARS) {
951
1019
  if (typeof value !== 'string' || !value.trim()) {
952
1020
  throw invalidIntent(`${field} is required.`);
953
1021
  }
954
1022
  const normalized = value.trim();
955
- if (normalized.length > MAX_TEXT_CHARS) {
956
- throw invalidIntent(`${field} must be ${MAX_TEXT_CHARS} characters or less.`);
1023
+ if (normalized.length > maxChars) {
1024
+ throw invalidIntent(`${field} must be ${maxChars} characters or less.`);
957
1025
  }
958
1026
  return normalized;
959
1027
  }
@@ -1043,6 +1111,8 @@ function formatEnvelope(envelope) {
1043
1111
  return `Agent message intent for ${envelope.agent.id} → ${destination}: ${status}${suffix}.${presentation}`;
1044
1112
  }
1045
1113
  function describeDestination(destination) {
1114
+ if (destination.kind === 'agent_email')
1115
+ return `agent_email:${destination.recipients.join(', ')}`;
1046
1116
  if (destination.kind === 'slack_channel') {
1047
1117
  return `slack_channel:${destination.channelId}${destination.threadTs ? `:${destination.threadTs}` : ''}`;
1048
1118
  }
@@ -594,7 +594,7 @@ class AgentsChat extends base_command_1.BaseCommand {
594
594
  'status is not managed for you. The runtime session is bound to this task for ' +
595
595
  'task context and artifact uploads; use the canonical id above.',
596
596
  ].join('\n');
597
- if (!flags.jsonl) {
597
+ if (!flags.jsonl && !flags.json) {
598
598
  this.log(`Bound to task ${task.identifier ?? taskId} (chat turn — task not checked out)`);
599
599
  }
600
600
  }
@@ -124,9 +124,6 @@ export default class AgentsCreate extends BaseCommand {
124
124
  avatar: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
125
125
  'conversation-starter': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
126
126
  'todo-working-hours': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
127
- 'reasoning-tool-escape': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
128
- 'commitment-responsibility': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
129
- 'commitment-responsibility-ceiling': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
130
127
  'max-turns': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
131
128
  'tool-resources': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
132
129
  'install-channel': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -280,32 +280,6 @@ class AgentsCreate extends base_command_1.BaseCommand {
280
280
  description: 'When this agent schedules its own work: HH:MM-HH:MM[@<IANA timezone>], e.g. ' +
281
281
  '09:00-18:00@America/Los_Angeles',
282
282
  }),
283
- // Retired (OSK-11975): a Commitment turn works under the Agent's own
284
- // permissions. Hidden, warned on use, and never sent (OSK-12068) — see
285
- // `mergeAgentConfigFlags`. Kept parseable so an old script still runs.
286
- 'reasoning-tool-escape': core_1.Flags.string({
287
- description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
288
- options: [...agent_config_1.REASONING_TOOL_ESCAPES],
289
- multiple: true,
290
- deprecated: { message: (0, agent_config_1.retiredCommitmentPermissionFlagMessage)('reasoning-tool-escape') },
291
- hidden: true,
292
- }),
293
- 'commitment-responsibility': core_1.Flags.string({
294
- description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
295
- options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
296
- deprecated: {
297
- message: (0, agent_config_1.retiredCommitmentPermissionFlagMessage)('commitment-responsibility'),
298
- },
299
- hidden: true,
300
- }),
301
- 'commitment-responsibility-ceiling': core_1.Flags.string({
302
- description: "Retired — has no effect and is not sent. Commitment runs use the agent's own permissions.",
303
- options: [...agent_config_1.COMMITMENT_RESPONSIBILITY_LEVELS],
304
- deprecated: {
305
- message: (0, agent_config_1.retiredCommitmentPermissionFlagMessage)('commitment-responsibility-ceiling'),
306
- },
307
- hidden: true,
308
- }),
309
283
  'max-turns': core_1.Flags.integer({ description: 'Maximum turns in one agent run (1-500)' }),
310
284
  'tool-resources': core_1.Flags.string({
311
285
  description: 'Tool resources JSON object (file_search / context / image_edit bindings)',
@@ -0,0 +1,17 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class AgentsEgress extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ allow: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
+ remove: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ repo: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
+ 'remove-repo': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ clear: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
15
+ };
16
+ run(): Promise<void>;
17
+ }