@skrr-ai/cli 0.1.22 → 0.1.24

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 (281) hide show
  1. package/README.md +83 -73
  2. package/bin/dev-fallback.js +221 -0
  3. package/bin/run.js +96 -0
  4. package/dist/base-command.d.ts +15 -0
  5. package/dist/base-command.js +145 -11
  6. package/dist/commands/agents/attach-harness.d.ts +37 -0
  7. package/dist/commands/agents/attach-harness.js +102 -3
  8. package/dist/commands/agents/chat.d.ts +69 -1
  9. package/dist/commands/agents/chat.js +202 -22
  10. package/dist/commands/agents/create.d.ts +38 -0
  11. package/dist/commands/agents/create.js +95 -2
  12. package/dist/commands/agents/schedule-trace.js +11 -0
  13. package/dist/commands/agents/show.js +29 -1
  14. package/dist/commands/api-keys/create.d.ts +24 -0
  15. package/dist/commands/api-keys/create.js +80 -0
  16. package/dist/commands/api-keys/list.d.ts +11 -0
  17. package/dist/commands/api-keys/list.js +42 -0
  18. package/dist/commands/api-keys/revoke.d.ts +14 -0
  19. package/dist/commands/api-keys/revoke.js +33 -0
  20. package/dist/commands/api-keys/rotate.d.ts +19 -0
  21. package/dist/commands/api-keys/rotate.js +42 -0
  22. package/dist/commands/balance/index.d.ts +18 -0
  23. package/dist/commands/balance/index.js +29 -0
  24. package/dist/commands/balance/show.d.ts +25 -0
  25. package/dist/commands/balance/show.js +60 -0
  26. package/dist/commands/balance/statement.d.ts +19 -0
  27. package/dist/commands/balance/statement.js +52 -0
  28. package/dist/commands/browser/doctor.d.ts +11 -0
  29. package/dist/commands/browser/doctor.js +11 -0
  30. package/dist/commands/code/jobs/approve.d.ts +25 -0
  31. package/dist/commands/code/jobs/approve.js +52 -0
  32. package/dist/commands/code/jobs/cancel.d.ts +12 -0
  33. package/dist/commands/code/jobs/cancel.js +39 -0
  34. package/dist/commands/code/jobs/index.d.ts +19 -0
  35. package/dist/commands/code/jobs/index.js +38 -0
  36. package/dist/commands/code/jobs/list.d.ts +12 -0
  37. package/dist/commands/code/jobs/list.js +87 -0
  38. package/dist/commands/code/jobs/run.d.ts +32 -0
  39. package/dist/commands/code/jobs/run.js +151 -0
  40. package/dist/commands/code/jobs/show.d.ts +12 -0
  41. package/dist/commands/code/jobs/show.js +86 -0
  42. package/dist/commands/commitments/preflight.js +18 -4
  43. package/dist/commands/commitments/receipt-metrics.d.ts +39 -0
  44. package/dist/commands/commitments/receipt-metrics.js +78 -0
  45. package/dist/commands/convos/search.js +5 -5
  46. package/dist/commands/harnesses/leases/list.d.ts +3 -0
  47. package/dist/commands/harnesses/leases/list.js +59 -12
  48. package/dist/commands/harnesses/leases/show.js +36 -0
  49. package/dist/commands/harnesses/list.d.ts +18 -0
  50. package/dist/commands/harnesses/list.js +30 -0
  51. package/dist/commands/harnesses/products.js +49 -3
  52. package/dist/commands/harnesses/quota.d.ts +16 -0
  53. package/dist/commands/harnesses/quota.js +26 -1
  54. package/dist/commands/harnesses/usage.js +17 -0
  55. package/dist/commands/inbox/index.d.ts +2 -1
  56. package/dist/commands/inbox/index.js +27 -4
  57. package/dist/commands/login.js +4 -1
  58. package/dist/commands/machines/dedicated/create.d.ts +18 -0
  59. package/dist/commands/machines/dedicated/create.js +90 -0
  60. package/dist/commands/machines/dedicated/destroy.d.ts +14 -0
  61. package/dist/commands/machines/dedicated/destroy.js +56 -0
  62. package/dist/commands/machines/dedicated/grow.d.ts +13 -0
  63. package/dist/commands/machines/dedicated/grow.js +46 -0
  64. package/dist/commands/machines/dedicated/health-check.d.ts +12 -0
  65. package/dist/commands/machines/dedicated/health-check.js +42 -0
  66. package/dist/commands/machines/dedicated/incident-close.d.ts +12 -0
  67. package/dist/commands/machines/dedicated/incident-close.js +42 -0
  68. package/dist/commands/machines/dedicated/index.d.ts +6 -0
  69. package/dist/commands/machines/dedicated/index.js +39 -0
  70. package/dist/commands/machines/dedicated/list.d.ts +11 -0
  71. package/dist/commands/machines/dedicated/list.js +58 -0
  72. package/dist/commands/machines/dedicated/restart.d.ts +12 -0
  73. package/dist/commands/machines/dedicated/restart.js +42 -0
  74. package/dist/commands/machines/dedicated/show.d.ts +12 -0
  75. package/dist/commands/machines/dedicated/show.js +58 -0
  76. package/dist/commands/machines/dedicated/snapshot.d.ts +12 -0
  77. package/dist/commands/machines/dedicated/snapshot.js +42 -0
  78. package/dist/commands/machines/dedicated/start.d.ts +12 -0
  79. package/dist/commands/machines/dedicated/start.js +42 -0
  80. package/dist/commands/machines/dedicated/stop.d.ts +12 -0
  81. package/dist/commands/machines/dedicated/stop.js +42 -0
  82. package/dist/commands/machines/hosted/index.js +4 -1
  83. package/dist/commands/machines/list.d.ts +1 -0
  84. package/dist/commands/machines/list.js +33 -5
  85. package/dist/commands/memory/approvals/issue.d.ts +18 -0
  86. package/dist/commands/memory/approvals/issue.js +63 -0
  87. package/dist/commands/memory/commit.js +5 -2
  88. package/dist/commands/memory/context.d.ts +3 -2
  89. package/dist/commands/memory/context.js +9 -11
  90. package/dist/commands/memory/delete.d.ts +1 -1
  91. package/dist/commands/memory/list.d.ts +1 -1
  92. package/dist/commands/memory/list.js +1 -1
  93. package/dist/commands/memory/mounts/resolve.d.ts +10 -0
  94. package/dist/commands/memory/mounts/resolve.js +27 -0
  95. package/dist/commands/memory/proposals/commit.d.ts +18 -0
  96. package/dist/commands/memory/proposals/commit.js +32 -0
  97. package/dist/commands/memory/proposals/create.d.ts +18 -0
  98. package/dist/commands/memory/proposals/create.js +56 -0
  99. package/dist/commands/memory/proposals/list.d.ts +13 -0
  100. package/dist/commands/memory/proposals/list.js +31 -0
  101. package/dist/commands/memory/proposals/promote.d.ts +22 -0
  102. package/dist/commands/memory/proposals/promote.js +33 -0
  103. package/dist/commands/memory/proposals/reject.d.ts +16 -0
  104. package/dist/commands/memory/proposals/reject.js +28 -0
  105. package/dist/commands/memory/rebuild-index.d.ts +20 -0
  106. package/dist/commands/memory/rebuild-index.js +25 -0
  107. package/dist/commands/memory/save.d.ts +1 -1
  108. package/dist/commands/memory/save.js +1 -1
  109. package/dist/commands/memory/search.d.ts +6 -3
  110. package/dist/commands/memory/search.js +27 -3
  111. package/dist/commands/memory/show.d.ts +1 -1
  112. package/dist/commands/memory/stores/archive.d.ts +14 -0
  113. package/dist/commands/memory/stores/archive.js +31 -0
  114. package/dist/commands/memory/stores/create.d.ts +12 -0
  115. package/dist/commands/memory/stores/create.js +30 -0
  116. package/dist/commands/memory/stores/delete.d.ts +15 -0
  117. package/dist/commands/memory/stores/delete.js +38 -0
  118. package/dist/commands/memory/stores/list.d.ts +10 -0
  119. package/dist/commands/memory/stores/list.js +29 -0
  120. package/dist/commands/memory/stores/show.d.ts +12 -0
  121. package/dist/commands/memory/stores/show.js +14 -0
  122. package/dist/commands/memory/stores/update.d.ts +15 -0
  123. package/dist/commands/memory/stores/update.js +39 -0
  124. package/dist/commands/memory/sync/delete.d.ts +13 -0
  125. package/dist/commands/memory/sync/delete.js +35 -0
  126. package/dist/commands/memory/sync/pull.d.ts +19 -0
  127. package/dist/commands/memory/sync/pull.js +52 -0
  128. package/dist/commands/memory/sync/push.d.ts +18 -0
  129. package/dist/commands/memory/sync/push.js +115 -0
  130. package/dist/commands/memory/sync/resolve.d.ts +23 -0
  131. package/dist/commands/memory/sync/resolve.js +27 -0
  132. package/dist/commands/memory/sync/status.d.ts +9 -0
  133. package/dist/commands/memory/sync/status.js +22 -0
  134. package/dist/commands/memory/topics/archive.d.ts +20 -0
  135. package/dist/commands/memory/topics/archive.js +29 -0
  136. package/dist/commands/memory/topics/create.d.ts +23 -0
  137. package/dist/commands/memory/topics/create.js +46 -0
  138. package/dist/commands/memory/topics/delete.d.ts +22 -0
  139. package/dist/commands/memory/topics/delete.js +30 -0
  140. package/dist/commands/memory/topics/forget.d.ts +22 -0
  141. package/dist/commands/memory/topics/forget.js +29 -0
  142. package/dist/commands/memory/topics/list.d.ts +13 -0
  143. package/dist/commands/memory/topics/list.js +22 -0
  144. package/dist/commands/memory/topics/move.d.ts +22 -0
  145. package/dist/commands/memory/topics/move.js +26 -0
  146. package/dist/commands/memory/topics/show.d.ts +17 -0
  147. package/dist/commands/memory/topics/show.js +21 -0
  148. package/dist/commands/memory/topics/update.d.ts +23 -0
  149. package/dist/commands/memory/topics/update.js +27 -0
  150. package/dist/commands/memory/tree.d.ts +5 -2
  151. package/dist/commands/memory/tree.js +20 -2
  152. package/dist/commands/memory/update.d.ts +1 -1
  153. package/dist/commands/memory/versions/list.d.ts +16 -0
  154. package/dist/commands/memory/versions/list.js +22 -0
  155. package/dist/commands/memory/versions/redact.d.ts +21 -0
  156. package/dist/commands/memory/versions/redact.js +22 -0
  157. package/dist/commands/memory/versions/restore.d.ts +20 -0
  158. package/dist/commands/memory/versions/restore.js +16 -0
  159. package/dist/commands/memory/versions/show.d.ts +15 -0
  160. package/dist/commands/memory/versions/show.js +17 -0
  161. package/dist/commands/messages/list.js +25 -8
  162. package/dist/commands/messages/show.js +10 -0
  163. package/dist/commands/spaces/update.js +16 -12
  164. package/dist/commands/spaces/work-sync/reconcile.d.ts +38 -0
  165. package/dist/commands/spaces/work-sync/reconcile.js +90 -0
  166. package/dist/commands/spaces/work-sync/telemetry.d.ts +29 -0
  167. package/dist/commands/spaces/work-sync/telemetry.js +96 -0
  168. package/dist/commands/store/install.js +5 -1
  169. package/dist/commands/store/releases.js +1 -1
  170. package/dist/commands/store/update.js +5 -1
  171. package/dist/commands/subscriptions/cancel.js +5 -1
  172. package/dist/commands/subscriptions/health.js +5 -1
  173. package/dist/commands/subscriptions/status.js +5 -1
  174. package/dist/commands/subscriptions/subscribe.js +5 -1
  175. package/dist/commands/tasks/activity.d.ts +2 -3
  176. package/dist/commands/tasks/activity.js +20 -6
  177. package/dist/commands/tasks/complete.d.ts +22 -150
  178. package/dist/commands/tasks/complete.js +174 -599
  179. package/dist/commands/tasks/create.d.ts +6 -0
  180. package/dist/commands/tasks/create.js +114 -4
  181. package/dist/commands/tasks/deliverable/add.d.ts +8 -0
  182. package/dist/commands/tasks/deliverable/add.js +62 -11
  183. package/dist/commands/tasks/deliverable/confirm.d.ts +5 -5
  184. package/dist/commands/tasks/deliverable/confirm.js +9 -18
  185. package/dist/commands/tasks/deliverable/list.d.ts +12 -0
  186. package/dist/commands/tasks/deliverable/list.js +58 -0
  187. package/dist/commands/tasks/events/append.d.ts +2 -0
  188. package/dist/commands/tasks/events/append.js +41 -6
  189. package/dist/commands/tasks/events/list.d.ts +1 -0
  190. package/dist/commands/tasks/events/list.js +1 -0
  191. package/dist/commands/tasks/events/tail.d.ts +1 -0
  192. package/dist/commands/tasks/events/tail.js +1 -0
  193. package/dist/commands/tasks/expectations/assess.d.ts +21 -0
  194. package/dist/commands/tasks/expectations/assess.js +103 -0
  195. package/dist/commands/tasks/expectations.js +121 -25
  196. package/dist/commands/tasks/judge.d.ts +1 -0
  197. package/dist/commands/tasks/judge.js +2 -1
  198. package/dist/commands/tasks/output.d.ts +1 -11
  199. package/dist/commands/tasks/output.js +80 -141
  200. package/dist/commands/tasks/report.d.ts +3 -3
  201. package/dist/commands/tasks/report.js +66 -41
  202. package/dist/commands/tasks/resource/add.d.ts +23 -0
  203. package/dist/commands/tasks/resource/add.js +76 -0
  204. package/dist/commands/tasks/resource/list.d.ts +13 -0
  205. package/dist/commands/tasks/resource/list.js +41 -0
  206. package/dist/commands/tasks/result/show.d.ts +13 -0
  207. package/dist/commands/tasks/result/show.js +64 -0
  208. package/dist/commands/tasks/result/submit.d.ts +26 -0
  209. package/dist/commands/tasks/result/submit.js +134 -0
  210. package/dist/commands/tasks/resume/save.d.ts +16 -0
  211. package/dist/commands/tasks/resume/save.js +69 -0
  212. package/dist/commands/tasks/resume/show.d.ts +13 -0
  213. package/dist/commands/tasks/resume/show.js +35 -0
  214. package/dist/commands/tasks/review.d.ts +17 -0
  215. package/dist/commands/tasks/review.js +54 -0
  216. package/dist/commands/tasks/runs.js +5 -0
  217. package/dist/commands/tasks/self-schedule.js +7 -4
  218. package/dist/commands/tasks/show.js +19 -0
  219. package/dist/commands/tasks/timeline.d.ts +2 -2
  220. package/dist/commands/tasks/timeline.js +19 -24
  221. package/dist/commands/tasks/updates/add.d.ts +22 -0
  222. package/dist/commands/tasks/updates/add.js +122 -0
  223. package/dist/commands/tasks/updates/list.d.ts +15 -0
  224. package/dist/commands/tasks/updates/list.js +57 -0
  225. package/dist/commands/tasks/waive.d.ts +1 -0
  226. package/dist/commands/tasks/waive.js +2 -1
  227. package/dist/lib/agentic-stream.d.ts +257 -0
  228. package/dist/lib/agentic-stream.js +490 -33
  229. package/dist/lib/balance.d.ts +8 -0
  230. package/dist/lib/commitment-product.d.ts +10 -1
  231. package/dist/lib/commitment-product.js +66 -2
  232. package/dist/lib/commitments.d.ts +2 -1
  233. package/dist/lib/commitments.js +1 -0
  234. package/dist/lib/conversation-search.d.ts +3 -0
  235. package/dist/lib/conversation-search.js +75 -0
  236. package/dist/lib/dedicated-machines.d.ts +66 -0
  237. package/dist/lib/dedicated-machines.js +161 -0
  238. package/dist/lib/execution-target.d.ts +21 -0
  239. package/dist/lib/execution-target.js +22 -6
  240. package/dist/lib/harnesses.d.ts +87 -6
  241. package/dist/lib/harnesses.js +32 -1
  242. package/dist/lib/hosted-machines.js +9 -0
  243. package/dist/lib/inbox.d.ts +2 -1
  244. package/dist/lib/machines.d.ts +17 -0
  245. package/dist/lib/machines.js +29 -0
  246. package/dist/lib/managed-agent-keys.d.ts +104 -0
  247. package/dist/lib/managed-agent-keys.js +50 -0
  248. package/dist/lib/memory-scope.d.ts +4 -3
  249. package/dist/lib/memory-scope.js +9 -9
  250. package/dist/lib/memory-sync.d.ts +114 -0
  251. package/dist/lib/memory-sync.js +730 -0
  252. package/dist/lib/message-provenance.d.ts +42 -0
  253. package/dist/lib/message-provenance.js +89 -0
  254. package/dist/lib/scoped-memory-command.d.ts +68 -0
  255. package/dist/lib/scoped-memory-command.js +183 -0
  256. package/dist/lib/session-task-endpoints.d.ts +1 -1
  257. package/dist/lib/session-task-endpoints.js +7 -0
  258. package/dist/lib/sky-code-broker.d.ts +47 -0
  259. package/dist/lib/sky-code-broker.js +50 -0
  260. package/dist/lib/sky-code-doctor.js +10 -1
  261. package/dist/lib/task-artifact-flags.d.ts +2 -0
  262. package/dist/lib/task-artifact-flags.js +7 -0
  263. package/dist/lib/task-execution.d.ts +4 -5
  264. package/dist/lib/task-execution.js +4 -5
  265. package/dist/lib/task-extras.d.ts +1 -5
  266. package/dist/lib/task-extras.js +1 -5
  267. package/dist/lib/task-legacy.d.ts +24 -0
  268. package/dist/lib/task-legacy.js +48 -0
  269. package/dist/lib/task-transcript.d.ts +5 -7
  270. package/dist/lib/task-transcript.js +8 -16
  271. package/dist/lib/tasks.d.ts +39 -21
  272. package/dist/lib/tasks.js +64 -21
  273. package/dist/lib/triggers.d.ts +39 -1
  274. package/dist/lib/triggers.js +70 -2
  275. package/dist/lib/usage-discovery.d.ts +17 -1
  276. package/dist/lib/usage-discovery.js +17 -1
  277. package/dist/lib/work-sync.d.ts +70 -0
  278. package/dist/lib/work-sync.js +29 -0
  279. package/dist/node_modules/@skrr-ai/data-provider/index.js +3760 -3621
  280. package/oclif.manifest.json +14653 -7813
  281. package/package.json +26 -5
