@skrr-ai/cli 0.1.36 → 0.1.38

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 (266) hide show
  1. package/README.md +17 -4
  2. package/bin/dev-fallback.js +167 -4
  3. package/dist/base-command.js +46 -2
  4. package/dist/commands/agents/actions/create.d.ts +5 -0
  5. package/dist/commands/agents/actions/create.js +42 -5
  6. package/dist/commands/agents/actions/delete.d.ts +1 -0
  7. package/dist/commands/agents/actions/delete.js +7 -0
  8. package/dist/commands/agents/actions/update.d.ts +5 -0
  9. package/dist/commands/agents/actions/update.js +27 -1
  10. package/dist/commands/agents/api-actions/add.d.ts +41 -0
  11. package/dist/commands/agents/api-actions/add.js +205 -0
  12. package/dist/commands/agents/api-actions/index.d.ts +39 -0
  13. package/dist/commands/agents/api-actions/index.js +103 -0
  14. package/dist/commands/agents/api-actions/remove.d.ts +14 -0
  15. package/dist/commands/agents/api-actions/remove.js +36 -0
  16. package/dist/commands/agents/avatar/default/apply.d.ts +25 -0
  17. package/dist/commands/agents/avatar/default/apply.js +56 -0
  18. package/dist/commands/agents/avatar/default/index.d.ts +29 -0
  19. package/dist/commands/agents/avatar/default/index.js +61 -0
  20. package/dist/commands/agents/avatar/default/set.d.ts +24 -0
  21. package/dist/commands/agents/avatar/default/set.js +120 -0
  22. package/dist/commands/agents/avatar/library/delete.d.ts +20 -0
  23. package/dist/commands/agents/avatar/library/delete.js +47 -0
  24. package/dist/commands/agents/avatar/library/index.d.ts +16 -0
  25. package/dist/commands/agents/avatar/library/index.js +56 -0
  26. package/dist/commands/agents/avatar/presets.d.ts +19 -0
  27. package/dist/commands/agents/avatar/presets.js +56 -0
  28. package/dist/commands/agents/avatar/upload.js +7 -1
  29. package/dist/commands/agents/categories/create.d.ts +14 -0
  30. package/dist/commands/agents/categories/create.js +46 -0
  31. package/dist/commands/agents/categories/delete.d.ts +13 -0
  32. package/dist/commands/agents/categories/delete.js +41 -0
  33. package/dist/commands/agents/categories/index.d.ts +36 -0
  34. package/dist/commands/agents/categories/index.js +135 -0
  35. package/dist/commands/agents/categories/update.d.ts +22 -0
  36. package/dist/commands/agents/categories/update.js +59 -0
  37. package/dist/commands/agents/chat.d.ts +21 -0
  38. package/dist/commands/agents/chat.js +94 -7
  39. package/dist/commands/agents/create.d.ts +10 -0
  40. package/dist/commands/agents/create.js +64 -3
  41. package/dist/commands/agents/ensure-default.d.ts +27 -0
  42. package/dist/commands/agents/ensure-default.js +65 -0
  43. package/dist/commands/agents/list.d.ts +15 -0
  44. package/dist/commands/agents/list.js +31 -1
  45. package/dist/commands/agents/mute.d.ts +16 -0
  46. package/dist/commands/agents/mute.js +38 -0
  47. package/dist/commands/agents/pin.d.ts +16 -0
  48. package/dist/commands/agents/pin.js +38 -0
  49. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  50. package/dist/commands/agents/scene-background/upload.js +97 -0
  51. package/dist/commands/agents/show.js +13 -0
  52. package/dist/commands/agents/tools/auth.d.ts +19 -0
  53. package/dist/commands/agents/tools/auth.js +41 -0
  54. package/dist/commands/agents/tools/calls.d.ts +36 -0
  55. package/dist/commands/agents/tools/calls.js +117 -0
  56. package/dist/commands/agents/tools/index.d.ts +20 -0
  57. package/dist/commands/agents/tools/index.js +77 -0
  58. package/dist/commands/agents/unmute.d.ts +16 -0
  59. package/dist/commands/agents/unmute.js +38 -0
  60. package/dist/commands/agents/unpin.d.ts +16 -0
  61. package/dist/commands/agents/unpin.js +38 -0
  62. package/dist/commands/agents/update.d.ts +12 -0
  63. package/dist/commands/agents/update.js +74 -3
  64. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  65. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  66. package/dist/commands/convos/messages.d.ts +13 -0
  67. package/dist/commands/convos/messages.js +40 -16
  68. package/dist/commands/daemon/index.js +8 -1
  69. package/dist/commands/daemon/logs.d.ts +26 -0
  70. package/dist/commands/daemon/logs.js +56 -0
  71. package/dist/commands/daemon/uninstall.js +1 -1
  72. package/dist/commands/followups/act.d.ts +22 -0
  73. package/dist/commands/followups/act.js +103 -0
  74. package/dist/commands/followups/cancel.d.ts +20 -0
  75. package/dist/commands/followups/cancel.js +50 -0
  76. package/dist/commands/followups/checkin.d.ts +27 -0
  77. package/dist/commands/followups/checkin.js +107 -0
  78. package/dist/commands/followups/list.d.ts +24 -0
  79. package/dist/commands/followups/list.js +90 -0
  80. package/dist/commands/followups/observability.d.ts +23 -0
  81. package/dist/commands/followups/observability.js +114 -0
  82. package/dist/commands/followups/remind.d.ts +26 -0
  83. package/dist/commands/followups/remind.js +106 -0
  84. package/dist/commands/followups/reschedule.d.ts +15 -0
  85. package/dist/commands/followups/reschedule.js +48 -0
  86. package/dist/commands/followups/resolve.d.ts +23 -0
  87. package/dist/commands/followups/resolve.js +54 -0
  88. package/dist/commands/followups/show.d.ts +12 -0
  89. package/dist/commands/followups/show.js +46 -0
  90. package/dist/commands/followups/watch.d.ts +51 -0
  91. package/dist/commands/followups/watch.js +211 -0
  92. package/dist/commands/goals/labels/add.js +19 -2
  93. package/dist/commands/goals/labels/remove.js +22 -2
  94. package/dist/commands/harnesses/forget.js +28 -0
  95. package/dist/commands/harnesses/install.d.ts +41 -0
  96. package/dist/commands/harnesses/install.js +165 -0
  97. package/dist/commands/harnesses/installers.d.ts +45 -0
  98. package/dist/commands/harnesses/installers.js +155 -0
  99. package/dist/commands/harnesses/list.d.ts +82 -1
  100. package/dist/commands/harnesses/list.js +104 -12
  101. package/dist/commands/harnesses/models.js +10 -1
  102. package/dist/commands/harnesses/show.d.ts +15 -0
  103. package/dist/commands/harnesses/show.js +44 -3
  104. package/dist/commands/harnesses/update.js +1 -1
  105. package/dist/commands/harnesses/usage.d.ts +25 -0
  106. package/dist/commands/harnesses/usage.js +24 -2
  107. package/dist/commands/instructions/status.js +13 -1
  108. package/dist/commands/labels/list.d.ts +24 -1
  109. package/dist/commands/labels/list.js +62 -19
  110. package/dist/commands/machines/dedicated/catalog.js +4 -15
  111. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  112. package/dist/commands/machines/dedicated/create.js +58 -1
  113. package/dist/commands/machines/dedicated/destroy.js +18 -4
  114. package/dist/commands/machines/dedicated/list.js +9 -6
  115. package/dist/commands/machines/hosted/list.js +12 -0
  116. package/dist/commands/machines/list.js +11 -0
  117. package/dist/commands/machines/show.d.ts +1 -0
  118. package/dist/commands/machines/show.js +40 -11
  119. package/dist/commands/memory/governance/index.d.ts +21 -0
  120. package/dist/commands/memory/governance/index.js +64 -0
  121. package/dist/commands/memory/governance/set.d.ts +24 -0
  122. package/dist/commands/memory/governance/set.js +95 -0
  123. package/dist/commands/memory/provenance.d.ts +18 -0
  124. package/dist/commands/memory/provenance.js +66 -0
  125. package/dist/commands/profiles/list.d.ts +23 -0
  126. package/dist/commands/profiles/list.js +96 -0
  127. package/dist/commands/search/skills.d.ts +11 -1
  128. package/dist/commands/search/skills.js +26 -8
  129. package/dist/commands/skills/details.d.ts +2 -0
  130. package/dist/commands/skills/details.js +24 -1
  131. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  132. package/dist/commands/skills/import-as-actions.js +175 -0
  133. package/dist/commands/skills/install.d.ts +2 -0
  134. package/dist/commands/skills/install.js +24 -1
  135. package/dist/commands/skills/invocable.js +6 -2
  136. package/dist/commands/skills/local.d.ts +40 -0
  137. package/dist/commands/skills/local.js +160 -0
  138. package/dist/commands/skills/search.d.ts +11 -1
  139. package/dist/commands/skills/search.js +29 -8
  140. package/dist/commands/spaces/actions/add.js +23 -5
  141. package/dist/commands/spaces/actions/available.js +3 -0
  142. package/dist/commands/spaces/actions/list.d.ts +5 -0
  143. package/dist/commands/spaces/actions/list.js +49 -3
  144. package/dist/commands/spaces/actions/run.d.ts +28 -0
  145. package/dist/commands/spaces/actions/run.js +146 -0
  146. package/dist/commands/spaces/actions/update.js +27 -5
  147. package/dist/commands/spaces/create.d.ts +3 -0
  148. package/dist/commands/spaces/create.js +32 -2
  149. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  150. package/dist/commands/spaces/label-summaries.js +64 -0
  151. package/dist/commands/spaces/labels/add.js +19 -2
  152. package/dist/commands/spaces/labels/remove.js +22 -2
  153. package/dist/commands/spaces/show.js +2 -0
  154. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  155. package/dist/commands/spaces/task-rollups.js +15 -4
  156. package/dist/commands/spaces/update.d.ts +5 -0
  157. package/dist/commands/spaces/update.js +41 -4
  158. package/dist/commands/spaces/workflows/lifecycle-diagnostics.d.ts +11 -0
  159. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +105 -0
  160. package/dist/commands/tasks/actions/attach.js +32 -7
  161. package/dist/commands/tasks/complete.d.ts +1 -0
  162. package/dist/commands/tasks/complete.js +21 -1
  163. package/dist/commands/tasks/create.d.ts +3 -0
  164. package/dist/commands/tasks/create.js +34 -2
  165. package/dist/commands/tasks/events/append.js +29 -6
  166. package/dist/commands/tasks/expectations/assess.d.ts +12 -0
  167. package/dist/commands/tasks/expectations/assess.js +123 -14
  168. package/dist/commands/tasks/expectations.js +95 -17
  169. package/dist/commands/tasks/follow-up.js +2 -2
  170. package/dist/commands/tasks/labels/attach.js +20 -3
  171. package/dist/commands/tasks/labels/detach.js +21 -3
  172. package/dist/commands/tasks/lifecycle/attach.d.ts +14 -0
  173. package/dist/commands/tasks/lifecycle/attach.js +47 -0
  174. package/dist/commands/tasks/lifecycle/definitions.d.ts +11 -0
  175. package/dist/commands/tasks/lifecycle/definitions.js +59 -0
  176. package/dist/commands/tasks/lifecycle/detach.d.ts +19 -0
  177. package/dist/commands/tasks/lifecycle/detach.js +72 -0
  178. package/dist/commands/tasks/lifecycle/history.d.ts +13 -0
  179. package/dist/commands/tasks/lifecycle/history.js +38 -0
  180. package/dist/commands/tasks/lifecycle/migrate.d.ts +14 -0
  181. package/dist/commands/tasks/lifecycle/migrate.js +68 -0
  182. package/dist/commands/tasks/lifecycle/reconcile.d.ts +11 -0
  183. package/dist/commands/tasks/lifecycle/reconcile.js +27 -0
  184. package/dist/commands/tasks/lifecycle/reopen.d.ts +15 -0
  185. package/dist/commands/tasks/lifecycle/reopen.js +71 -0
  186. package/dist/commands/tasks/lifecycle/show.d.ts +11 -0
  187. package/dist/commands/tasks/lifecycle/show.js +27 -0
  188. package/dist/commands/tasks/lifecycle/start.d.ts +12 -0
  189. package/dist/commands/tasks/lifecycle/start.js +30 -0
  190. package/dist/commands/tasks/lifecycle/transition.d.ts +15 -0
  191. package/dist/commands/tasks/lifecycle/transition.js +109 -0
  192. package/dist/commands/tasks/list.js +16 -9
  193. package/dist/commands/tasks/move.d.ts +1 -0
  194. package/dist/commands/tasks/move.js +29 -0
  195. package/dist/commands/tasks/result/submit.js +11 -1
  196. package/dist/commands/tasks/review-queue.d.ts +44 -0
  197. package/dist/commands/tasks/review-queue.js +314 -0
  198. package/dist/commands/tasks/show.d.ts +11 -0
  199. package/dist/commands/tasks/show.js +139 -4
  200. package/dist/commands/tasks/trust.js +7 -2
  201. package/dist/commands/tasks/update.d.ts +1 -0
  202. package/dist/commands/tasks/update.js +83 -0
  203. package/dist/commands/tasks/updates/add.js +1 -1
  204. package/dist/commands/token/list.d.ts +22 -0
  205. package/dist/commands/token/list.js +68 -0
  206. package/dist/commands/token/revoke.d.ts +23 -0
  207. package/dist/commands/token/revoke.js +53 -0
  208. package/dist/lib/agent-actions.d.ts +92 -11
  209. package/dist/lib/agent-actions.js +171 -25
  210. package/dist/lib/agent-config.d.ts +11 -0
  211. package/dist/lib/agent-config.js +85 -3
  212. package/dist/lib/agent-pin-mute.d.ts +29 -0
  213. package/dist/lib/agent-pin-mute.js +30 -0
  214. package/dist/lib/agent-visual-refs.d.ts +33 -0
  215. package/dist/lib/agent-visual-refs.js +267 -0
  216. package/dist/lib/agentic-stream.d.ts +6 -0
  217. package/dist/lib/agentic-stream.js +34 -3
  218. package/dist/lib/assignee-resolver.d.ts +1 -1
  219. package/dist/lib/assignee-resolver.js +8 -0
  220. package/dist/lib/bulk-task-targets.d.ts +12 -0
  221. package/dist/lib/bulk-task-targets.js +34 -1
  222. package/dist/lib/cli-installers.d.ts +130 -0
  223. package/dist/lib/cli-installers.js +145 -0
  224. package/dist/lib/command-miss.js +55 -7
  225. package/dist/lib/dedicated-lease-command.d.ts +13 -0
  226. package/dist/lib/dedicated-lease-command.js +22 -1
  227. package/dist/lib/dedicated-machines.d.ts +95 -1
  228. package/dist/lib/dedicated-machines.js +236 -2
  229. package/dist/lib/failed-lookup.d.ts +3 -0
  230. package/dist/lib/failed-lookup.js +40 -0
  231. package/dist/lib/first-party-harness-agent.js +16 -2
  232. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  233. package/dist/lib/first-party-harness-doctor.js +126 -27
  234. package/dist/lib/followups.d.ts +115 -0
  235. package/dist/lib/followups.js +127 -0
  236. package/dist/lib/harnesses.d.ts +8 -0
  237. package/dist/lib/instruction-input.d.ts +19 -1
  238. package/dist/lib/instruction-input.js +52 -1
  239. package/dist/lib/label-ref.d.ts +95 -0
  240. package/dist/lib/label-ref.js +163 -0
  241. package/dist/lib/label-scope.d.ts +17 -2
  242. package/dist/lib/label-scope.js +27 -7
  243. package/dist/lib/local-skills.d.ts +98 -0
  244. package/dist/lib/local-skills.js +195 -0
  245. package/dist/lib/machines.d.ts +10 -10
  246. package/dist/lib/machines.js +23 -1
  247. package/dist/lib/search-query.d.ts +19 -0
  248. package/dist/lib/search-query.js +35 -0
  249. package/dist/lib/space-custom-actions.d.ts +72 -0
  250. package/dist/lib/space-custom-actions.js +117 -0
  251. package/dist/lib/space-resolver.d.ts +1 -21
  252. package/dist/lib/space-resolver.js +19 -10
  253. package/dist/lib/task-closure.d.ts +33 -0
  254. package/dist/lib/task-closure.js +104 -0
  255. package/dist/lib/task-lifecycle-output.d.ts +14 -0
  256. package/dist/lib/task-lifecycle-output.js +209 -0
  257. package/dist/lib/task-ref-resolver.d.ts +1 -17
  258. package/dist/lib/task-ref-resolver.js +18 -8
  259. package/dist/lib/tasks.d.ts +3 -0
  260. package/dist/lib/tasks.js +40 -3
  261. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/harnessTrust.js +4 -0
  262. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/harnessTrust.js +4 -0
  263. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  264. package/dist/node_modules/@skrr-ai/data-provider/index.js +5559 -4897
  265. package/oclif.manifest.json +33890 -28690
  266. package/package.json +24 -6
