@skrr-ai/cli 0.1.22 → 0.1.23

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 (243) hide show
  1. package/README.md +83 -73
  2. package/bin/dev-fallback.js +221 -0
  3. package/bin/run.js +43 -0
  4. package/dist/base-command.d.ts +15 -0
  5. package/dist/base-command.js +71 -10
  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 +48 -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/list.d.ts +1 -0
  59. package/dist/commands/machines/list.js +33 -5
  60. package/dist/commands/memory/approvals/issue.d.ts +18 -0
  61. package/dist/commands/memory/approvals/issue.js +63 -0
  62. package/dist/commands/memory/commit.js +5 -2
  63. package/dist/commands/memory/context.d.ts +3 -2
  64. package/dist/commands/memory/context.js +9 -11
  65. package/dist/commands/memory/delete.d.ts +1 -1
  66. package/dist/commands/memory/list.d.ts +1 -1
  67. package/dist/commands/memory/list.js +1 -1
  68. package/dist/commands/memory/mounts/resolve.d.ts +10 -0
  69. package/dist/commands/memory/mounts/resolve.js +27 -0
  70. package/dist/commands/memory/proposals/commit.d.ts +18 -0
  71. package/dist/commands/memory/proposals/commit.js +32 -0
  72. package/dist/commands/memory/proposals/create.d.ts +18 -0
  73. package/dist/commands/memory/proposals/create.js +56 -0
  74. package/dist/commands/memory/proposals/list.d.ts +13 -0
  75. package/dist/commands/memory/proposals/list.js +31 -0
  76. package/dist/commands/memory/proposals/promote.d.ts +22 -0
  77. package/dist/commands/memory/proposals/promote.js +33 -0
  78. package/dist/commands/memory/proposals/reject.d.ts +16 -0
  79. package/dist/commands/memory/proposals/reject.js +28 -0
  80. package/dist/commands/memory/rebuild-index.d.ts +20 -0
  81. package/dist/commands/memory/rebuild-index.js +25 -0
  82. package/dist/commands/memory/save.d.ts +1 -1
  83. package/dist/commands/memory/save.js +1 -1
  84. package/dist/commands/memory/search.d.ts +6 -3
  85. package/dist/commands/memory/search.js +27 -3
  86. package/dist/commands/memory/show.d.ts +1 -1
  87. package/dist/commands/memory/stores/archive.d.ts +14 -0
  88. package/dist/commands/memory/stores/archive.js +31 -0
  89. package/dist/commands/memory/stores/create.d.ts +12 -0
  90. package/dist/commands/memory/stores/create.js +30 -0
  91. package/dist/commands/memory/stores/delete.d.ts +15 -0
  92. package/dist/commands/memory/stores/delete.js +38 -0
  93. package/dist/commands/memory/stores/list.d.ts +10 -0
  94. package/dist/commands/memory/stores/list.js +29 -0
  95. package/dist/commands/memory/stores/show.d.ts +12 -0
  96. package/dist/commands/memory/stores/show.js +14 -0
  97. package/dist/commands/memory/stores/update.d.ts +15 -0
  98. package/dist/commands/memory/stores/update.js +39 -0
  99. package/dist/commands/memory/sync/delete.d.ts +13 -0
  100. package/dist/commands/memory/sync/delete.js +35 -0
  101. package/dist/commands/memory/sync/pull.d.ts +19 -0
  102. package/dist/commands/memory/sync/pull.js +52 -0
  103. package/dist/commands/memory/sync/push.d.ts +18 -0
  104. package/dist/commands/memory/sync/push.js +115 -0
  105. package/dist/commands/memory/sync/resolve.d.ts +23 -0
  106. package/dist/commands/memory/sync/resolve.js +27 -0
  107. package/dist/commands/memory/sync/status.d.ts +9 -0
  108. package/dist/commands/memory/sync/status.js +22 -0
  109. package/dist/commands/memory/topics/archive.d.ts +20 -0
  110. package/dist/commands/memory/topics/archive.js +29 -0
  111. package/dist/commands/memory/topics/create.d.ts +23 -0
  112. package/dist/commands/memory/topics/create.js +46 -0
  113. package/dist/commands/memory/topics/delete.d.ts +22 -0
  114. package/dist/commands/memory/topics/delete.js +30 -0
  115. package/dist/commands/memory/topics/forget.d.ts +22 -0
  116. package/dist/commands/memory/topics/forget.js +29 -0
  117. package/dist/commands/memory/topics/list.d.ts +13 -0
  118. package/dist/commands/memory/topics/list.js +22 -0
  119. package/dist/commands/memory/topics/move.d.ts +22 -0
  120. package/dist/commands/memory/topics/move.js +26 -0
  121. package/dist/commands/memory/topics/show.d.ts +17 -0
  122. package/dist/commands/memory/topics/show.js +21 -0
  123. package/dist/commands/memory/topics/update.d.ts +23 -0
  124. package/dist/commands/memory/topics/update.js +27 -0
  125. package/dist/commands/memory/tree.d.ts +5 -2
  126. package/dist/commands/memory/tree.js +20 -2
  127. package/dist/commands/memory/update.d.ts +1 -1
  128. package/dist/commands/memory/versions/list.d.ts +16 -0
  129. package/dist/commands/memory/versions/list.js +22 -0
  130. package/dist/commands/memory/versions/redact.d.ts +21 -0
  131. package/dist/commands/memory/versions/redact.js +22 -0
  132. package/dist/commands/memory/versions/restore.d.ts +20 -0
  133. package/dist/commands/memory/versions/restore.js +16 -0
  134. package/dist/commands/memory/versions/show.d.ts +15 -0
  135. package/dist/commands/memory/versions/show.js +17 -0
  136. package/dist/commands/messages/list.js +25 -8
  137. package/dist/commands/messages/show.js +10 -0
  138. package/dist/commands/store/install.js +5 -1
  139. package/dist/commands/store/releases.js +1 -1
  140. package/dist/commands/store/update.js +5 -1
  141. package/dist/commands/subscriptions/cancel.js +5 -1
  142. package/dist/commands/subscriptions/health.js +5 -1
  143. package/dist/commands/subscriptions/status.js +5 -1
  144. package/dist/commands/subscriptions/subscribe.js +5 -1
  145. package/dist/commands/tasks/activity.d.ts +2 -3
  146. package/dist/commands/tasks/activity.js +15 -5
  147. package/dist/commands/tasks/complete.d.ts +18 -150
  148. package/dist/commands/tasks/complete.js +161 -600
  149. package/dist/commands/tasks/create.d.ts +5 -0
  150. package/dist/commands/tasks/create.js +107 -4
  151. package/dist/commands/tasks/deliverable/add.d.ts +8 -0
  152. package/dist/commands/tasks/deliverable/add.js +41 -8
  153. package/dist/commands/tasks/deliverable/confirm.d.ts +5 -5
  154. package/dist/commands/tasks/deliverable/confirm.js +9 -18
  155. package/dist/commands/tasks/deliverable/list.d.ts +12 -0
  156. package/dist/commands/tasks/deliverable/list.js +37 -0
  157. package/dist/commands/tasks/events/append.d.ts +2 -0
  158. package/dist/commands/tasks/events/append.js +11 -4
  159. package/dist/commands/tasks/events/list.d.ts +1 -0
  160. package/dist/commands/tasks/events/list.js +1 -0
  161. package/dist/commands/tasks/events/tail.d.ts +1 -0
  162. package/dist/commands/tasks/events/tail.js +1 -0
  163. package/dist/commands/tasks/expectations/assess.d.ts +21 -0
  164. package/dist/commands/tasks/expectations/assess.js +77 -0
  165. package/dist/commands/tasks/expectations.js +14 -18
  166. package/dist/commands/tasks/judge.d.ts +1 -0
  167. package/dist/commands/tasks/judge.js +2 -1
  168. package/dist/commands/tasks/output.d.ts +2 -11
  169. package/dist/commands/tasks/output.js +64 -145
  170. package/dist/commands/tasks/report.d.ts +3 -3
  171. package/dist/commands/tasks/report.js +64 -41
  172. package/dist/commands/tasks/resource/add.d.ts +23 -0
  173. package/dist/commands/tasks/resource/add.js +76 -0
  174. package/dist/commands/tasks/resource/list.d.ts +13 -0
  175. package/dist/commands/tasks/resource/list.js +41 -0
  176. package/dist/commands/tasks/result/show.d.ts +13 -0
  177. package/dist/commands/tasks/result/show.js +46 -0
  178. package/dist/commands/tasks/result/submit.d.ts +26 -0
  179. package/dist/commands/tasks/result/submit.js +134 -0
  180. package/dist/commands/tasks/resume/save.d.ts +16 -0
  181. package/dist/commands/tasks/resume/save.js +59 -0
  182. package/dist/commands/tasks/resume/show.d.ts +13 -0
  183. package/dist/commands/tasks/resume/show.js +35 -0
  184. package/dist/commands/tasks/review.d.ts +17 -0
  185. package/dist/commands/tasks/review.js +54 -0
  186. package/dist/commands/tasks/runs.js +5 -0
  187. package/dist/commands/tasks/timeline.d.ts +2 -2
  188. package/dist/commands/tasks/timeline.js +10 -23
  189. package/dist/commands/tasks/updates/add.d.ts +22 -0
  190. package/dist/commands/tasks/updates/add.js +101 -0
  191. package/dist/commands/tasks/updates/list.d.ts +15 -0
  192. package/dist/commands/tasks/updates/list.js +57 -0
  193. package/dist/commands/tasks/waive.d.ts +1 -0
  194. package/dist/commands/tasks/waive.js +2 -1
  195. package/dist/lib/agentic-stream.d.ts +257 -0
  196. package/dist/lib/agentic-stream.js +490 -33
  197. package/dist/lib/balance.d.ts +8 -0
  198. package/dist/lib/commitment-product.d.ts +10 -1
  199. package/dist/lib/commitment-product.js +66 -2
  200. package/dist/lib/commitments.d.ts +2 -1
  201. package/dist/lib/commitments.js +1 -0
  202. package/dist/lib/conversation-search.d.ts +3 -0
  203. package/dist/lib/conversation-search.js +75 -0
  204. package/dist/lib/execution-target.d.ts +21 -0
  205. package/dist/lib/execution-target.js +22 -6
  206. package/dist/lib/harnesses.d.ts +82 -6
  207. package/dist/lib/harnesses.js +30 -2
  208. package/dist/lib/hosted-machines.js +9 -0
  209. package/dist/lib/inbox.d.ts +2 -1
  210. package/dist/lib/machines.d.ts +17 -0
  211. package/dist/lib/machines.js +29 -0
  212. package/dist/lib/managed-agent-keys.d.ts +104 -0
  213. package/dist/lib/managed-agent-keys.js +50 -0
  214. package/dist/lib/memory-scope.d.ts +4 -3
  215. package/dist/lib/memory-scope.js +9 -9
  216. package/dist/lib/memory-sync.d.ts +114 -0
  217. package/dist/lib/memory-sync.js +730 -0
  218. package/dist/lib/message-provenance.d.ts +42 -0
  219. package/dist/lib/message-provenance.js +89 -0
  220. package/dist/lib/scoped-memory-command.d.ts +68 -0
  221. package/dist/lib/scoped-memory-command.js +183 -0
  222. package/dist/lib/session-task-endpoints.d.ts +1 -1
  223. package/dist/lib/session-task-endpoints.js +7 -0
  224. package/dist/lib/sky-code-broker.d.ts +47 -0
  225. package/dist/lib/sky-code-broker.js +50 -0
  226. package/dist/lib/sky-code-doctor.js +10 -1
  227. package/dist/lib/task-artifact-flags.d.ts +2 -0
  228. package/dist/lib/task-artifact-flags.js +7 -0
  229. package/dist/lib/task-execution.d.ts +4 -5
  230. package/dist/lib/task-execution.js +4 -5
  231. package/dist/lib/task-extras.d.ts +1 -5
  232. package/dist/lib/task-extras.js +1 -5
  233. package/dist/lib/task-transcript.d.ts +5 -7
  234. package/dist/lib/task-transcript.js +8 -16
  235. package/dist/lib/tasks.d.ts +7 -21
  236. package/dist/lib/tasks.js +17 -20
  237. package/dist/lib/triggers.d.ts +39 -1
  238. package/dist/lib/triggers.js +70 -2
  239. package/dist/lib/usage-discovery.d.ts +17 -1
  240. package/dist/lib/usage-discovery.js +17 -1
  241. package/dist/node_modules/@skrr-ai/data-provider/index.js +3733 -3618
  242. package/oclif.manifest.json +16201 -10377
  243. package/package.json +26 -5