@@ -163,6 +163,14 @@ export interface BalanceStatementEntry {
163
163
  service: string | null;
164
164
  provider: string | null;
165
165
  model: string | null;
166
+ /**
167
+ * WHICH agent and conversation incurred it (OSK-7641).
168
+ *
169
+ * Optional because they are: the ledger carries them only where the producer
170
+ * knows them, and an older row has neither.
171
+ */
172
+ agentId?: string | null;
173
+ conversationId?: string | null;
166
174
  /** What incurred it, e.g. `session` / `run` — with `sourceId`, the thing
167
175
  * `balance history` could never name. */
168
176
  sourceType?: string;
@@ -1,4 +1,4 @@
1
- import type { CommitmentDeliveryIntent, CommitmentExecutionReceipt } from '@skrr-ai/data-provider';
1
+ import type { CommitmentDeliveryIntent, CommitmentExecutionReceipt, CommitmentExecutionReceiptQuarterlyMetricsResponse } from '@skrr-ai/data-provider';
2
2
  export declare const COMMITMENT_PRODUCT_MODES: readonly ["monitor", "prepare", "execute"];
3
3
  export declare const COMMITMENT_DELIVERY_CHOICES: readonly ["report-only", "local-patch", "draft-pr", "push-branch", "direct-branch"];
4
4
  /** Compatibility is accepted at the input boundary; everyday output uses the product contract. */
@@ -12,3 +12,12 @@ export declare function deliveryInput(mode: string, options?: {
12
12
  /** Monitor is observation-only; retain report expectations, never repo coordinates. */
13
13
  export declare function deliveryForProductMode(mode: string | undefined, delivery?: CommitmentDeliveryIntent): CommitmentDeliveryIntent | undefined;
14
14
  export declare function renderExecutionReceipt(receipt: CommitmentExecutionReceipt, log: (line: string) => void, verbose?: boolean): void;
15
+ /**
16
+ * `skrr commitments receipt-metrics <id>` — the closed-quarter rollup.
17
+ *
18
+ * Ordered as the funnel it describes rather than as the payload's field order:
19
+ * a wake arrives, some wakes were eligible to act on, some of those started an
20
+ * execution, some declared a delivery, and some of THOSE were verified. Reading
21
+ * it top to bottom is how you see which step the loop is losing work at.
22
+ */
23
+ export declare function renderExecutionReceiptQuarterlyMetrics(response: CommitmentExecutionReceiptQuarterlyMetricsResponse, log: (line: string) => void, verbose?: boolean): void;
@@ -7,6 +7,7 @@ exports.deliveryLabel = deliveryLabel;
7
7
  exports.deliveryInput = deliveryInput;
8
8
  exports.deliveryForProductMode = deliveryForProductMode;
9
9
  exports.renderExecutionReceipt = renderExecutionReceipt;
10
+ exports.renderExecutionReceiptQuarterlyMetrics = renderExecutionReceiptQuarterlyMetrics;
10
11
  exports.COMMITMENT_PRODUCT_MODES = ['monitor', 'prepare', 'execute'];
11
12
  exports.COMMITMENT_DELIVERY_CHOICES = [
12
13
  'report-only',
@@ -71,8 +72,9 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
71
72
  const mark = { done: '✓', working: '→', waiting: '…', failed: '×', skipped: '–' }[phase.status];
72
73
  log(` ${mark} ${phase.phase[0].toUpperCase() + phase.phase.slice(1)}: ${phase.summary}${phase.at ? ` · ${phase.at}` : ''}`);
73
74
  for (const evidence of phase.evidence) {
74
- if (evidence.url)
75
- log(` ${evidence.label}: ${evidence.url}`);
75
+ const location = evidence.url || evidence.path;
76
+ if (location)
77
+ log(` ${evidence.label}: ${location}`);
76
78
  }
77
79
  }
78
80
  if (receipt.nextActor)
@@ -85,3 +87,65 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
85
87
  if (verbose)
86
88
  log(`Diagnostics:\n${JSON.stringify({ id: receipt.id, revision: receipt.revision, evidence: receipt.evidence, diagnostics: receipt.diagnostics }, null, 2)}`);
87
89
  }
90
+ /**
91
+ * A rate the server could not compute is NOT zero.
92
+ *
93
+ * Every `*Rate` on the metrics payload is `number | null`, and the null means
94
+ * "no denominator in this cohort" — a quarter with no eligible material wake
95
+ * has no verified-outcome rate to report. Printing that as `0%` would read as
96
+ * "we tried and delivered nothing", which is the opposite of "we were never
97
+ * asked". So an absent rate renders as an em dash and stays visibly absent.
98
+ */
99
+ function rate(value) {
100
+ if (value === null || value === undefined)
101
+ return '—';
102
+ return `${(value * 100).toFixed(1)}%`;
103
+ }
104
+ /** Same rule for a ratio that is not a percentage. */
105
+ function ratio(value) {
106
+ if (value === null || value === undefined)
107
+ return '—';
108
+ return value.toFixed(2);
109
+ }
110
+ /**
111
+ * `skrr commitments receipt-metrics <id>` — the closed-quarter rollup.
112
+ *
113
+ * Ordered as the funnel it describes rather than as the payload's field order:
114
+ * a wake arrives, some wakes were eligible to act on, some of those started an
115
+ * execution, some declared a delivery, and some of THOSE were verified. Reading
116
+ * it top to bottom is how you see which step the loop is losing work at.
117
+ */
118
+ function renderExecutionReceiptQuarterlyMetrics(response, log, verbose = false) {
119
+ const { cohort, metrics } = response;
120
+ log(`Q${cohort.quarter} ${cohort.year} · ${cohort.from} → ${cohort.to}`);
121
+ log(`Checks in cohort: ${cohort.checkCount} · receipts: ${metrics.receiptCount}`);
122
+ log('');
123
+ log('Funnel');
124
+ log(` Material wakes: ${metrics.materialWakes} (${metrics.eligibleMaterialWakes} eligible)`);
125
+ log(` Execute starts: ${metrics.executeStarts}`);
126
+ log(` Declared deliveries: ${metrics.declaredDeliveries}`);
127
+ log(` Verified outcomes: ${metrics.verifiedOutcomes}`);
128
+ log(` Blocked: ${metrics.blocked}`);
129
+ log('');
130
+ log('Rates');
131
+ log(` Verified per eligible wake: ${ratio(metrics.verifiedOutcomesPerEligibleMaterialWake)}`);
132
+ log(` Verified outcome rate: ${rate(metrics.verifiedOutcomeRate)}`);
133
+ log(` Delivery ratio: ${rate(metrics.deliveryRatio)}`);
134
+ log(` Autonomous, no intervention: ${rate(metrics.autonomousDeliveryWithoutInterventionRate)}`);
135
+ log(` Human intervention rate: ${rate(metrics.humanInterventionRate)}`);
136
+ log(` Orphaned delivery rate: ${rate(metrics.orphanedDeliveryRate)}`);
137
+ log(` False ready / false blocked: ${rate(metrics.falseReadyRate)} / ${rate(metrics.falseBlockedRate)}`);
138
+ // The unknown counts decide whether the rates above are worth acting on, so
139
+ // they are one flag away rather than buried in --json.
140
+ if (verbose) {
141
+ const m = metrics.measurement;
142
+ log('');
143
+ log('Measurement');
144
+ log(` Cohort: ${m.cohort}`);
145
+ log(` Intervention: ${m.interventionDefinition}`);
146
+ log(` Eligible material wake: ${m.eligibleMaterialWakeDefinition}`);
147
+ log(` Unknown admission runs: ${m.unknownAdmissionRuns} · unknown startup runs: ${m.unknownStartupRuns}`);
148
+ log(` Tracked completed runs: ${m.trackedCompletedRuns}`);
149
+ log(` Time to explanation: p50 ${metrics.timeToExplanationMs.p50 ?? '—'}ms · p95 ${metrics.timeToExplanationMs.p95 ?? '—'}ms (n=${metrics.timeToExplanationMs.count})`);
150
+ }
151
+ }
@@ -17,7 +17,7 @@
17
17
  * "hold this state", derived from a Compass invariant — not authored here).
18
18
  */
19
19
  import { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, COMMITMENT_AUTONOMY_MODES, COMMITMENT_EXECUTION_ISOLATION_MODES } from '@skrr-ai/data-provider';
20
- import type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentStatusResponse, CommitmentWorkLink, CommitmentExecutionReceiptListResponse, CommitmentCapabilitySource, CommitmentDeliveryPatchResponse, CommitmentDeliveryBundleResponse } from '@skrr-ai/data-provider';
20
+ import type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentStatusResponse, CommitmentWorkLink, CommitmentExecutionReceiptListResponse, CommitmentExecutionReceiptQuarterlyMetricsResponse, CommitmentCapabilitySource, CommitmentDeliveryPatchResponse, CommitmentDeliveryBundleResponse } from '@skrr-ai/data-provider';
21
21
  import { withQuery } from './triggers';