@@ -21,6 +21,10 @@ exports.writeDedicatedRuntimeFileChunk = writeDedicatedRuntimeFileChunk;
21
21
  exports.parseDedicatedCopyEndpoints = parseDedicatedCopyEndpoints;
22
22
  exports.dedicatedLeaseId = dedicatedLeaseId;
23
23
  exports.dedicatedLeaseState = dedicatedLeaseState;
24
+ exports.dedicatedDestroyRetentionNote = dedicatedDestroyRetentionNote;
25
+ exports.dedicatedScopeMonthSpendCents = dedicatedScopeMonthSpendCents;
26
+ exports.dedicatedLeaseCapCell = dedicatedLeaseCapCell;
27
+ exports.dedicatedLeaseStatusCell = dedicatedLeaseStatusCell;
24
28
  exports.dedicatedLeaseDisplayName = dedicatedLeaseDisplayName;
25
29
  exports.formatUsdCents = formatUsdCents;
26
30
  exports.dedicatedSpendSummary = dedicatedSpendSummary;
@@ -33,6 +37,9 @@ exports.dedicatedLeaseIdForDaemon = dedicatedLeaseIdForDaemon;
33
37
  exports.dedicatedTerminalHarness = dedicatedTerminalHarness;
34
38
  exports.selectDedicatedAttachHarness = selectDedicatedAttachHarness;