@@ -10,6 +10,7 @@ exports.describeAssigneeNotInSpace = describeAssigneeNotInSpace;
10
10
  exports.describeForbidden = describeForbidden;
11
11
  exports.isDaemonScopedCredential = isDaemonScopedCredential;
12
12
  exports.describeDaemonScopedCredential = describeDaemonScopedCredential;
13
+ exports.isTypedServerRefusal = isTypedServerRefusal;
13
14
  exports.isRetryableFailure = isRetryableFailure;
14
15
  const core_1 = require("@oclif/core");
15
16
  Object.defineProperty(exports, "Flags", { enumerable: true, get: function () { return core_1.Flags; } });
@@ -936,6 +937,29 @@ class BaseCommand extends core_1.Command {
936
937
  retryable: true,
937
938
  });
938
939
  }
940
+ else if (isTypedServerRefusal(body)) {
941
+ // A 5xx the server DELIBERATELY produced and described in its own
942
+ // words. Reaching the outage branch below made the CLI announce a
943
+ // platform outage for a per-conversation condition: "https://skrr.ai is
944
+ // not serving requests right now. This is a service outage, not a
945
+ // problem with your account or this command" — for
946
+ // `CONVERSATION_ADMISSION_OUTCOME_UNCONFIRMED`, while skrr.ai served
947
+ // every other command in the same minute, with the outer `retryable`
948
+ // reading true over an inner `false` (retrying was futile for ~35
949
+ // minutes). The reader is sent to a status page instead of learning
950
+ // that one conversation is parked (OSK-7719).
951
+ //
952
+ // So: the server's code, the server's words, and the server's verdict
953
+ // on whether retrying can help.
954
+ this.failWithCliError({
955
+ message: say(bodyMessage || e.message),
956
+ code: bodyCode || `HTTP_${e.status}`,
957
+ status: e.status,
958
+ exit: 1,
959
+ retryable: isRetryableFailure(e.status, body),
960
+ details: body ?? undefined,
961
+ });
962
+ }
939
963
  else if ((0, outbox_1.isOutageFailure)(e)) {
940
964
  // A gateway/unavailable status is an OUTAGE, and saying so is the whole
941
965
  // point. During the OSK-2806 incident these surfaced as bare
@@ -955,11 +979,17 @@ class BaseCommand extends core_1.Command {
955
979
  // predicate is what made a single route's 500 read as a platform
956
980
  // outage.
957
981
  this.noteControlPlaneOutage(e.status);
982
+ const method = e.method?.toUpperCase();
983
+ const readOnlyRequest = method === 'GET' || method === 'HEAD' || method === 'OPTIONS';
984
+ const outcome = readOnlyRequest
985
+ ? 'No write was attempted by this request; retry once the service recovers.'
986
+ : 'For a write, the gateway may have timed out after the server committed it. ' +
987
+ 'The outcome is unknown — check the target or reconcile by idempotency key before retrying.';
958
988
  this.failWithCliError({
959
989
  message: say(`${this.cliConfig.baseURL} is not serving requests right now` +
960
990
  `${e.status ? ` (HTTP ${e.status})` : ''}. ` +
961
991
  'This is a service outage, not a problem with your account or this command. ' +
962
- 'Nothing was changed — retry once it recovers.'),
992
+ outcome),
963
993
  code: 'CONTROL_PLANE_UNAVAILABLE',
964
994
  status: e.status,
965
995
  exit: 5,
@@ -1093,15 +1123,16 @@ class BaseCommand extends core_1.Command {
1093
1123
  });
1094
1124
  }
1095
1125
  // Same discipline as `handleApiError`: only a real outage gets outage
1096
- // wording, and only an outage lets us say nothing was written. A 500 is
1097
- // queued too, but the server ran our request before failing, so what it did
1098
- // with it is unknown and a replay is safe on the idempotency key rather
1099
- // than on an assumption that nothing landed.
1126
+ // wording. Even an edge 504 can arrive AFTER the application committed a
1127
+ // write, so neither branch claims nothing landed. A replay is safe because
1128
+ // the queued record carries an idempotency key, not because the first
1129
+ // attempt is assumed absent.
1100
1130
  const outage = (0, outbox_1.isOutageFailure)(err);
1101
1131
  const statusSuffix = e.status ? ` (HTTP ${e.status})` : '';
1102
1132
  const lead = outage
1103
1133
  ? `${this.cliConfig.baseURL} is not serving requests right now${statusSuffix}. ` +
1104
- `Your ${write.summary} was queued locally as ${queuedId} nothing has been written yet.`
1134
+ `Your ${write.summary} was queued locally as ${queuedId}; whether the server already ` +
1135
+ 'applied the timed-out attempt is unknown, so verify before replaying.'
1105
1136
  : `${this.cliConfig.baseURL} did not accept this write${statusSuffix}. ` +
1106
1137
  `Your ${write.summary} was queued locally as ${queuedId}; whether the server applied ` +
1107
1138
  'any of it is not reported, so verify before replaying.';
@@ -1341,15 +1372,23 @@ function describeAssigneeNotInSpace(serverMessage, bin = 'skrr') {
1341
1372
  * Server-declared TERMINAL refusals: a state conflict the caller must resolve
1342
1373
  * by doing something DIFFERENT, never by repeating the identical request.
1343
1374
  *
1344
- * `skrr tasks move --status done` on a checked-out task with no approved
1345
- * TASK_OUTPUT returns 409 `task_output_required`. 409 used to be in the
1375
+ * `skrr tasks move --status done` with no current Result or independent Review
1376
+ * returns a typed 409. 409 used to be in the
1346
1377
  * transient set below, so the JSON envelope said `retryable: true` — and
1347
1378
  * `daemon/CLAUDE.md` tells daemon-hosted agents to retry retryable errors, so
1348
1379
  * they looped on a refusal that can never succeed on retry. The refusal names
1349
- * the supported path (`POST /:taskId/complete` / `skrr tasks complete`); the
1380
+ * the supported path (`skrr tasks complete`, Result, then Review); the
1350
1381
  * envelope has to agree that the ONLY way forward is that path.
1351
1382
  */
1352
- const TERMINAL_ERROR_CODES = new Set(['task_output_required', 'task_output_not_approved']);
1383
+ const TERMINAL_ERROR_CODES = new Set([
1384
+ 'TASK_RESULT_REQUIRED',
1385
+ 'TASK_CURRENT_RESULT_MISSING',
1386
+ 'TASK_RESULT_SPEC_VERSION_MISMATCH',
1387
+ 'TASK_EXPECTATIONS_NOT_MET',
1388
+ 'TASK_RESULT_CHANGES_REQUESTED',
1389
+ 'TASK_RESULT_REVIEW_REQUIRED',
1390
+ 'TASK_DELIVERY_NOT_INTEGRATED',
1391
+ ]);
1353
1392
  /**
1354
1393
  * Whether an automated retry of the IDENTICAL request could plausibly succeed.
1355
1394
  *
@@ -1451,6 +1490,28 @@ function describeDaemonScopedCredential(bin, scope = 'daemon', source) {
1451
1490
  return (`${subject}, which cannot access user API commands. ` +
1452
1491
  `Run \`${bin} login\` to create a CLI credential. For the local runtime itself, use \`${bin} daemon login\`.`);
1453
1492
  }
1493
+ /**
1494
+ * Did the server MEAN this 5xx, or did it fall over?
1495
+ *
1496
+ * An outage and a designed refusal arrive with the same status and want
1497
+ * opposite reactions — wait for recovery, versus act on a condition. The
1498
+ * discriminator is whether the server authored an answer: a typed `code` plus
1499
+ * either an explanation it wrote (`cause` / `remedy`, both of which the 5xx
1500
+ * redaction boundary deliberately lets through) or an explicit verdict that
1501
+ * retrying will not help.
1502
+ *
1503
+ * Deliberately NOT folded into `isOutageFailure`: that predicate also gates the
1504
+ * outbox queue decision and `noteControlPlaneOutage`, and those ask a different
1505
+ * question. Merging the two is the mistake its own comment warns about.
1506
+ */
1507
+ function isTypedServerRefusal(body) {
1508
+ if (!body || typeof body !== 'object')
1509
+ return false;
1510
+ const code = stringProp(body, 'code') || stringProp(body, 'errorType');
1511
+ if (!code)
1512
+ return false;
1513
+ return (typeof body.cause === 'string' || typeof body.remedy === 'string' || body.retryable === false);
1514
+ }
1454
1515
  function isRetryableFailure(status, body) {
1455
1516
  const declared = body?.retryable;
1456
1517
  if (typeof declared === 'boolean')
@@ -1,15 +1,52 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * Sort two positionals into (harness, agent), whichever order they were typed.
4
+ *
5
+ * The discriminator is the id SHAPE, not the position: an agent id is
6
+ * `agent_…`, and nothing else this command accepts is. That covers the reported
7
+ * mistake exactly — `attach-harness <agent> <harness>` — without guessing when
8
+ * neither value looks like an agent.
9
+ *
10
+ * Returns `{ error }` rather than picking, when the two sources disagree.
11
+ */
12
+ export declare function resolveAttachPositionals(first: string, second: string | undefined, agentFlag: string | undefined): {
13
+ harness: string;
14
+ agent?: string;
15
+ error?: undefined;
16
+ } | {
17
+ error: string;
18
+ harness?: undefined;
19
+ };
2
20
  export default class AgentsAttachHarness extends BaseCommand {
3
21
  static description: string;
4
22
  static examples: string[];
23
+ /**
24
+ * Which positional is which, when a caller gave two.
25
+ *
26
+ * This is the ONLY command in the `agents` topic whose positional is not the
27
+ * agent — every neighbour is `agents <verb> <agent-id> [args]`, and the
28
+ * space's own Execution Map documents `attach-harness <agent> <harness>`. A
29
+ * caller writing that shape had their agent id silently accepted AS THE
30
+ * HARNESS and the real harness rejected with `Unexpected argument:
31
+ * 6a985ed2…` — an error that names the argument it does not want, never says
32
+ * what it does want, and does not notice that the value it accepted is
33
+ * obviously an `agent_…` id (OSK-7686).
34
+ *
35
+ * So both orders work, decided by the shape of the ids rather than by
36
+ * position. An explicit `--agent` still wins, and a conflict is refused
37
+ * rather than silently resolved — being attached to the wrong machine is the
38
+ * wrong way to find out which one was meant.
39
+ */
5
40
  static args: {
6
41
  harness: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
42
+ agent: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
43
  };
8
44
  static flags: {
9
45
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
46
  agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
47
  'next-target': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
48
  'advance-on': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
49
+ 'strict-order': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
50
  'rebind-tasks': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
14
51
  };
15
52
  run(): Promise<void>;
@@ -1,10 +1,50 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAttachPositionals = resolveAttachPositionals;
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../base-command");
6
7
  const execution_target_1 = require("../../lib/execution-target");
7
8
  const agent_resolver_1 = require("../../lib/agent-resolver");
9
+ /**
10
+ * Sort two positionals into (harness, agent), whichever order they were typed.
11
+ *
12
+ * The discriminator is the id SHAPE, not the position: an agent id is
13
+ * `agent_…`, and nothing else this command accepts is. That covers the reported
14
+ * mistake exactly — `attach-harness <agent> <harness>` — without guessing when
15
+ * neither value looks like an agent.
16
+ *
17
+ * Returns `{ error }` rather than picking, when the two sources disagree.
18
+ */
19
+ function resolveAttachPositionals(first, second, agentFlag) {
20
+ const isAgentId = (value) => !!value && value.startsWith('agent_');
21
+ let harness = first;
22
+ let agent = second;
23
+ if (second && isAgentId(first) && !isAgentId(second)) {
24
+ harness = second;
25
+ agent = first;
26
+ }
27
+ else if (second && isAgentId(first) && isAgentId(second)) {
28
+ return {
29
+ error: `Both positionals look like agent ids ("${first}", "${second}"). ` +
30
+ 'Pass one agent and one harness: `agents attach-harness <harness> --agent <agent>`.',
31
+ };
32
+ }
33
+ if (agent && agentFlag && agent !== agentFlag) {
34
+ return {
35
+ error: `Conflicting agents: "${agent}" (positional) and "${agentFlag}" (--agent). Pass one. ` +
36
+ 'Attaching to the wrong machine is the wrong way to find out which was meant.',
37
+ };
38
+ }
39
+ if (isAgentId(harness)) {
40
+ return {
41
+ error: `"${harness}" is an agent id, not a harness. This command takes the harness first: ` +
42
+ '`agents attach-harness <harness> --agent <agent>`. ' +
43
+ 'List harnesses with `skrr harnesses list --all`.',
44
+ };
45
+ }
46
+ return { harness, ...(agent ? { agent } : {}) };
47
+ }
8
48
  class AgentsAttachHarness extends base_command_1.BaseCommand {
9
49
  static description = 'Attach a harness to an agent';
10
50
  static examples = [
@@ -18,6 +58,23 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
18
58
  // local-daemon user — a dead end at the exact moment the id is needed.
19
59
  // `harnesses list` is the command that surfaces daemon-registered harness
20
60
  // _ids (see cli/src/commands/harnesses/list.ts).
61
+ /**
62
+ * Which positional is which, when a caller gave two.
63
+ *
64
+ * This is the ONLY command in the `agents` topic whose positional is not the
65
+ * agent — every neighbour is `agents <verb> <agent-id> [args]`, and the
66
+ * space's own Execution Map documents `attach-harness <agent> <harness>`. A
67
+ * caller writing that shape had their agent id silently accepted AS THE
68
+ * HARNESS and the real harness rejected with `Unexpected argument:
69
+ * 6a985ed2…` — an error that names the argument it does not want, never says
70
+ * what it does want, and does not notice that the value it accepted is
71
+ * obviously an `agent_…` id (OSK-7686).
72
+ *
73
+ * So both orders work, decided by the shape of the ids rather than by
74
+ * position. An explicit `--agent` still wins, and a conflict is refused
75
+ * rather than silently resolved — being attached to the wrong machine is the
76
+ * wrong way to find out which one was meant.
77
+ */
21
78
  static args = {
22
79
  harness: core_1.Args.string({
23
80
  description: 'Harness id. Use sbs_... for a Hosted Machine session, hosted-machine for the default Hosted Machine product, agent-worker for the cloud worker, or a harness id. ' +
@@ -25,6 +82,12 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
25
82
  required: true,
26
83
  ignoreStdin: true,
27
84
  }),
85
+ agent: core_1.Args.string({
86
+ description: 'Optional second positional, so `agents attach-harness <agent> <harness>` — the shape ' +
87
+ 'every other agents command uses — works. Equivalent to --agent.',
88
+ required: false,
89
+ ignoreStdin: true,
90
+ }),
28
91
  };
29
92
  static flags = {
30
93
  json: core_1.Flags.boolean({ description: 'Output the attachment result as JSON' }),
@@ -36,10 +99,23 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
36
99
  multiple: true,
37
100
  }),
38
101
  'advance-on': core_1.Flags.string({
39
- description: 'Failure category that permits the current target to advance to the next target.',
102
+ description: 'Failure category that permits EACH non-final target to advance to the next one. ' +
103
+ // The flag read as an explicit opt-in, so omitting it looked like
104
+ // opting into nothing — and the server was writing
105
+ // `["offline","unavailable"]` on a target the caller had explicitly
106
+ // pinned, with nothing in --help or in either command's output saying
107
+ // so (OSK-7669).
108
+ 'Defaults to `offline unavailable` when omitted and a next target exists; ' +
109
+ 'pass --strict-order for a chain no failure may advance. ' +
110
+ 'Applies to every non-final target, not only the one named here.',
40
111
  multiple: true,
41
112
  options: [...execution_target_1.EXECUTION_TARGET_ADVANCE_REASONS],
42
113
  }),
114
+ 'strict-order': core_1.Flags.boolean({
115
+ description: 'No failure category permits an advance. The declared next targets stay recorded but ' +
116
+ 'the chain never moves — previously only expressible by hand-writing executionPolicy ' +
117
+ 'through --from-json.',
118
+ }),
43
119
  'rebind-tasks': core_1.Flags.boolean({
44
120
  description: 'Publish the updated agent release and migrate dormant assigned task pins to it. Running, review, and completed tasks are never changed.',
45
121
  }),
@@ -47,9 +123,20 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
47
123
  async run() {
48
124
  this.requireAuth();
49
125
  const { args, flags } = await this.parse(AgentsAttachHarness);
126
+ const positional = resolveAttachPositionals(args.harness, args.agent, flags.agent);
127
+ if (positional.error) {
128
+ this.failWithCliError({
129
+ message: positional.error,
130
+ code: 'ATTACH_HARNESS_ARGUMENTS',
131
+ exit: 2,
132
+ retryable: false,
133
+ });
134
+ }
135
+ const harnessArg = positional.harness;
136
+ const agentArg = positional.agent;
50
137
  let agentIdentity;
51
138
  try {
52
- agentIdentity = await (0, agent_resolver_1.resolveAgentIdentity)(flags.agent);
139
+ agentIdentity = await (0, agent_resolver_1.resolveAgentIdentity)(flags.agent || agentArg);
53
140
  }
54
141
  catch (err) {
55
142
  const failure = (0, agent_resolver_1.agentResolutionFailure)(err);
@@ -66,8 +153,9 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
66
153
  let patch;
67
154
  try {
68
155
  patch = (0, execution_target_1.buildExecutionOrderPatch)({
69
- selectors: [args.harness, ...(flags['next-target'] || [])],
156
+ selectors: [harnessArg, ...(flags['next-target'] || [])],
70
157
  advanceOn: flags['advance-on'],
158
+ strict: flags['strict-order'],
71
159
  });
72
160
  }
73
161
  catch (err) {
@@ -110,6 +198,17 @@ class AgentsAttachHarness extends base_command_1.BaseCommand {
110
198
  this.log(`Agent resolved from ${agentIdentity.source}.`);
111
199
  }
112
200
  this.log(`Execution order: ${targets.map(executionTargetSelector).join(' → ')}`);
201
+ // Print the policy that was actually written. A default the operator did
202
+ // not ask for and cannot see is one they will not know they have — the
203
+ // only way to find it was to diff the returned JSON against what was typed
204
+ // (OSK-7669).
205
+ for (const [index, target] of targets.entries()) {
206
+ const advance = Array.isArray(target.advanceOn) ? target.advanceOn : [];
207
+ this.log(` ${index + 1}. ${executionTargetSelector(target)} — ` +
208
+ (advance.length > 0
209
+ ? `may advance on: ${advance.join(', ')}`
210
+ : 'strict: no failure permits an advance'));
211
+ }
113
212
  const migration = updated?.harnessAttachment;
114
213
  if (migration) {
115
214
  this.log(`Published release ${migration.releaseId || 'unknown'} and rebound ` +
@@ -1,5 +1,73 @@
1
1
  import { BaseCommand } from '../../base-command';
2
- import { type AgenticStreamResult } from '../../lib/agentic-stream';
2
+ import { type AgenticStreamResult, type ReconciledTerminal } from '../../lib/agentic-stream';
3
+ /**
4
+ * Exit code for a run the USER stopped watching, by pressing Ctrl-C.
5
+ *
6
+ * OSK-2944 established that exiting 1 on a lost stream made "the run failed"
7
+ * and "we lost the view of a live run" indistinguishable, and invited duplicate
8
+ * execution. So a detach gets its own non-zero code.
9
+ *
10
+ * It must be non-zero on EVERY output mode. The `--jsonl` branch used to
11
+ * `return` here, which leaves the exit code at 0, so a script driving the CLI
12
+ * read a detached — possibly already-failed — run as a success. That is the
13
+ * mode automation actually uses.
14
+ */
15
+ export declare const DETACH_EXIT_CODE = 130;
16
+ /**
17
+ * Exit code for a detach nobody asked for — the stream was lost, or could not
18
+ * be attached at all.
19
+ *
20
+ * 130 is the conventional "killed by SIGINT" code, and it was returned for a
21
+ * detach that no signal caused: a script reading it concludes the user pressed
22
+ * Ctrl-C, which is the opposite of what happened (OSK-7660). Reserving 130 for
23
+ * an actual interrupt and giving the unattended case its own code keeps both
24
+ * answers true.
25
+ */
26
+ export declare const STREAM_LOST_EXIT_CODE = 75;
27
+ /**
28
+ * What to say when the user pressed Ctrl-C and the run did not stop.
29
+ *
30
+ * A cancel is REQUESTED over the session; whether the harness honours it is not
31
+ * something this process can guarantee. Measured on a local harness, where the
32
+ * answer is checkable with `ps`: the CLI exited, and the shell and the `sleep
33
+ * 200` it had spawned were still alive at +10s, +26s and +53s — while the CLI
34
+ * said nothing about it at all (OSK-7733). Three things compound there: work
35
+ * the user asked to stop keeps consuming their machine and their vendor tokens;
36
+ * they are not told, so they will not go and kill it; and the record is left
37
+ * `unfinished: true` with no error, so the cancellation is invisible in history
38
+ * too.
39
+ *
40
+ * Silence is the one unacceptable option, because the user will believe they
41
+ * cancelled. This says what was asked, what is not guaranteed, and how to look.
42
+ */
43
+ export declare function interruptedDetachMessage(input: {
44
+ sessionId: string;
45
+ conversationId?: string | null;
46
+ }): string;
47
+ /**
48
+ * What the durable record says about a turn, read from its messages.
49
+ *
50
+ * The stream is not the only witness and must not be the only one consulted: a
51
+ * run refused during admission writes `error: true` plus the reason on the
52
+ * response message and never closes the session stream, so a joined subscriber
53
+ * waits forever for an event that will not come (OSK-7624, OSK-7635, OSK-7720,
54
+ * OSK-7735). Exported as a pure function so the mapping from a message row to a
55
+ * terminal verdict is testable without a socket.
56
+ *
57
+ * Returns null while the row is still in flight — the caller keeps waiting,
58
+ * because a long tool call is silence, not a failure.
59
+ */
60
+ export declare function reconcileTerminalFromMessages(messages: ReadonlyArray<Record<string, unknown>>, responseMessageId?: string): ReconciledTerminal | null;
61
+ /**
62
+ * How to look at a turn the CLI is no longer watching.
63
+ *
64
+ * NOT `skrr agents chat --conversation <id>`, which is what this used to print:
65
+ * that command requires the `agentId` argument and `--prompt`, so it exits with
66
+ * `Missing 1 required arg: agentId` and never reattaches. A recovery
67
+ * instruction that cannot be run is worse than none — it costs the reader a
68
+ * round trip to discover the product was wrong.
69
+ */
70
+ export declare function inspectTurnCommand(conversationId?: string | null): string;
3
71
  /**
4
72
  * A terminal run that produced no text is NOT a success to report silently.
5
73
  *