22
22
  export { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, };
23
23
  export type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentActionProposal, CommitmentListResponse, CommitmentPlanResponse, CommitmentStatusResponse, CommitmentWorkLink, };
@@ -284,6 +284,7 @@ export declare const commitmentApi: {
284
284
  mode: (id: string, body: Record<string, unknown>) => Promise<Commitment>;
285
285
  delivery: (id: string, body: Record<string, unknown>) => Promise<Commitment>;
286
286
  receipts: (id: string, limit?: number) => Promise<CommitmentExecutionReceiptListResponse>;
287
+ receiptQuarterlyMetrics: (id: string, year: number, quarter: number) => Promise<CommitmentExecutionReceiptQuarterlyMetricsResponse>;
287
288
  deliveryPatch: (id: string, taskId: string) => Promise<CommitmentDeliveryPatchResponse>;
288
289
  deliveryBundle: (id: string, taskId: string) => Promise<CommitmentDeliveryBundleResponse>;
289
290
  operatingReview: (agentId: string) => Promise<Record<string, unknown>>;
@@ -183,6 +183,7 @@ exports.commitmentApi = {
183
183
  mode: (id, body) => data_provider_1.request.post(`${base(id)}/mode`, body),
184
184
  delivery: (id, body) => data_provider_1.request.post(`${base(id)}/delivery`, body),
185
185
  receipts: (id, limit = 1) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/receipts`, { limit })),
186
+ receiptQuarterlyMetrics: (id, year, quarter) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/receipt-metrics/quarterly`, { year, quarter })),
186
187
  deliveryPatch: (id, taskId) => data_provider_1.request.get(`${base(id)}/delivery/${encodeURIComponent(taskId)}/patch`),