35
39
  exports.dedicatedCatalogFromProducts = dedicatedCatalogFromProducts;
40
+ exports.dedicatedCatalogSizeLines = dedicatedCatalogSizeLines;
41
+ exports.dedicatedCreateSummary = dedicatedCreateSummary;
42
+ exports.dedicatedCatalogPriceLineOutput = dedicatedCatalogPriceLineOutput;
36
43
  exports.dedicatedRuntimeApiErrorCode = dedicatedRuntimeApiErrorCode;
37
44
  exports.dedicatedRuntimeApiErrorDetails = dedicatedRuntimeApiErrorDetails;
38
45
  exports.formatDedicatedRuntimeApiError = formatDedicatedRuntimeApiError;
@@ -300,6 +307,71 @@ function dedicatedLeaseId(lease) {
300
307
  function dedicatedLeaseState(lease) {
301
308
  return String(lease?.state ?? 'unknown');
302
309
  }
310
+ /**
311
+ * What `destroy` should say when it keeps the workspace — or null when it does
312
+ * not.
313
+ *
314
+ * A destroy under retention class standard/extended archives the lease and keeps
315
+ * its workspace volume, which keeps billing storage until it is purged. Nothing
316
+ * said so at the moment it mattered: `destroy` printed the new state and exited,
317
+ * `list` showed `archived` in the same column as `terminated`, and the one way to
318
+ * find out was the bill (OSK-9593).
319
+ */
320
+ function dedicatedDestroyRetentionNote(priorLease, opts) {
321
+ if (opts.purge || !priorLease)
322
+ return null;
323
+ if (priorLease.state === 'archived')
324
+ return null; // destroying an archived lease purges it
325
+ const retentionClass = String(priorLease.storage?.retentionClass || 'none');
326
+ if (!opts.retain && retentionClass === 'none')
327
+ return null;
328
+ const id = dedicatedLeaseId(priorLease) || '<lease-id>';
329
+ const why = opts.retain ? '--retain-storage' : `retention class ${retentionClass}`;
330
+ return (`The workspace is kept (${why}), and its storage keeps billing until you delete it: ` +
331
+ `${opts.bin} machines dedicated destroy ${id} --purge-storage`);
332
+ }
333
+ const DEDICATED_TERMINAL_STATES = new Set(['terminated', 'failed', 'lost']);
334
+ /**
335
+ * Month-to-date Dedicated Runtime spend for the scope a `list` shows.
336
+ *
337
+ * `billing.currentMonthSpendCents` is the SCOPE's spend for the product — the
338
+ * server aggregates by owner scope, not by lease — so every row carried the same
339
+ * figure, and a column headed MONTH/CAP on per-machine rows read as that
340
+ * machine's cost when it was every machine's (OSK-9576). It is said once instead.
341
+ */
342
+ function dedicatedScopeMonthSpendCents(leases) {
343
+ for (const lease of leases) {
344
+ const cents = lease?.billing
345
+ ?.currentMonthSpendCents;
346
+ if (typeof cents === 'number' && Number.isFinite(cents))
347
+ return cents;
348
+ }
349
+ return null;
350
+ }
351
+ /**
352
+ * A lease's own cap, marked when the scope's spend has reached it. The mark is
353
+ * left off a terminal lease: its cap can no longer stop anything, and
354
+ * "$11.06/$10.00 OVER" on a machine terminated days ago read as that machine
355
+ * having overrun.
356
+ */
357
+ function dedicatedLeaseCapCell(lease) {
358
+ const billing = lease?.billing;
359
+ if (!billing || typeof billing.monthlySpendLimitCents !== 'number')
360
+ return '-';
361
+ const reached = billing.spendingLimitExceeded && !DEDICATED_TERMINAL_STATES.has(dedicatedLeaseState(lease));
362
+ return `${formatUsdCents(billing.monthlySpendLimitCents)}${reached ? ' · reached' : ''}`;
363
+ }
364
+ /**
365
+ * A lease's STATUS cell. `archived` and `terminated` are both "gone" to read, and
366
+ * only one of them is still charging — so a lease billing retained storage says
367
+ * so (OSK-9593).
368
+ */
369
+ function dedicatedLeaseStatusCell(lease) {
370
+ const state = dedicatedLeaseState(lease);
371
+ const billableState = lease?.billing
372
+ ?.billableState;
373
+ return billableState === 'storage' ? `${state} · billing storage` : state;
374
+ }
303
375
  function dedicatedLeaseDisplayName(lease) {
304
376
  return lease?.displayName ? String(lease.displayName) : '-';
305
377
  }
@@ -310,7 +382,17 @@ function formatUsdCents(cents) {
310
382
  const sign = cents < 0 ? '-' : '';
311
383
  return `${sign}$${(Math.abs(cents) / 100).toFixed(2)}`;
312
384
  }
313
- /** `this month $1.23 of $500.00 cap`, or why that sentence cannot be written. */
385
+ /**
386
+ * `$1.23 this month for machines of this type · $500.00 cap for this machine`,
387
+ * or why that sentence cannot be written.
388
+ *
389
+ * The two figures are different scopes and the sentence has to say so. The spend
390
+ * is a shared pot the server computes for (owner, team, machineProduct), so every
391
+ * lease of the same product reports the same number — a machine created minutes
392
+ * ago with no active seconds still reports the month's total. The cap is this
393
+ * lease's own. Printed as "$1.23 this month of $500.00 cap" the only available
394
+ * reading was that this machine had spent it.
395
+ */
314
396
  function dedicatedSpendSummary(lease) {
315
397
  const billing = lease.billing;
316
398
  if (!billing)
@@ -321,7 +403,10 @@ function dedicatedSpendSummary(lease) {
321
403
  }
322
404
  const spent = billing.currentMonthSpendCents;
323
405
  const projected = billing.projectedCurrentMonthSpendCents;
324
- const parts = [`${formatUsdCents(spent)} this month of ${formatUsdCents(cap)} cap`];
406
+ const parts = [
407
+ `${formatUsdCents(spent)} this month for machines of this type`,
408
+ `${formatUsdCents(cap)} cap for this machine`,
409
+ ];
325
410
  if (typeof projected === 'number' && projected !== spent) {
326
411
  parts.push(`projected ${formatUsdCents(projected)}`);
327
412
  }
@@ -743,14 +828,128 @@ function dedicatedCatalogFromProducts(payload) {
743
828
  : {}),
744
829
  retentionClasses: configuration.retentionClasses || [],
745
830
  backupProfiles: configuration.backupProfiles || [],
831
+ ...(typeof configuration.defaultSizePreset === 'string'
832
+ ? { defaultSizePreset: configuration.defaultSizePreset }
833
+ : {}),
834
+ ...(typeof configuration.defaultRetentionClass === 'string'
835
+ ? { defaultRetentionClass: configuration.defaultRetentionClass }
836
+ : {}),
837
+ ...(typeof configuration.defaultBackupProfile === 'string'
838
+ ? { defaultBackupProfile: configuration.defaultBackupProfile }
839
+ : {}),
746
840
  defaultMonthlySpendingLimitCents: billing.defaultMonthlySpendingLimitCents,
747
841
  maxUserMonthlySpendingLimitCents: billing.maxUserMonthlySpendingLimitCents,
748
842
  priceLines: Array.isArray(presentation.priceLines)
749
843
  ? presentation.priceLines
750
844
  : [],
845
+ ...(typeof billing.priceBook?.version === 'string'
846
+ ? { priceBookVersion: billing.priceBook.version }
847
+ : {}),
751
848
  ...(typeof presentation.headline === 'string' ? { headline: presentation.headline } : {}),
752
849
  };
753
850
  }