187
188
  deliveryBundle: (id, taskId) => data_provider_1.request.get(`${base(id)}/delivery/${encodeURIComponent(taskId)}/bundle`),
188
189
  operatingReview: (agentId) => data_provider_1.request.get(`/api/commitments/agents/${encodeURIComponent(agentId)}/operating-review`),
@@ -0,0 +1,3 @@
1
+ export declare function normalizedSnippetLength(value: number): number;
2
+ /** Keep search output a navigation result, never a transcript payload. */
3
+ export declare function compactMessageSearchHit(message: Record<string, unknown>, requestedSnippetLength: number, query?: string): Record<string, unknown>;
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizedSnippetLength = normalizedSnippetLength;
4
+ exports.compactMessageSearchHit = compactMessageSearchHit;
5
+ const MAX_SEARCH_SNIPPET_CHARS = 4000;
6
+ function normalizedSnippetLength(value) {
7
+ if (!Number.isFinite(value))
8
+ return 160;
9
+ return Math.min(MAX_SEARCH_SNIPPET_CHARS, Math.max(1, Math.trunc(value)));
10
+ }
11
+ function findQueryMatch(text, query) {
12
+ const normalizedQuery = query.trim().toLocaleLowerCase();
13
+ if (!normalizedQuery)
14
+ return null;
15
+ const loweredText = text.toLocaleLowerCase();
16
+ const exactAt = loweredText.indexOf(normalizedQuery);
17
+ if (exactAt >= 0)
18
+ return { at: exactAt, length: normalizedQuery.length };
19
+ // Meilisearch tokenizes punctuation-bearing queries and can legitimately
20
+ // match AGENT_EMPTY_RESPONSE as separate words. If the exact phrase is not
21
+ // present, center on the earliest substantive query token so the bounded CLI
22
+ // result still explains why it matched instead of truncating just before the
23
+ // highlighted word returned by the server crop.
24
+ const tokens = Array.from(new Set(normalizedQuery.split(/[^\p{L}\p{N}]+/u).filter((token) => token.length >= 2)));
25
+ let best = null;
26
+ for (const token of tokens) {
27
+ const at = loweredText.indexOf(token);
28
+ if (at >= 0 && (!best || at < best.at))
29
+ best = { at, length: token.length };
30
+ }
31
+ return best;
32
+ }
33
+ /** Keep search output a navigation result, never a transcript payload. */
34
+ function compactMessageSearchHit(message, requestedSnippetLength, query = '') {
35
+ const limit = normalizedSnippetLength(requestedSnippetLength);
36
+ const normalizedText = String(message.text ?? '')
37
+ .replace(/\s+/g, ' ')
38
+ .trim();
39
+ const normalizedQuery = query.trim();
40
+ let text = normalizedText;
41
+ if (!text) {
42
+ text = '[matching text unavailable]';
43
+ }
44
+ else if (text.length > limit) {
45
+ const match = findQueryMatch(text, normalizedQuery);
46
+ if (!match) {
47
+ text = text.slice(0, limit);
48
+ }
49
+ else {
50
+ const matchAt = match.at;
51
+ const matchLength = Math.min(match.length, limit);
52
+ const prefixBudget = Math.floor((limit - matchLength) / 2);
53
+ let start = Math.max(0, matchAt - prefixBudget);
54
+ let prefix = start > 0 ? '…' : '';
55
+ let suffix = '…';
56
+ let contentBudget = Math.max(1, limit - prefix.length - suffix.length);
57
+ start = Math.min(start, Math.max(0, text.length - contentBudget));
58
+ prefix = start > 0 ? '…' : '';
59
+ suffix = start + contentBudget < text.length ? '…' : '';
60
+ contentBudget = Math.max(1, limit - prefix.length - suffix.length);
61
+ start = Math.min(start, Math.max(0, text.length - contentBudget));
62
+ text = `${prefix}${text.slice(start, start + contentBudget)}${suffix}`;
63
+ }
64
+ }
65
+ text = text.slice(0, limit);
66
+ return {
67
+ messageId: message.messageId,
68
+ conversationId: message.conversationId,
69
+ title: message.title,
70
+ sender: message.sender,
71
+ isCreatedByUser: message.isCreatedByUser,
72
+ createdAt: message.createdAt,
73
+ text,
74
+ };
75
+ }
@@ -0,0 +1,66 @@
1
+ import { type HarnessLease } from './harnesses';
2
+ export type DedicatedLeaseAction = 'start' | 'stop' | 'restart' | 'snapshot' | 'health-check';
3
+ export declare const DEDICATED_LEASE_ACTIONS: DedicatedLeaseAction[];
4
+ export interface DedicatedRuntimeScope {
5
+ workspaceId?: string;
6
+ }
7
+ export interface CreateDedicatedRuntimeInput extends DedicatedRuntimeScope {
8
+ requestId: string;
9
+ displayName: string;
10
+ sizePreset: string;
11
+ regionClass?: string;
12
+ storageGb?: number;
13
+ retentionClass?: string;
14
+ backupProfile?: 'crash-consistent' | 'application-consistent';
15
+ monthlySpendingLimitCents: number;
16
+ repositoryRef?: string;
17
+ }
18
+ export interface DedicatedRuntimeResponse {
19
+ lease?: HarnessLease;
20
+ }
21
+ /**
22
+ * Flags in, request body out — and the point of it being a function is the
23
+ * OMISSION.
24
+ *
25
+ * A field the user did not set must be ABSENT from the body, not present as
26
+ * `undefined`, because the server reaches its own default through
27
+ * `input.x || <default>`. Restating those defaults on the client looks
28
+ * harmless and is not: `--storage-gb` shipped with a client default of 100
29
+ * against a product default of 50, so a user who never chose a disk size was
30
+ * billed for twice the documented one and `workspaceDefaultGb` was unreachable
31
+ * through the CLI. The three neighbouring flags happened to agree, which only
32
+ * means the same bug was invisible in them.
33
+ */
34
+ export declare function dedicatedRuntimeCreateInput(input: {
35
+ requestId: string;
36
+ displayName: string;
37
+ sizePreset: string;
38
+ monthlySpendingLimitCents: number;
39
+ regionClass?: string;
40
+ storageGb?: number;
41
+ retentionClass?: string;
42
+ backupProfile?: string;
43
+ workspaceId?: string;
44
+ }): CreateDedicatedRuntimeInput;
45
+ export declare function createDedicatedRuntime(input: CreateDedicatedRuntimeInput): Promise<DedicatedRuntimeResponse>;
46
+ export declare function listDedicatedRuntimes(options?: {
47
+ limit?: number;
48
+ state?: string;
49
+ }): Promise<{
50
+ leases?: HarnessLease[];
51
+ }>;
52
+ export declare function getDedicatedRuntime(leaseId: string): Promise<DedicatedRuntimeResponse>;
53
+ export declare function performDedicatedLeaseAction(leaseId: string, action: DedicatedLeaseAction): Promise<DedicatedRuntimeResponse>;
54
+ export declare function growDedicatedRuntime(leaseId: string, storageGb: number, options?: {
55
+ requestId?: string;
56
+ }): Promise<DedicatedRuntimeResponse>;
57
+ export declare function closeDedicatedRuntimeIncidentHold(leaseId: string, options?: {
58
+ requestId?: string;
59
+ }): Promise<DedicatedRuntimeResponse>;
60
+ export declare function destroyDedicatedRuntime(leaseId: string, options?: {
61
+ retentionIntent?: 'retain' | 'purge';
62
+ }): Promise<DedicatedRuntimeResponse>;
63
+ export declare function dedicatedLeaseId(lease: HarnessLease | undefined | null): string;
64
+ export declare function dedicatedLeaseState(lease: HarnessLease | undefined | null): string;
65
+ export declare function dedicatedLeaseDisplayName(lease: HarnessLease | undefined | null): string;
66
+ export declare function formatDedicatedRuntimeApiError(err: unknown): string | null;
@@ -0,0 +1,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEDICATED_LEASE_ACTIONS = void 0;
4
+ exports.dedicatedRuntimeCreateInput = dedicatedRuntimeCreateInput;
5
+ exports.createDedicatedRuntime = createDedicatedRuntime;
6
+ exports.listDedicatedRuntimes = listDedicatedRuntimes;
7
+ exports.getDedicatedRuntime = getDedicatedRuntime;
8
+ exports.performDedicatedLeaseAction = performDedicatedLeaseAction;
9
+ exports.growDedicatedRuntime = growDedicatedRuntime;
10
+ exports.closeDedicatedRuntimeIncidentHold = closeDedicatedRuntimeIncidentHold;
11
+ exports.destroyDedicatedRuntime = destroyDedicatedRuntime;
12
+ exports.dedicatedLeaseId = dedicatedLeaseId;
13
+ exports.dedicatedLeaseState = dedicatedLeaseState;
14
+ exports.dedicatedLeaseDisplayName = dedicatedLeaseDisplayName;
15
+ exports.formatDedicatedRuntimeApiError = formatDedicatedRuntimeApiError;
16
+ const api_fetch_1 = require("./api-fetch");
17
+ const harnesses_1 = require("./harnesses");
18
+ exports.DEDICATED_LEASE_ACTIONS = [
19
+ 'start',
20
+ 'stop',
21
+ 'restart',
22
+ 'snapshot',
23
+ 'health-check',
24
+ ];
25
+ /**
26
+ * Flags in, request body out — and the point of it being a function is the
27
+ * OMISSION.
28
+ *
29
+ * A field the user did not set must be ABSENT from the body, not present as
30
+ * `undefined`, because the server reaches its own default through
31
+ * `input.x || <default>`. Restating those defaults on the client looks
32
+ * harmless and is not: `--storage-gb` shipped with a client default of 100
33
+ * against a product default of 50, so a user who never chose a disk size was
34
+ * billed for twice the documented one and `workspaceDefaultGb` was unreachable
35
+ * through the CLI. The three neighbouring flags happened to agree, which only
36
+ * means the same bug was invisible in them.
37
+ */
38
+ function dedicatedRuntimeCreateInput(input) {
39
+ return {
40
+ requestId: input.requestId,
41
+ displayName: input.displayName,
42
+ sizePreset: input.sizePreset,
43
+ monthlySpendingLimitCents: input.monthlySpendingLimitCents,
44
+ ...(input.regionClass ? { regionClass: input.regionClass } : {}),
45
+ ...(input.storageGb !== undefined ? { storageGb: input.storageGb } : {}),
46
+ ...(input.retentionClass ? { retentionClass: input.retentionClass } : {}),
47
+ ...(input.backupProfile
48
+ ? { backupProfile: input.backupProfile }
49
+ : {}),
50
+ ...(input.workspaceId ? { workspaceId: input.workspaceId } : {}),
51
+ };
52
+ }
53
+ async function createDedicatedRuntime(input) {
54
+ const { requestId, workspaceId, ...body } = input;
55
+ if (workspaceId) {
56
+ return (0, api_fetch_1.apiFetch)(`/api/machines/dedicated?workspaceId=${encodeURIComponent(workspaceId)}`, {
57
+ method: 'POST',
58
+ body,
59
+ headers: { 'Idempotency-Key': requestId },
60
+ });
61
+ }
62
+ return (0, api_fetch_1.apiFetch)('/api/machines/dedicated', {
63
+ method: 'POST',
64
+ body,
65
+ headers: { 'Idempotency-Key': requestId },
66
+ });
67
+ }
68
+ async function listDedicatedRuntimes(options = {}) {
69
+ return (0, harnesses_1.listHarnessLeases)({
70
+ ...options,
71
+ machineProduct: 'dedicated-machine',
72
+ });
73
+ }
74
+ async function getDedicatedRuntime(leaseId) {
75
+ return (0, harnesses_1.getHarnessLease)(leaseId);
76
+ }
77
+ async function performDedicatedLeaseAction(leaseId, action) {
78
+ return (0, harnesses_1.performHarnessLeaseAction)(leaseId, action);
79
+ }
80
+ async function growDedicatedRuntime(leaseId, storageGb, options = {}) {
81
+ return (0, api_fetch_1.apiFetch)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/storage`, {
82
+ method: 'POST',
83
+ body: { storageGb },
84
+ ...(options.requestId ? { headers: { 'Idempotency-Key': options.requestId } } : {}),
85
+ });
86
+ }
87
+ async function closeDedicatedRuntimeIncidentHold(leaseId, options = {}) {
88
+ return (0, api_fetch_1.apiFetch)(`/api/machines/dedicated/${encodeURIComponent(leaseId)}/incident-hold/close`, {
89
+ method: 'POST',
90
+ body: {},
91
+ ...(options.requestId ? { headers: { 'Idempotency-Key': options.requestId } } : {}),
92
+ });
93
+ }
94
+ async function destroyDedicatedRuntime(leaseId, options = {}) {
95
+ const query = options.retentionIntent
96
+ ? `?retentionIntent=${encodeURIComponent(options.retentionIntent)}`
97
+ : '';
98
+ return (0, api_fetch_1.apiFetch)(`/api/machines/leases/${encodeURIComponent(leaseId)}${query}`, { method: 'DELETE' });
99
+ }
100
+ function dedicatedLeaseId(lease) {
101
+ return String(lease?.id ?? '');
102
+ }
103
+ function dedicatedLeaseState(lease) {
104
+ return String(lease?.state ?? 'unknown');
105
+ }
106
+ function dedicatedLeaseDisplayName(lease) {
107
+ return lease?.displayName ? String(lease.displayName) : '-';
108
+ }
109
+ /**
110
+ * Dedicated Runtime commands are unusually likely to meet a server that has
111
+ * never heard of them.
112
+ *
113
+ * The CLI ships independently of the API (`@skrr-ai/cli` on npm), so "CLI newer
114
+ * than the deployed server" is the normal state for anyone who runs
115
+ * `npm update`, and this feature widened that window: twelve commands arrived at
116
+ * once, and three of them call endpoints that land in a later deploy. The server
117
+ * redacts an unknown-route 404 to "An unexpected error occurred.", so every one
118
+ * of those users got eight words that cannot be told apart from a wrong lease
119
+ * id or a broken server.
120
+ *
121
+ * A 404 carrying no product `code` is that case. Naming it is the difference
122
+ * between "wait for the deploy" and "open a bug".
123
+ */
124
+ function describeMissingEndpoint(err) {
125
+ if (err.status !== 404) {
126
+ return null;
127
+ }
128
+ try {
129
+ const parsed = JSON.parse(err.body || '');
130
+ // A product code means the server DID handle the route and refused on the
131
+ // merits — that message is the useful one and must not be overwritten.
132
+ if (parsed.code) {
133
+ return null;
134
+ }
135
+ }
136
+ catch {
137
+ // Unparseable body: no product code by definition.
138
+ }
139
+ return ('This server does not have that Dedicated Runtime endpoint. Your CLI is probably ahead of ' +
140
+ 'the deployment — `skrr context` names the server it is talking to, and `skrr machines ' +
141
+ 'dedicated list` will still work if the feature is deployed at all.');
142
+ }
143
+ function formatDedicatedRuntimeApiError(err) {
144
+ if (!(err instanceof api_fetch_1.ApiFetchError) || !err.body) {
145
+ return null;
146
+ }
147
+ if (err.status === 401) {
148
+ return null;
149
+ }
150
+ const missingEndpoint = describeMissingEndpoint(err);
151
+ if (missingEndpoint) {
152
+ return missingEndpoint;
153
+ }
154
+ try {
155
+ const parsed = JSON.parse(err.body);
156
+ return parsed.message || parsed.error || parsed.code || null;
157
+ }
158
+ catch {
159
+ return err.body.trim() || null;
160
+ }
161
+ }
@@ -21,8 +21,29 @@ export interface BuildExecutionOrderPatchInput {
21
21
  selectors: string[];
22
22
  /** Reasons that permit each non-final target to advance. */
23
23
  advanceOn?: string[];
24
+ /**
25
+ * No category permits an advance — a STRICT chain.
26
+ *
27
+ * `advanceOn` omitted defaults to `['offline','unavailable']` on every
28
+ * non-final target (see below), and the enum has no empty member, so
29
+ * "strictly pinned, with a declared next target" was inexpressible from any
30
+ * flag: the only way to build it was to bypass the flags entirely and
31
+ * hand-write `executionPolicy` through `--from-json` (OSK-7669).
32
+ */
33
+ strict?: boolean;
24
34
  }
25
35
  export declare function normalizeExecutionTargetSelector(input: string): string;
26
36
  export declare function normalizeAdvanceReasons(values: string[] | undefined): ExecutionTargetAdvanceReason[];
27
37
  export declare function executionTargetForSelector(input: string): AgentExecutionTarget;
38
+ /**
39
+ * What a non-final target may advance on when the caller said nothing.
40
+ *
41
+ * Exported because it is a DEFAULT the operator is not told about anywhere:
42
+ * `--advance-on` reads as an explicit opt-in ("Failure category that PERMITS
43
+ * the current target to advance"), so omitting it looks like opting into
44
+ * nothing — and it grants two categories on a target the caller explicitly
45
+ * pinned (OSK-7669). Naming it here lets the help text and the confirmation
46
+ * output say the same thing the code does.
47
+ */
48
+ export declare const DEFAULT_ADVANCE_REASONS: readonly ExecutionTargetAdvanceReason[];
28
49
  export declare function buildExecutionOrderPatch(input: BuildExecutionOrderPatchInput): AgentExecutionPatch;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.EXECUTION_TARGET_ADVANCE_REASONS = void 0;
3
+ exports.DEFAULT_ADVANCE_REASONS = exports.EXECUTION_TARGET_ADVANCE_REASONS = void 0;
4
4
  exports.normalizeExecutionTargetSelector = normalizeExecutionTargetSelector;