851
+ /**
852
+ * The catalog's size rows: spec, the size's own hourly price, and which one
853
+ * `create` uses by default. The rows used to carry spec alone, sorted by name,
854
+ * so the $1.40/hr tier came first with nothing saying what it cost or that
855
+ * $0.65/hr standard was the default; a reader had to join two blocks by name.
856
+ */
857
+ function dedicatedCatalogSizeLines(catalog) {
858
+ return catalog.sizes.map((size) => {
859
+ const spec = [
860
+ size.vcpus ? `${size.vcpus} vCPU` : '',
861
+ size.memoryGiB ? `${size.memoryGiB} GiB RAM` : '',
862
+ size.accelerator || '',
863
+ typeof size.rateCentsPerHour === 'number' && size.rateCentsPerHour > 0
864
+ ? `${formatUsdCents(size.rateCentsPerHour)}/hr`
865
+ : '',
866
+ ]
867
+ .filter(Boolean)
868
+ .join(' · ');
869
+ const isDefault = size.id !== undefined && size.id === catalog.defaultSizePreset;
870
+ return ` ${String(size.id).padEnd(14)}${spec}${isDefault ? ' (default)' : ''}`;
871
+ });
872
+ }
873
+ /**
874
+ * What a create is about to buy, in the words and numbers the catalog uses.
875
+ *
876
+ * `create` provisioned a billable machine the moment it ran — no confirmation,
877
+ * no dry run, and every omitted flag silently took a paid default — while
878
+ * `destroy` asked first. A loop probing size validation bought two real
879
+ * machines, one of them $1.40/hr (OSK-9592). This is what `create` now shows
880
+ * before it asks, and what `--dry-run` prints instead of creating.
881
+ *
882
+ * Every default comes from the catalog, never from this file: a CLI that
883
+ * restates a default it does not own is how `--storage-gb` once billed twice the
884
+ * product's disk. A value the catalog does not declare is shown as "server
885
+ * default" rather than guessed.
886
+ */
887
+ function dedicatedCreateSummary(catalog, choices) {
888
+ const size = catalog?.sizes.find((entry) => entry.id === choices.sizePreset);
889
+ const spec = size
890
+ ? [
891
+ size.vcpus ? `${size.vcpus} vCPU` : '',
892
+ size.memoryGiB ? `${size.memoryGiB} GiB RAM` : '',
893
+ typeof size.rateCentsPerHour === 'number' && size.rateCentsPerHour > 0
894
+ ? `${formatUsdCents(size.rateCentsPerHour)} per running hour`
895
+ : '',
896
+ ]
897
+ .filter(Boolean)
898
+ .join(' · ')
899
+ : 'not in the catalog this server offers';
900
+ const storagePrice = catalog?.priceLines.find((line) => line.cadence === 'per-gb-month');
901
+ const storageGb = choices.storageGb ?? catalog?.storage?.defaultGb;
902
+ const storage = storageGb !== undefined
903
+ ? `${storageGb} GiB${choices.storageGb === undefined ? ' (catalog default)' : ''}`
904
+ : 'server default';
905
+ const retentionClass = choices.retentionClass ?? catalog?.defaultRetentionClass;
906
+ const retention = retentionClass
907
+ ? `${retentionClass}${choices.retentionClass === undefined ? ' (catalog default)' : ''}`
908
+ : 'server default';
909
+ const retains = retentionClass !== undefined && retentionClass !== 'none';
910
+ const capCents = choices.spendingLimitCents ?? catalog?.defaultMonthlySpendingLimitCents;
911
+ const cap = capCents !== undefined && capCents !== null
912
+ ? `${formatUsdCents(capCents)} per month${choices.spendingLimitCents === undefined ? ' (catalog default)' : ''}`
913
+ : 'server default';
914
+ return [
915
+ `Create Dedicated Runtime "${choices.name}" — it bills while it runs:`,
916
+ ` size ${choices.sizePreset} — ${spec}`,
917
+ ` storage ${storage}${storagePrice?.display ? ` — ${storagePrice.display}` : ''}`,
918
+ ` retention ${retention}${retains ? ' — destroy keeps the workspace, and its storage bills until you purge it' : ''}`,
919
+ ` monthly cap ${cap}`,
920
+ ];
921
+ }
922
+ /**
923
+ * The catalog's money lines, grouped by what each amount IS.
924
+ *
925
+ * A start hold and a monthly cap used to print under "Prices" beside the hourly
926
+ * rates, so starting a machine read as a 33c charge and "$500 default monthly
927
+ * cap" read as a bill (OSK-9536). A line with no `kind` comes from a server that
928
+ * predates it and is shown the way every line used to be: as a charge.
929
+ */
930
+ function dedicatedCatalogPriceLineOutput(priceLines, priceBookVersion) {
931
+ // The version is what a charge dispute later cites; it was in the payload and
932
+ // the command dropped it (OSK-9562).
933
+ const pricesHeading = priceBookVersion ? `Prices (price book ${priceBookVersion})` : 'Prices';
934
+ const groups = [
935
+ { kind: 'charge', heading: pricesHeading },
936
+ { kind: 'hold', heading: 'Held against your cap at start (not billed)' },
937
+ { kind: 'limit', heading: 'Monthly caps (--spending-limit-cents)' },
938
+ ];
939
+ if (priceLines.length === 0)
940
+ return ['', pricesHeading, ' not published by this server'];
941
+ const output = [];
942
+ for (const group of groups) {
943
+ const lines = priceLines.filter((line) => (line.kind || 'charge') === group.kind);
944
+ if (lines.length === 0)
945
+ continue;
946
+ output.push('', group.heading);
947
+ for (const line of lines) {
948
+ output.push(` ${line.display || `${formatUsdCents(line.amountCents)} ${line.label || ''}`}`);
949
+ }
950
+ }
951
+ return output;
952
+ }
754
953
  /**
755
954
  * Dedicated Runtime commands are unusually likely to meet a server that has
756
955
  * never heard of them.
@@ -867,9 +1066,44 @@ function dedicatedRuntimeApiErrorDetails(err) {
867
1066
  */