5
5
  exports.normalizeAdvanceReasons = normalizeAdvanceReasons;
6
6
  exports.executionTargetForSelector = executionTargetForSelector;
@@ -78,6 +78,20 @@ function executionTargetForSelector(input) {
78
78
  managedUsageConsent: false,
79
79
  };
80
80
  }
81
+ /**
82
+ * What a non-final target may advance on when the caller said nothing.
83
+ *
84
+ * Exported because it is a DEFAULT the operator is not told about anywhere:
85
+ * `--advance-on` reads as an explicit opt-in ("Failure category that PERMITS
86
+ * the current target to advance"), so omitting it looks like opting into
87
+ * nothing — and it grants two categories on a target the caller explicitly
88
+ * pinned (OSK-7669). Naming it here lets the help text and the confirmation
89
+ * output say the same thing the code does.
90
+ */
91
+ exports.DEFAULT_ADVANCE_REASONS = [
92
+ 'offline',
93
+ 'unavailable',
94
+ ];
81
95
  function buildExecutionOrderPatch(input) {
82
96
  const values = input.selectors.map((value) => String(value || '').trim()).filter(Boolean);
83
97
  if (values.length === 0)
@@ -91,11 +105,13 @@ function buildExecutionOrderPatch(input) {
91
105
  mode: 'ordered',
92
106
  targets: targets.map((target, index) => ({
93
107
  ...target,
94
- advanceOn: index < targets.length - 1
95
- ? advanceOn.length > 0
96
- ? advanceOn
97
- : ['offline', 'unavailable']
98
- : [],
108
+ advanceOn: input.strict
109
+ ? []
110
+ : index < targets.length - 1
111
+ ? advanceOn.length > 0
112
+ ? advanceOn
113
+ : [...exports.DEFAULT_ADVANCE_REASONS]
114
+ : [],
99
115
  })),
100
116
  },
101
117
  };
@@ -51,15 +51,59 @@ export interface Harness {
51
51
  claudeEngine?: 'cli' | 'sdk';
52
52
  lastSeenAt?: string;
53
53
  }
54
+ /**
55
+ * One open lease, as `/api/machines/leases` actually returns it.
56
+ *
57
+ * This shape was previously guessed — it declared `productId`, `runtimeId` and
58
+ * `expiresAt`, none of which the server has ever sent. Optional properties made
59
+ * that compile, so `leases list` rendered three permanently empty columns while
60
+ * `--json` showed the data plainly, and the one view built to tell a
61
+ * Hosted Machine lease from a Hosted Job Machine lease could not.
62
+ *
63
+ * So: only fields `toPublicMachineLeasePayload` emits belong here. There is no
64
+ * expiry on a lease — it lives until released — and no top-level runtime id.
65
+ */
54
66
  export interface HarnessLease {
55
67
  id?: string;
56
- leaseId?: string;
57
- productId?: string;
68
+ machineProduct?: string;
69
+ machineProductLabel?: string;
70
+ displayName?: string;
58
71
  state?: string;
59
- runtimeId?: string;
60
- workspaceId?: string | null;
72
+ stateReason?: string;
73
+ lifecycleSemantics?: string;
74
+ /**
75
+ * `active | paused | storage | none` — whether this lease is still costing
76
+ * money — plus the monthly spend status the server already computes.
77
+ *
78
+ * These six were allow-listed in `PUBLIC_RUNTIME_BILLING_KEYS` and attached
79
+ * by `attachMonthlySpendStatus` on every lease read, and the web renders
80
+ * them. Nothing in the CLI read them, so the number that actually refuses a
81
+ * Hosted Machine — the MONTHLY cap — was unreadable from here while
82
+ * `skrr balance show` reported a DAILY cap with full headroom. A user who
83
+ * correctly suspected billing could not confirm it (OSK-7920).
84
+ */
85
+ billing?: {
86
+ billableState?: string;
87
+ monthlySpendLimitCents?: number | null;
88
+ currentMonthSpendCents?: number;
89
+ openUsageEstimateCents?: number;
90
+ projectedCurrentMonthSpendCents?: number;
91
+ spendingLimitExceeded?: boolean;
92
+ spendStatusUnavailable?: boolean;
93
+ monthStartedAt?: string | null;
94
+ } & Record<string, unknown>;
95
+ leaseTimestamps?: Record<string, string>;
96
+ health?: {
97
+ assignable?: boolean;
98
+ reasons?: string[];
99
+ } & Record<string, unknown>;
100
+ storage?: {
101
+ retentionClass?: string;
102
+ lastBackupAt?: string;
103
+ backupProfile?: string;
104
+ };
61
105
  createdAt?: string;
62
- expiresAt?: string;
106
+ updatedAt?: string;
63
107
  }