868
1067
  function describeRefusalReasons(err) {
869
1068
  const details = dedicatedRuntimeApiErrorDetails(err);
1069
+ const spend = describeSpendingCapRefusal(err.body, details);
1070
+ if (spend)
1071
+ return spend;
870
1072
  const sentences = (0, harnesses_1.describeHealthReasonSentences)(details?.reasons, details?.state);
871
1073
  return sentences ? ` Reasons: ${sentences}` : '';
872
1074
  }
1075
+ /**
1076
+ * The numbers behind a cap refusal: the cap, this month's spend so far, and
1077
+ * where the refused action would have taken it — plus the command that raises
1078
+ * the cap. Without them "would exceed the monthly cap" left a user guessing by
1079
+ * how much, and which cap (OSK-9552).
1080
+ */
1081
+ function describeSpendingCapRefusal(body, details) {
1082
+ let code;
1083
+ try {
1084
+ code = JSON.parse(body || '{}').code;
1085
+ }
1086
+ catch {
1087
+ return '';
1088
+ }
1089
+ if (code !== 'RUNTIME_SPENDING_LIMIT_EXCEEDED' || !details)
1090
+ return '';
1091
+ const cents = (key) => {
1092
+ const value = details[key];
1093
+ return typeof value === 'number' && Number.isFinite(value) ? value : null;
1094
+ };
1095
+ const limit = cents('limitCents');
1096
+ const spent = cents('currentSpendCents');
1097
+ const projected = cents('projectedCents');
1098
+ const parts = [
1099
+ limit !== null ? `Cap ${formatUsdCents(limit)}` : '',
1100
+ spent !== null ? `spent this month ${formatUsdCents(spent)}` : '',
1101
+ projected !== null ? `this would bring it to ${formatUsdCents(projected)}` : '',
1102
+ ].filter(Boolean);
1103
+ if (parts.length === 0)
1104
+ return '';
1105
+ return ` ${parts.join('; ')}. Raise the cap with \`machines dedicated spending-limit <lease-id> --cents <n>\`.`;
1106
+ }
873
1107
  function formatDedicatedRuntimeApiError(err) {
874
1108
  if (!(err instanceof api_fetch_1.ApiFetchError) || !err.body) {
875
1109
  return null;
@@ -0,0 +1,3 @@
1
+ export declare function isFailedLookupValue(value: unknown): boolean;
2
+ /** The shared explanation, so every resolver refuses in the same words. */
3
+ export declare function describeFailedLookupValue(value: string, noun: string): string;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFailedLookupValue = isFailedLookupValue;
4
+ exports.describeFailedLookupValue = describeFailedLookupValue;
5
+ /**
6
+ * Values a script produces when a lookup upstream has ALREADY failed.
7
+ *
8
+ * `jq -r .id` on a missing field prints `null`. A JavaScript value that was
9
+ * never set stringifies to `undefined`; an object to `[object Object]`. Python
10
+ * prints `None`, Ruby `nil`, and a boolean field read as an id gives `true` or
11
+ * `false`. No person types any of these as a reference to a Task, a Space or a
12
+ * member — and a script passes one exactly when its previous step went wrong.
13
+ *
14
+ * The reference resolvers are generous by design: a person can type part of a
15
+ * title, and when exactly one title contains it, that is the one they meant.
16
+ * The same generosity turned `tasks complete "$ID"` with `ID=null` into
17
+ * "complete whichever single task mentions null". So these are refused before
18
+ * any search runs. Matching is on the WHOLE trimmed value, case-insensitively:
19
+ * a real title that merely contains one of these words still resolves.
20
+ */
21
+ const FAILED_LOOKUP_VALUES = new Set([
22
+ 'null',
23
+ 'undefined',
24
+ 'none',
25
+ 'nil',
26
+ 'nan',
27
+ 'true',
28
+ 'false',
29
+ '[object object]',
30
+ ]);
31
+ function isFailedLookupValue(value) {
32
+ return typeof value === 'string' && FAILED_LOOKUP_VALUES.has(value.trim().toLowerCase());
33
+ }
34
+ /** The shared explanation, so every resolver refuses in the same words. */
35
+ function describeFailedLookupValue(value, noun) {
36
+ return (`"${value.trim()}" is not a ${noun} reference — it is what a failed lookup prints ` +
37
+ '(for example `jq -r` on a missing field, or an unset variable). Refusing to search ' +
38
+ 'names for it, because a single accidental match would be acted on.\n' +
39
+ ' → check the step that produced this value; it most likely failed.');
40
+ }
@@ -242,10 +242,17 @@ async function resolveFirstPartyHarnessAgent(explicit, deps = {}, options = {})
242
242
  // Asked, not told: the caller opted out of creation, so "no default" is a fact
243
243
  // about this machine rather than a failure, and the remedy is a real command.
244
244
  if (options.createDefault === false) {
245
- throw new FirstPartyHarnessAgentError('agent-unset', `nothing is configured and you have no ${auth_core_1.FIRST_PARTY_HARNESS.displayName} default agent yet. ` +
245
+ throw new FirstPartyHarnessAgentError('agent-unset',
246
+ // No roster here. The question was which agent a session runs as, and a
247
+ // hundred names in front of the one sentence that answers it buried the
248
+ // answer (OSK-9396). A person choosing one has `agents list` for that.
249
+ `nothing is configured and you have no ${auth_core_1.FIRST_PARTY_HARNESS.displayName} default agent yet. ` +
246
250
  'One is created automatically the first time you run a managed session — ' +
247
251
  `or make it now with \`${auth_core_1.FIRST_PARTY_HARNESS.command} skrr-agent --create\`` +
248
- (rows.length ? `. Yours: ${candidates(rows)}` : ''));
252
+ (rows.length
253
+ ? `, or pick one of your ${rows.length} with ` +
254
+ `\`${auth_core_1.FIRST_PARTY_HARNESS.command} skrr-agent --set <id-or-name>\` (see \`skrr agents list\`)`
255
+ : ''));
249
256
  }
250
257
  // The endpoint answered without an agent. Nothing here can repair that, but
251
258
  // the user still has every ordinary way to name one.
@@ -419,6 +426,13 @@ async function currentUserId(signal) {
419
426
  * function only after the flag, the config key, and the sole-owned-agent branch
420
427
  * have all declined, which is why the extra round trip is acceptable.
421
428
  */
429
+ /**
430
+ * Deliberately NOT `dataService.ensureDefaultAgent`, which `agents
431
+ * ensure-default` uses for the same endpoint. This path takes an `AbortSignal`
432
+ * and the caller below distinguishes an abort from a refusal — reporting "no
433
+ * default agent" for a caller whose deadline expired sends someone to fix the
434
+ * wrong thing. The shared helper cannot carry a signal, so this one stays.
435
+ */
422
436
  async function ensureDefaultAgent(harness, signal) {
423
437
  const response = await (0, api_fetch_1.apiFetch)('/api/agents/default', {
424
438
  method: 'POST',
@@ -35,6 +35,21 @@ export interface DoctorCheck {
35
35
  detail: string;
36
36
  /** What to do about it. Only present when there is something to do. */
37
37
  remedy?: string;
38
+ /** The files and directories a check looked at, for `--json` consumers. */
39
+ sources?: DoctorSource[];
40
+ }
41
+ /** One place the engine reads instructions or skills from. */
42
+ export interface DoctorSource {
43
+ /** As a person would type it: relative for the project, `~/` for the user. */
44
+ path: string;
45
+ kind: 'instructions' | 'skills';
46
+ owner: 'engine' | 'compatibility';
47
+ scope: 'project' | 'user';
48
+ present: boolean;
49
+ /** Skill directories holding a SKILL.md; only for a present skill root. */
50
+ skills?: number;
51
+ /** Why the engine will not read it although it exists, when it will not. */
52
+ disabled?: string;
38
53
  }
39
54
  export interface DoctorReport {
40
55
  checks: DoctorCheck[];
@@ -36,6 +36,7 @@ exports.projectStateDir = projectStateDir;
36
36
  exports.runDoctor = runDoctor;
37
37
  exports.formatReport = formatReport;
38
38
  const node_fs_1 = require("node:fs");
39
+ const node_os_1 = require("node:os");
39
40
  const node_path_1 = __importDefault(require("node:path"));
40
41
  const first_party_harness_1 = require("./first-party-harness");
41
42
  const auth_core_1 = require("@skrr-ai/auth-core");
@@ -285,40 +286,138 @@ function stateDirCheck(cwd, env) {
285
286
  ];
286
287
  return { name: 'state-paths', status: 'ok', detail: parts.join('; ') };
287
288
  }
288
- function compatCheck(cwd, env) {
289
- // Report both the engine's own writable instruction surfaces and read-only
290
- // compatibility sources so "where did this instruction come from?" has an
291
- // answer — a WL-2 acceptance criterion.
292
- const root = node_path_1.default.resolve(cwd);
293
- const own = [];
294
- for (const file of [
295
- node_path_1.default.join(projectStateDir(root), 'AGENTS.md'),
296
- node_path_1.default.join((0, first_party_harness_1.engineInstructionHome)(env), 'AGENTS.md'),
297
- ]) {
298
- if ((0, node_fs_1.existsSync)(file))
299
- own.push(file);
289
+ /**
290
+ * Skill directories directly under `root` that hold a SKILL.md.
291
+ *
292
+ * A symlinked skill directory counts: installers and dotfile managers link
293
+ * skills in, and a count that skipped them reported 11 of a root's 31.
294
+ */
295
+ function countSkills(root) {
296
+ try {
297
+ return (0, node_fs_1.readdirSync)(root).filter((name) => {
298
+ try {
299
+ return ((0, node_fs_1.statSync)(node_path_1.default.join(root, name)).isDirectory() &&
300
+ (0, node_fs_1.existsSync)(node_path_1.default.join(root, name, 'SKILL.md')));
301
+ }
302
+ catch {
303
+ return false;
304
+ }
305
+ }).length;
300
306
  }
301
- const compatibility = [];
302
- for (const rel of ['AGENTS.md', 'CLAUDE.md', '.agents/skills', '.claude/skills']) {
303
- if ((0, node_fs_1.existsSync)(node_path_1.default.join(root, rel)))
304
- compatibility.push(rel);
307
+ catch {
308
+ return 0;
305
309
  }
306
- if (own.length === 0 && compatibility.length === 0) {
310
+ }
311
+ function compatCheck(cwd, env) {
312
+ // Report every instruction and skill source the engine reads, owned and
313
+ // compatibility alike, so "where did this come from?" has an answer — a WL-2
314
+ // acceptance criterion.
315
+ //
316
+ // It named two skill roots, project-level only, as bare directory names: the
317
+ // user-level `~/.agents/skills` and `~/.claude/skills` the engine also reads
318
+ // were missing, an empty root and a full one looked the same, and nothing said
319
+ // why a machine's other roots (.devin, .windsurf, .codex, …) were absent — so
320
+ // a reader could not tell "not read" from "not reported" (OSK-9357). The set
321
+ // below is the engine's documented compatibility contract
322
+ // (opencode-white-label-implementation-plan §7.2); roots beyond it reach a
323
+ // session only when the daemon links them in, which `skrr skills local` shows.
324
+ const root = node_path_1.default.resolve(cwd);
325
+ const home = env.HOME || (0, node_os_1.homedir)();
326
+ const projectDir = auth_core_1.FIRST_PARTY_HARNESS_ENGINE.projectDirectory;
327
+ const noCompatPrompts = Boolean(env[auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.noCompatPrompts]);
328
+ const engineHomeDir = (0, first_party_harness_1.engineInstructionHome)(env);
329
+ const shown = (abs) => abs.startsWith(root + node_path_1.default.sep)
330
+ ? node_path_1.default.relative(root, abs)
331
+ : abs.startsWith(home + node_path_1.default.sep)
332
+ ? `~/${node_path_1.default.relative(home, abs)}`
333
+ : abs;
334
+ const declared = [
335
+ {
336
+ abs: node_path_1.default.join(root, projectDir, 'AGENTS.md'),
337
+ kind: 'instructions',
338
+ owner: 'engine',
339
+ scope: 'project',
340
+ },
341
+ {
342
+ abs: node_path_1.default.join(engineHomeDir, 'AGENTS.md'),
343
+ kind: 'instructions',
344
+ owner: 'engine',
345
+ scope: 'user',
346
+ },
347
+ {
348
+ abs: node_path_1.default.join(root, 'AGENTS.md'),
349
+ kind: 'instructions',
350
+ owner: 'compatibility',
351
+ scope: 'project',
352
+ },
353
+ {
354
+ abs: node_path_1.default.join(root, 'CLAUDE.md'),
355
+ kind: 'instructions',
356
+ owner: 'compatibility',
357
+ scope: 'project',
358
+ ...(noCompatPrompts ? { disabled: auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.noCompatPrompts } : {}),
359
+ },
360
+ {
361
+ abs: node_path_1.default.join(home, '.claude', 'CLAUDE.md'),
362
+ kind: 'instructions',
363
+ owner: 'compatibility',
364
+ scope: 'user',
365
+ ...(noCompatPrompts ? { disabled: auth_core_1.FIRST_PARTY_HARNESS_ENGINE.env.noCompatPrompts } : {}),
366
+ },
367
+ {
368
+ abs: node_path_1.default.join(root, '.agents', 'skills'),
369
+ kind: 'skills',
370
+ owner: 'compatibility',
371
+ scope: 'project',
372
+ },
373
+ {
374
+ abs: node_path_1.default.join(root, '.claude', 'skills'),
375
+ kind: 'skills',
376
+ owner: 'compatibility',
377
+ scope: 'project',
378
+ },
379
+ {
380
+ abs: node_path_1.default.join(home, '.agents', 'skills'),
381
+ kind: 'skills',
382
+ owner: 'compatibility',
383
+ scope: 'user',
384
+ },
385
+ {
386
+ abs: node_path_1.default.join(home, '.claude', 'skills'),
387
+ kind: 'skills',
388
+ owner: 'compatibility',
389
+ scope: 'user',
390
+ },
391
+ ];
392
+ const sources = declared.map(({ abs, ...source }) => {
393
+ const present = (0, node_fs_1.existsSync)(abs);
307
394
  return {
308
- name: 'compat-sources',
309
- status: 'ok',
310
- detail: `no ${auth_core_1.FIRST_PARTY_HARNESS.displayName}-owned or compatibility instruction files in this project`,
395
+ path: shown(abs),
396
+ ...source,
397
+ present,
398
+ ...(source.kind === 'skills' && present ? { skills: countSkills(abs) } : {}),
311
399
  };
312
- }
313
- const details = [];
314
- if (own.length > 0)
315
- details.push(`${auth_core_1.FIRST_PARTY_HARNESS.displayName}-owned: ${own.join(', ')}`);
316
- if (compatibility.length > 0)
317
- details.push(`compatibility (read-only): ${compatibility.join(', ')}`);
400
+ });
401
+ const describe = (source) => {
402
+ if (!source.present)
403
+ return `${source.path} (absent)`;
404
+ if (source.disabled)
405
+ return `${source.path} (off: ${source.disabled})`;
406
+ if (source.kind === 'skills') {
407
+ return `${source.path} (${source.skills} skill${source.skills === 1 ? '' : 's'})`;
408
+ }
409
+ return source.path;
410
+ };
411
+ const owned = sources.filter((source) => source.owner === 'engine');
412
+ const compatibility = sources.filter((source) => source.owner === 'compatibility');
318
413
  return {
319
414
  name: 'compat-sources',
320
415
  status: 'ok',
321
- detail: details.join('; '),
416
+ detail: `${auth_core_1.FIRST_PARTY_HARNESS.displayName}-owned: ${owned.map(describe).join(', ')}; ` +
417
+ `compatibility (read-only): ${compatibility.map(describe).join(', ')}. ` +
418
+ 'A daemon-hosted session also gets the skills its daemon discovers under other roots ' +
419
+ '(.devin, .windsurf, .codex, …) — `skrr skills local` lists them.',
420
+ sources,
322
421
  };
323
422
  }
324
423
  function opencodeCheck(cwd) {