64
108
  /** `pause | resume | destroy | terminate | health-check` (MachineLeaseService). */
65
109
  export declare const HARNESS_LEASE_ACTIONS: string[];
@@ -80,6 +124,32 @@ export declare function getHarnessUsage(id: string): Promise<unknown>;
80
124
  /** The rollup across every harness. The web calls this key `fleetUsage`. */
81
125
  export declare function getAllHarnessUsage(): Promise<unknown>;
82
126
  export type { QuotaAccountSummary, QuotaHistory };
127
+ /** One hosted product's monthly runtime spend cap and where this month stands. */
128
+ export type RuntimeMonthlySpend = {
129
+ machineProduct: string;
130
+ hasLimit: boolean;
131
+ exceeded?: boolean;
132
+ limitCents?: number;
133
+ currentSpendCents?: number;
134
+ openUsageEstimateCents?: number;
135
+ projectedCents?: number;
136
+ monthStartedAt?: string;
137
+ };
138
+ /**
139
+ * The monthly runtime spend cap, read before it refuses anything.
140
+ *
141
+ * Distinct from the balance's own spend cap, which is a per-period RATE LIMIT
142
+ * on credits. A user can have headroom on that one and still be refused by
143
+ * this one, which is exactly what happened: every number on `balance show`
144
+ * said there was room while a monthly hosted-runtime cap turned every turn
145
+ * away (OSK-7920).
146
+ */
147
+ export declare function getRuntimeMonthlySpend(): Promise<{
148
+ scope: {
149
+ teamId: string | null;
150
+ };
151
+ spend: RuntimeMonthlySpend[];
152
+ }>;
83
153
  /** The CLI subscription accounts this user's daemons have reported readings for. */
84
154
  export declare function listQuotaAccounts(): Promise<{
85
155
  accounts: QuotaAccountSummary[];
@@ -123,7 +193,18 @@ export declare function readHarnessPing(id: string, pingId: string): Promise<{
123
193
  latencyMs?: number;
124
194
  error?: string;
125
195
  }>;
126
- export declare function listHarnessLeases(): Promise<{
196
+ /**
197
+ * List leases. The server answers newest-first and caps at 50 unless asked
198
+ * otherwise (`Math.min(Math.max(Number(limit) || 50, 1), 100)`), so an account
199
+ * with more than fifty leases could not see past the newest fifty from here —
200
+ * including `lost` ones, which are terminal, invisible, and still billed for
201
+ * the whole window the platform had lost track of them.
202
+ */
203
+ export declare function listHarnessLeases(options?: {
204
+ limit?: number;
205
+ state?: string;
206
+ machineProduct?: string;
207
+ }): Promise<{
127
208
  leases?: HarnessLease[];
128
209
  }>;
129
210
  export declare function getHarnessLease(leaseId: string): Promise<{