@skrr-ai/cli 0.1.127 → 0.1.129

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 (165) hide show
  1. package/dist/base-command.d.ts +35 -0
  2. package/dist/base-command.js +107 -0
  3. package/dist/commands/agenda/trigger/list.d.ts +2 -1
  4. package/dist/commands/agenda/trigger/list.js +55 -6
  5. package/dist/commands/agents/chat.js +7 -0
  6. package/dist/commands/agents/create.js +3 -1
  7. package/dist/commands/browser/persistent-profile.d.ts +22 -0
  8. package/dist/commands/browser/persistent-profile.js +46 -0
  9. package/dist/commands/commitments/action-history.js +1 -1
  10. package/dist/commands/commitments/cases.js +3 -3
  11. package/dist/commands/commitments/check-now.d.ts +10 -2
  12. package/dist/commands/commitments/check-now.js +53 -3
  13. package/dist/commands/commitments/create.d.ts +6 -14
  14. package/dist/commands/commitments/create.js +64 -7
  15. package/dist/commands/commitments/doctor.js +3 -1
  16. package/dist/commands/commitments/effective-policy.d.ts +11 -1
  17. package/dist/commands/commitments/effective-policy.js +90 -4
  18. package/dist/commands/commitments/explain.js +10 -1
  19. package/dist/commands/commitments/hypotheses.js +12 -12
  20. package/dist/commands/commitments/next-tick.js +5 -0
  21. package/dist/commands/commitments/observations.js +18 -17
  22. package/dist/commands/commitments/pack/list.js +2 -1
  23. package/dist/commands/commitments/pack/plan.d.ts +6 -0
  24. package/dist/commands/commitments/pack/plan.js +19 -0
  25. package/dist/commands/commitments/plan.d.ts +14 -0
  26. package/dist/commands/commitments/plan.js +32 -0
  27. package/dist/commands/commitments/preflight.d.ts +20 -1
  28. package/dist/commands/commitments/preflight.js +118 -12
  29. package/dist/commands/commitments/show.d.ts +9 -0
  30. package/dist/commands/commitments/show.js +14 -1
  31. package/dist/commands/commitments/trigger-status.js +8 -0
  32. package/dist/commands/commitments/update.d.ts +1 -0
  33. package/dist/commands/commitments/update.js +45 -4
  34. package/dist/commands/commitments/wake-capabilities.js +13 -0
  35. package/dist/commands/compass/archive.js +4 -0
  36. package/dist/commands/compass/commitments.js +15 -1
  37. package/dist/commands/compass/create.js +11 -3
  38. package/dist/commands/compass/effective-policy.d.ts +1 -0
  39. package/dist/commands/compass/effective-policy.js +8 -7
  40. package/dist/commands/compass/revert.d.ts +8 -0
  41. package/dist/commands/compass/revert.js +25 -1
  42. package/dist/commands/compass/revisions.d.ts +6 -0
  43. package/dist/commands/compass/revisions.js +26 -2
  44. package/dist/commands/compass/tactic.d.ts +10 -3
  45. package/dist/commands/compass/tactic.js +22 -7
  46. package/dist/commands/compass/update.js +19 -3
  47. package/dist/commands/followups/act.js +1 -1
  48. package/dist/commands/followups/cancel.js +13 -0
  49. package/dist/commands/followups/observability.js +20 -1
  50. package/dist/commands/followups/plans/create.js +12 -1
  51. package/dist/commands/followups/show.js +13 -0
  52. package/dist/commands/followups/watch.js +1 -1
  53. package/dist/commands/goals/planner-config.d.ts +11 -0
  54. package/dist/commands/goals/planner-config.js +16 -0
  55. package/dist/commands/harnesses/list.js +1 -1
  56. package/dist/commands/machines/update.d.ts +5 -3
  57. package/dist/commands/machines/update.js +70 -9
  58. package/dist/commands/northstars/create.js +3 -1
  59. package/dist/commands/northstars/governance/configure.d.ts +4 -0
  60. package/dist/commands/northstars/governance/configure.js +76 -10
  61. package/dist/commands/northstars/intents/decide.d.ts +6 -0
  62. package/dist/commands/northstars/intents/decide.js +68 -15
  63. package/dist/commands/northstars/intents/list.d.ts +2 -1
  64. package/dist/commands/northstars/intents/list.js +15 -5
  65. package/dist/commands/northstars/intents.d.ts +26 -0
  66. package/dist/commands/northstars/intents.js +49 -0
  67. package/dist/commands/northstars/list.d.ts +1 -0
  68. package/dist/commands/northstars/list.js +24 -3
  69. package/dist/commands/northstars/recommendations/list.d.ts +1 -1
  70. package/dist/commands/northstars/recommendations/list.js +3 -3
  71. package/dist/commands/northstars/recommendations.d.ts +19 -0
  72. package/dist/commands/northstars/recommendations.js +42 -0
  73. package/dist/commands/northstars/review-now.d.ts +1 -0
  74. package/dist/commands/northstars/review-now.js +15 -4
  75. package/dist/commands/northstars/reviews.d.ts +1 -0
  76. package/dist/commands/northstars/reviews.js +34 -6
  77. package/dist/commands/northstars/update.js +8 -1
  78. package/dist/commands/proposals/list.js +11 -2
  79. package/dist/commands/proposals/show.js +9 -1
  80. package/dist/commands/spaces/auto-executor/preview.d.ts +14 -1
  81. package/dist/commands/spaces/auto-executor/preview.js +35 -1
  82. package/dist/commands/spaces/auto-executor/run.d.ts +17 -0
  83. package/dist/commands/spaces/auto-executor/run.js +60 -2
  84. package/dist/commands/spaces/auto-executor/runs.d.ts +9 -1
  85. package/dist/commands/spaces/auto-executor/runs.js +18 -1
  86. package/dist/commands/spaces/files/upload.js +1 -2
  87. package/dist/commands/spaces/heartbeats/create.js +7 -5
  88. package/dist/commands/spaces/heartbeats/update.js +5 -3
  89. package/dist/commands/spaces/index.js +23 -0
  90. package/dist/commands/store/artifacts/releases.d.ts +20 -0
  91. package/dist/commands/store/artifacts/releases.js +85 -0
  92. package/dist/commands/store/library.d.ts +14 -0
  93. package/dist/commands/store/library.js +71 -0
  94. package/dist/commands/tasks/list.d.ts +48 -0
  95. package/dist/commands/tasks/list.js +141 -9
  96. package/dist/commands/tasks/remind.js +23 -11
  97. package/dist/commands/tasks/retry-schedule.js +6 -3
  98. package/dist/commands/tasks/schedule.js +29 -7
  99. package/dist/commands/tasks/show.js +4 -0
  100. package/dist/generated/agent-guides.json +69 -69
  101. package/dist/lib/agent-trigger-listing.d.ts +26 -0
  102. package/dist/lib/agent-trigger-listing.js +63 -0
  103. package/dist/lib/agentic-stream.d.ts +9 -0
  104. package/dist/lib/agentic-stream.js +152 -41
  105. package/dist/lib/command-miss.js +11 -2
  106. package/dist/lib/commitment-attention-copy.d.ts +96 -0
  107. package/dist/lib/commitment-attention-copy.js +209 -0
  108. package/dist/lib/commitment-ledgers.js +3 -1
  109. package/dist/lib/commitment-loop-status.d.ts +6 -0
  110. package/dist/lib/commitment-loop-status.js +16 -6
  111. package/dist/lib/commitments.d.ts +112 -0
  112. package/dist/lib/commitments.js +250 -2
  113. package/dist/lib/compass-body-help.d.ts +20 -0
  114. package/dist/lib/compass-body-help.js +61 -0
  115. package/dist/lib/compass-refusals.d.ts +35 -0
  116. package/dist/lib/compass-refusals.js +50 -0
  117. package/dist/lib/compasses.d.ts +82 -4
  118. package/dist/lib/compasses.js +232 -12
  119. package/dist/lib/followups.d.ts +22 -1
  120. package/dist/lib/followups.js +21 -1
  121. package/dist/lib/format.d.ts +11 -0
  122. package/dist/lib/format.js +29 -0
  123. package/dist/lib/initiatives.d.ts +9 -2
  124. package/dist/lib/initiatives.js +13 -2
  125. package/dist/lib/iso-instant.d.ts +52 -0
  126. package/dist/lib/iso-instant.js +162 -0
  127. package/dist/lib/machines.d.ts +14 -1
  128. package/dist/lib/machines.js +24 -0
  129. package/dist/lib/northstars.d.ts +201 -24
  130. package/dist/lib/northstars.js +655 -27
  131. package/dist/lib/proposal-decision-command.js +21 -1
  132. package/dist/lib/proposals.d.ts +26 -1
  133. package/dist/lib/proposals.js +167 -0
  134. package/dist/lib/rate-limit-retry.d.ts +5 -0
  135. package/dist/lib/rate-limit-retry.js +19 -0
  136. package/dist/lib/roles.d.ts +1 -1
  137. package/dist/lib/space-charter.js +9 -0
  138. package/dist/lib/store-artifact-releases.d.ts +39 -0
  139. package/dist/lib/store-artifact-releases.js +12 -0
  140. package/dist/lib/store-library.d.ts +37 -0
  141. package/dist/lib/store-library.js +12 -0
  142. package/dist/lib/task-asset-upload.js +1 -2
  143. package/dist/lib/task-calendar.d.ts +19 -0
  144. package/dist/lib/task-calendar.js +72 -0
  145. package/dist/lib/task-recurrence-outcome.js +3 -1
  146. package/dist/lib/task-recurrence-summary.d.ts +15 -0
  147. package/dist/lib/task-recurrence-summary.js +228 -0
  148. package/dist/lib/task-reminder.d.ts +28 -0
  149. package/dist/lib/task-reminder.js +50 -0
  150. package/dist/lib/triggers.d.ts +13 -2
  151. package/dist/lib/triggers.js +31 -13
  152. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +11 -1
  153. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.d.ts +123 -0
  154. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.js +160 -0
  155. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -0
  156. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +17 -0
  157. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +11 -1
  158. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.d.ts +123 -0
  159. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.js +173 -0
  160. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -0
  161. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +2 -0
  162. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  163. package/dist/node_modules/@skrr-ai/data-provider/index.js +6922 -6828
  164. package/oclif.manifest.json +612 -62
  165. package/package.json +4 -4
@@ -1,12 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.nextRunsLines = nextRunsLines;
4
+ exports.preflightHeadline = preflightHeadline;
4
5
  exports.preflightGateLines = preflightGateLines;
6
+ exports.attentionFooterLines = attentionFooterLines;
5
7
  const core_1 = require("@oclif/core");
6
8
  const data_provider_1 = require("@skrr-ai/data-provider");
7
9
  const base_command_1 = require("../../base-command");
8
10
  const commitments_1 = require("../../lib/commitments");
9
11
  const prompt_1 = require("../../lib/prompt");
12
+ const commitment_attention_copy_1 = require("../../lib/commitment-attention-copy");
10
13
  const preflight_copy_1 = require("../../lib/preflight-copy");
11
14
  /**
12
15
  * The lines one preflight gate contributes.
@@ -41,7 +44,26 @@ function nextRunsLines(report) {
41
44
  : 'Next runs:';
42
45
  return ['', header, ...report.nextRuns.map((run) => ` ${run}`)];
43
46
  }
44
- function preflightGateLines(gate, verbose = false) {
47
+ /**
48
+ * The first line of a preflight. `Ready to run` is a statement about activation (no blocking gate),
49
+ * and it sat directly above a spend-gate warning that the daily Run budget is 0, i.e. that no agent
50
+ * turn can ever start (OSK-21224). The verdict stays `ready` on purpose: a zero ACTION budget does
51
+ * not stop the sensing lane, so refusing activation would be wrong for a Commitment that only
52
+ * observes. The headline carries the caveat instead of contradicting the gate below it.
53
+ */
54
+ function preflightHeadline(report, persisted) {
55
+ const label = persisted ? 'Preflight' : 'Pre-create preview (nothing persisted)';
56
+ if (report.verdict === 'blocked')
57
+ return `${label}: Run is blocked`;
58
+ if (report.verdict !== 'ready')
59
+ return `${label}: A capability or configuration check is still needed`;
60
+ const reasons = report.gates?.find((gate) => gate.id === 'budget_bounds')?.detail?.reasons;
61
+ const noActionRuns = Array.isArray(reasons) && reasons.includes('action_runs_zero');
62
+ return noActionRuns
63
+ ? `${label}: Ready to run, but its daily Run budget is 0, so no agent turn will start`
64
+ : `${label}: Ready to run`;
65
+ }
66
+ function preflightGateLines(gate, verbose = false, refusesActivation = false) {
45
67
  // "Could it contact you now?" is not a Run gate. It answers whether a message
46
68
  // would reach the owner, and a limit the owner set themselves (zero nudges a day,
47
69
  // quiet hours) is a chosen quiet, not a defect. It carried the same BLOCK tag, a
@@ -50,15 +72,42 @@ function preflightGateLines(gate, verbose = false) {
50
72
  // (OSK-15420). Say what it is in words, and which setting moves it.
51
73
  if (gate.id === 'attention' && gate.verdict === 'block') {
52
74
  const copy = (0, data_provider_1.describeSuppression)(gate.reason);
75
+ // "Held until your quiet hours end." named no time and no zone; the report has both (OSK-21122).
76
+ const quietEnd = gate.reason === 'policy_quiet_hours'
77
+ ? (0, commitment_attention_copy_1.describeQuietHoursEnd)(gate.detail?.quietHours)
78
+ : null;
53
79
  const lines = [
54
80
  ` ${'info'.padEnd(5)} ${gate.label} — ${copy.title.toLowerCase()}${verbose && gate.reason ? ` (${gate.reason})` : ''}`,
55
- ` ${copy.detail}`,
81
+ ` ${quietEnd ?? copy.detail}`,
56
82
  ];
57
- if (copy.settingKey)
83
+ // Whose quiet hours these are, when they are not the Commitment's own (OSK-21151): a reader
84
+ // would otherwise be sent to a field this Commitment does not hold.
85
+ const note = gate.detail?.note;
86
+ if (typeof note === 'string' && note.trim())
87
+ lines.push(` ${note.trim()}`);
88
+ // The policy field is a path into JSON, not something to type: the sentence above says what
89
+ // it means, so the path joins the code behind --verbose (OSK-21124).
90
+ if (verbose && copy.settingKey)
58
91
  lines.push(` Setting: ${copy.settingKey}`);
59
92
  return lines;
60
93
  }
61
- const mark = gate.verdict === 'pass' ? 'ok' : gate.verdict === 'block' ? 'BLOCK' : 'warn';
94
+ // A `block` the report itself marks advisory stops nothing: it is not a Run-admission gate, so it
95
+ // never feeds the verdict or `pack activate`. Printed BLOCK, `pack preflight` read "ready" above
96
+ // two BLOCK lines (dogfooded on a goal_advance draft); the tag has to mean what the report means.
97
+ //
98
+ // An advisory gate the SERVER says refuses activation is a different case (OSK-21258): `resume`
99
+ // answers 409 for it, so it is tagged BLOCK and says what it stops. `info (advisory: does not stop a
100
+ // run)` beside an "Activation: would be refused" section naming the same gate was two answers to one
101
+ // question.
102
+ const advisoryBlock = gate.verdict === 'block' && gate.admission === 'advisory' && !refusesActivation;
103
+ const stopsActivationOnly = gate.verdict === 'block' && gate.admission === 'advisory' && refusesActivation;
104
+ const mark = advisoryBlock
105
+ ? 'info'
106
+ : gate.verdict === 'pass'
107
+ ? 'ok'
108
+ : gate.verdict === 'block'
109
+ ? 'BLOCK'
110
+ : 'warn';
62
111
  // A BLOCKING gate always states its reason. `ok` gates got a helpful `note`
63
112
  // and `warn` gates got a reason token, and the BLOCK gates — the only ones
64
113
  // that stop the run — got the label and nothing: two of them side by side,
@@ -75,7 +124,9 @@ function preflightGateLines(gate, verbose = false) {
75
124
  const hasNote = typeof note === 'string' && Boolean(note.trim());
76
125
  const showReason = verbose || (gate.verdict === 'block' && !hasNote);
77
126
  const lines = [
78
- ` ${mark.padEnd(5)} ${gate.label}${showReason && gate.reason ? ` — ${gate.reason}` : ''}`,
127
+ ` ${mark.padEnd(5)} ${gate.label}${showReason && gate.reason ? ` — ${gate.reason}` : ''}` +
128
+ (advisoryBlock ? ' (advisory: does not stop a run)' : '') +
129
+ (stopsActivationOnly ? ' (stops activation, not a run that is already live)' : ''),
79
130
  ];
80
131
  if (hasNote) {
81
132
  lines.push(` ${String(note).trim()}`);
@@ -92,6 +143,13 @@ function preflightGateLines(gate, verbose = false) {
92
143
  else if (status === 'invalid') {
93
144
  lines.push(' Active owner rules: unknown (stored rules invalid).');
94
145
  }
146
+ else if (status === 'archived' &&
147
+ typeof count === 'number' &&
148
+ Number.isSafeInteger(count) &&
149
+ count >= 0) {
150
+ // The Compass is archived, not unreadable: its rules are counted, and still enforced.
151
+ lines.push(` Active owner rules: ${count} (on the archived Compass, still enforced).`);
152
+ }
95
153
  else if (status === 'available' &&
96
154
  typeof count === 'number' &&
97
155
  Number.isSafeInteger(count) &&
@@ -116,6 +174,29 @@ function preflightGateLines(gate, verbose = false) {
116
174
  lines.push(` ${sentence}`);
117
175
  return lines;
118
176
  }
177
+ /**
178
+ * The `Attention:` footer, and under it the settings behind the verdict (OSK-21120).
179
+ *
180
+ * "delivery allowed now" is the same words for a cap of 1, a mute lifted and no limit at all, so
181
+ * the cap, the gap and the no-reply rule are said beside it. `policy` is the stored policy the
182
+ * report was computed from, or the unsaved one under `--from-json`; without it nothing is said
183
+ * about the settings, rather than a guess. The raw reason code (`policy_nudge_muted`) is printed only
184
+ * with `--verbose`: beside the sentence it said the same thing twice, in a vocabulary nobody types
185
+ * (OSK-21124).
186
+ */
187
+ function attentionFooterLines(attention, policy, verbose = false) {
188
+ const code = verbose && attention?.suppressedBy ? ` (${attention.suppressedBy})` : '';
189
+ const lines = [
190
+ '',
191
+ `Attention: ${attention?.allowed ? 'delivery allowed now' : `held back — ${(0, data_provider_1.describeSuppression)(attention?.suppressedBy).title.toLowerCase()}${code}`}`,
192
+ ];
193
+ if (policy) {
194
+ for (const line of (0, commitment_attention_copy_1.nudgeSettingLines)((0, commitment_attention_copy_1.nudgeSettingsFromPolicy)(policy)))
195
+ lines.push(` ${line}`);
196
+ lines.push(` Quiet hours: ${(0, commitment_attention_copy_1.describeQuietHours)(policy.quietHours)}`);
197
+ }
198
+ return lines;
199
+ }
119
200
  class CommitmentsPreflight extends base_command_1.BaseCommand {
120
201
  static description = 'Dry-run a commitment: runtime gates, capabilities, attention policy and upcoming runs (runs nothing)';
121
202
  static examples = [
@@ -151,16 +232,37 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
151
232
  this.error('--next must be between 1 and 10', { exit: 1 });
152
233
  }
153
234
  let report;
235
+ // The policy the verdict was computed from, for the settings printed under it.
236
+ let policy = null;
154
237
  try {
155
- report = args.id
156
- ? await commitments_1.commitmentApi.preflight(args.id, flags.next)
157
- : await commitments_1.commitmentApi.draftPreflight({
158
- commitment: (0, commitments_1.normalizeCreateBody)(await (0, prompt_1.readJsonBody)(flags['from-json'])),
238
+ if (args.id) {
239
+ report = await commitments_1.commitmentApi.preflight(args.id, flags.next);
240
+ try {
241
+ policy =
242
+ (await commitments_1.commitmentApi.get(args.id))?.commitment?.policy ?? null;
243
+ }
244
+ catch {
245
+ policy = null;
246
+ }
247
+ }
248
+ else {
249
+ const rawBody = await (0, prompt_1.readJsonBody)(flags['from-json']);
250
+ const draft = (0, commitments_1.normalizeCreateBody)(rawBody);
251
+ const ignoredWarning = (0, commitments_1.ignoredCreateKeysWarning)((0, commitments_1.ignoredCreateKeys)(rawBody));
252
+ if (ignoredWarning)
253
+ this.warn(ignoredWarning);
254
+ policy = draft.policy ?? {};
255
+ const quietHoursProblem = (0, commitment_attention_copy_1.quietHoursInputProblem)(policy);
256
+ if (quietHoursProblem)
257
+ throw new Error(quietHoursProblem);
258
+ report = await commitments_1.commitmentApi.draftPreflight({
259
+ commitment: draft,
159
260
  ...(flags['event-fixture']
160
261
  ? { eventFixture: await (0, prompt_1.readJsonBody)(flags['event-fixture']) }
161
262
  : {}),
162
263
  ...(flags.next !== undefined ? { nextRunCount: flags.next } : {}),
163
264
  });
265
+ }
164
266
  }
165
267
  catch (err) {
166
268
  this.handleApiError(err);
@@ -170,7 +272,9 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
170
272
  this.log(JSON.stringify(report, null, 2));
171
273
  return;
172
274
  }
173
- this.log(`${args.id ? 'Preflight' : 'Pre-create preview (nothing persisted)'}: ${report.verdict === 'ready' ? 'Ready to run' : report.verdict === 'blocked' ? 'Run is blocked' : 'A capability or configuration check is still needed'}`);
275
+ this.log(preflightHeadline(report, Boolean(args.id)));
276
+ for (const line of (0, commitments_1.activationRefusalLines)(report.activation))
277
+ this.log(line);
174
278
  // Whose permissions the run will use (OSK-11975), before the gates, so an
175
279
  // Execute Commitment on a plan-mode Agent is the first thing read
176
280
  // (OSK-12065).
@@ -183,7 +287,8 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
183
287
  }
184
288
  this.log('\nGates:');
185
289
  for (const gate of report.gates || []) {
186
- for (const line of preflightGateLines(gate, flags.verbose))
290
+ const refusesActivation = (0, commitments_1.gateRefusesActivation)(report.activation, gate);
291
+ for (const line of preflightGateLines(gate, flags.verbose, refusesActivation))
187
292
  this.log(line);
188
293
  }
189
294
  if (report.capabilities?.length) {
@@ -211,7 +316,8 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
211
316
  (endpoint.reason ? ` (${endpoint.reason})` : '') +
212
317
  (!endpoint.available && endpoint.detail ? ` — ${endpoint.detail}` : ''));
213
318
  }
214
- this.log(`\nAttention: ${report.attention?.allowed ? 'delivery allowed now' : `held back — ${(0, data_provider_1.describeSuppression)(report.attention?.suppressedBy).title.toLowerCase()}${report.attention?.suppressedBy ? ` (${report.attention.suppressedBy})` : ''}`}`);
319
+ for (const line of attentionFooterLines(report.attention, policy, flags.verbose))
320
+ this.log(line);
215
321
  if (report.verdict === 'blocked')
216
322
  process.exitCode = 1;
217
323
  }
@@ -1,4 +1,13 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * What `show --json` prints, said where the flag is read (OSK-21334, from OSK-21205): the server's
4
+ * read envelope, with the commitment UNDER `.commitment`. `create --json` and `update --json` print
5
+ * the commitment itself, so a script written against one raised `KeyError: 'priority'` on the other.
6
+ * The shape is kept (moving the commitment up a level would break every reader of `show --json`) and
7
+ * no top-level copy is added; `docs/cli/SKRR_COMMITMENTS.md` says why, and
8
+ * `__tests__/json-shapes.spec.ts` runs both commands against this sentence.
9
+ */
10
+ export declare const SHOW_JSON_DESCRIPTION: string;
2
11
  /**
3
12
  * `skrr commitments show <id>` — the commitment's contract + live control-loop
4
13
  * state (health, last check, next check, unresolved-check streak), plus the most
@@ -1,10 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SHOW_JSON_DESCRIPTION = void 0;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const commitments_1 = require("../../lib/commitments");
6
7
  const web_url_1 = require("../../lib/web-url");
7
8
  const commitment_product_1 = require("../../lib/commitment-product");
9
+ /**
10
+ * What `show --json` prints, said where the flag is read (OSK-21334, from OSK-21205): the server's
11
+ * read envelope, with the commitment UNDER `.commitment`. `create --json` and `update --json` print
12
+ * the commitment itself, so a script written against one raised `KeyError: 'priority'` on the other.
13
+ * The shape is kept (moving the commitment up a level would break every reader of `show --json`) and
14
+ * no top-level copy is added; `docs/cli/SKRR_COMMITMENTS.md` says why, and
15
+ * `__tests__/json-shapes.spec.ts` runs both commands against this sentence.
16
+ */
17
+ exports.SHOW_JSON_DESCRIPTION = 'Output the read envelope as JSON: { commitment, latestCheck, operatingState, abilities, url }. ' +
18
+ 'The commitment itself is under .commitment (so the priority is .commitment.priority); ' +
19
+ 'create --json and update --json print the commitment unwrapped.';
8
20
  /**
9
21
  * `skrr commitments show <id>` — the commitment's contract + live control-loop
10
22
  * state (health, last check, next check, unresolved-check streak), plus the most
@@ -18,12 +30,13 @@ class CommitmentsShow extends base_command_1.BaseCommand {
18
30
  static examples = [
19
31
  '<%= config.bin %> commitments show <commitment-id>',
20
32
  '<%= config.bin %> commitments show <commitment-id> --json',
33
+ "<%= config.bin %> commitments show <commitment-id> --json | jq '.commitment.priority'",
21
34
  ];
22
35
  static args = {
23
36
  id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
24
37
  };
25
38
  static flags = {
26
- json: core_1.Flags.boolean({ description: 'Output as JSON' }),
39
+ json: core_1.Flags.boolean({ description: exports.SHOW_JSON_DESCRIPTION }),
27
40
  verbose: core_1.Flags.boolean({
28
41
  description: 'Include raw compatibility policy and check diagnostics',
29
42
  }),
@@ -82,6 +82,14 @@ function formatTriggerStatus(status) {
82
82
  '(fired or disabled — cannot wake this commitment)');
83
83
  }
84
84
  }
85
+ // The plan can be converged and every row enabled while nothing can wake (Always-On off). `convergence`
86
+ // is a fact about the rows; this is the other fact, from the same projection `commitments status` reads.
87
+ const wake = status.wake;
88
+ if (wake?.held) {
89
+ lines.push(`Wakes: HELD — ${wake.reason ?? 'blocked'}`);
90
+ if (wake.detail)
91
+ lines.push(` ${String(wake.detail).replace(/\s+/g, ' ').trim()}`);
92
+ }
85
93
  const bindings = Array.isArray(status.bindings) ? status.bindings : [];
86
94
  lines.push('');
87
95
  if (bindings.length === 0) {
@@ -49,6 +49,7 @@ export default class CommitmentsUpdate extends BaseCommand {
49
49
  'cadence-mode': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
50
50
  timezone: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
51
51
  isolation: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
52
+ 'max-nudges': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
52
53
  'max-concurrent-runs': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
53
54
  'concurrency-reason': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
54
55
  };
@@ -6,10 +6,24 @@ const commitment_product_1 = require("../../lib/commitment-product");
6
6
  const data_provider_2 = require("@skrr-ai/data-provider");
7
7
  const base_command_1 = require("../../base-command");
8
8
  const prompt_1 = require("../../lib/prompt");
9
+ const commitment_attention_copy_1 = require("../../lib/commitment-attention-copy");
9
10
  const web_url_1 = require("../../lib/web-url");
10
11
  const prompt_2 = require("../../lib/prompt");
11
12
  const triggers_1 = require("../../lib/triggers");
12
13
  const commitments_1 = require("../../lib/commitments");
14
+ /**
15
+ * Every field a stored cadence can carry. `satisfies` makes a key the type does not know a
16
+ * compile error; `update.cadence-preservation.spec.ts` pins the other direction.
17
+ */
18
+ const CARRIED_CADENCE_KEYS = [
19
+ 'mode',
20
+ 'intervalMs',
21
+ 'times',
22
+ 'jitterRatio',
23
+ 'activeHours',
24
+ 'daysOfWeek',
25
+ 'timezone',
26
+ ];
13
27
  /**
14
28
  * `skrr commitments update <id>` — edit the contract.
15
29
  *
@@ -32,13 +46,15 @@ const commitments_1 = require("../../lib/commitments");
32
46
  */
33
47
  class CommitmentsUpdate extends base_command_1.BaseCommand {
34
48
  static description = "Update a commitment's contract (title, target, cadence, sources). A cadence is manual, an interval or daily clock times; " +
35
- 'activeHours and daysOfWeek in --from-json select hours and weekdays. No holiday, business-day or trading-calendar fields';
49
+ 'activeHours and daysOfWeek in --from-json select hours and weekdays. No holiday, business-day or trading-calendar fields. ' +
50
+ 'The Compass a commitment is bound to ("compassId" in the create body) cannot be changed: create a successor commitment instead';
36
51
  static examples = [
37
52
  '<%= config.bin %> commitments update <commitment-id> --cadence 12h',
38
53
  '<%= config.bin %> commitments update <commitment-id> --title "Ship v2 (Q3)" --outcome "v2 is live for all users"',
39
54
  '<%= config.bin %> commitments update <commitment-id> --watch tasks --watch github',
40
55
  '<%= config.bin %> commitments update <commitment-id> --criterion "signups confirmed" --evaluator metric --measure <measure-id>',
41
56
  '<%= config.bin %> commitments update <commitment-id> --agent-directive-file ./review-policy.md',
57
+ '# Nudges: how many times a day it may interrupt you. 0 mutes it; "unlimited" removes the limit.\n<%= config.bin %> commitments update <commitment-id> --max-nudges 2\n<%= config.bin %> commitments update <commitment-id> --max-nudges unlimited',
42
58
  `# Read the current contract and use its version for --if-version.
43
59
  <%= config.bin %> commitments show <commitment-id> --json
44
60
  cat > commitment-patch.json <<'JSON'
@@ -48,6 +64,11 @@ cat > commitment-patch.json <<'JSON'
48
64
  }
49
65
  JSON
50
66
  <%= config.bin %> commitments update <commitment-id> --from-json commitment-patch.json --if-version <observed-version> --json`,
67
+ `# Quiet hours: no messages to you from 22:00 to 07:00. Times are HH:MM (00:00-23:59); without a timezone UTC is used.
68
+ cat > quiet-hours.json <<'JSON'
69
+ { "policy": { "quietHours": { "enabled": true, "start": "22:00", "end": "07:00", "timezone": "America/Los_Angeles" } } }
70
+ JSON
71
+ <%= config.bin %> commitments update <commitment-id> --from-json quiet-hours.json`,
51
72
  ];
52
73
  static args = {
53
74
  id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
@@ -59,7 +80,7 @@ JSON
59
80
  description: 'Skip the confirmation prompt for an irreversible status change',
60
81
  }),
61
82
  'from-json': core_1.Flags.string({
62
- description: 'JSON patch file (or "-" for stdin). See examples below. JSON cadence and watchedSources replace those fields; policy is deep-merged.',
83
+ description: `JSON patch file (or "-" for stdin). See examples below. JSON cadence and watchedSources replace those fields; policy is deep-merged. ${commitment_attention_copy_1.QUIET_HOURS_HELP} ${commitment_attention_copy_1.EVENT_SOURCES_HELP}`,
63
84
  }),
64
85
  'if-version': core_1.Flags.integer({
65
86
  description: 'Only update this observed contract version; returns a conflict if it changed',
@@ -126,6 +147,11 @@ JSON
126
147
  'to be in a Git repository with an initial commit.',
127
148
  options: [...commitments_1.COMMITMENT_EXECUTION_ISOLATION_MODES],
128
149
  }),
150
+ 'max-nudges': core_1.Flags.string({
151
+ // Sent as a single-key `policy` patch, which the API deep-merges. Before this flag the only
152
+ // way back to unlimited was hand-writing {"policy":{"maxUserNudgesPerDay":null}}.
153
+ description: commitment_attention_copy_1.MAX_NUDGES_FLAG_HELP,
154
+ }),
129
155
  'max-concurrent-runs': core_1.Flags.string({
130
156
  description: 'Set 1..100, or "unlimited" to remove the owner cap. Finite values require --concurrency-reason.',
131
157
  }),
@@ -301,7 +327,11 @@ JSON
301
327
  if (wantsCadence) {
302
328
  const existing = current?.cadence ?? {};
303
329
  const storedCadence = {};
304
- for (const key of ['mode', 'intervalMs', 'times', 'jitterRatio', 'timezone']) {
330
+ // Every authored cadence field. The API replaces `cadence` wholesale, so a field left out
331
+ // of this list is erased from the Commitment by an edit that never mentioned it: omitting
332
+ // `activeHours`/`daysOfWeek` turned "weekdays 09:00-22:00" into "around the clock" on
333
+ // `--cadence 2h` (OSK-18366). Mirrors `cadenceShape` in api/server/routes/commitments.js.
334
+ for (const key of CARRIED_CADENCE_KEYS) {
305
335
  if (existing[key] !== undefined)
306
336
  storedCadence[key] = existing[key];
307
337
  }
@@ -350,6 +380,11 @@ JSON
350
380
  delete cadence.jitterRatio;
351
381
  }
352
382
  }
383
+ // A daily cadence's rhythm is its times, and the API refuses an `intervalMs` beside them.
384
+ // Dropping the inherited one also repairs a row an earlier build let store both.
385
+ if (cadence.mode === 'daily' && !Object.hasOwn(authoredCadence ?? {}, 'intervalMs')) {
386
+ delete cadence.intervalMs;
387
+ }
353
388
  if (!['interval', 'daily', 'event_plus_interval'].includes(String(cadence.mode)) &&
354
389
  !Object.hasOwn(authoredCadence ?? {}, 'activeHours'))
355
390
  delete cadence.activeHours;
@@ -365,13 +400,16 @@ JSON
365
400
  rawValue: flags['max-concurrent-runs'],
366
401
  reason: flags['concurrency-reason'],
367
402
  });
368
- if (flags.isolation || concurrencyPatch) {
403
+ const maxNudges = (0, commitment_attention_copy_1.parseMaxNudgesFlag)(flags['max-nudges']);
404
+ if (flags.isolation || concurrencyPatch || maxNudges !== undefined) {
369
405
  const existingPolicy = payload.policy && typeof payload.policy === 'object' && !Array.isArray(payload.policy)
370
406
  ? payload.policy
371
407
  : {};
372
408
  const nextPolicy = { ...existingPolicy };
373
409
  if (flags.isolation)
374
410
  nextPolicy.executionIsolation = flags.isolation;
411
+ if (maxNudges !== undefined)
412
+ nextPolicy.maxUserNudgesPerDay = maxNudges;
375
413
  if (concurrencyPatch) {
376
414
  const existingEnvelope = nextPolicy.executionBudget &&
377
415
  typeof nextPolicy.executionBudget === 'object' &&
@@ -390,6 +428,9 @@ JSON
390
428
  !Array.isArray(finalPolicy.executionBudget)
391
429
  ? finalPolicy.executionBudget
392
430
  : undefined;
431
+ const quietHoursProblem = (0, commitment_attention_copy_1.quietHoursInputProblem)(finalPolicy);
432
+ if (quietHoursProblem)
433
+ throw new Error(quietHoursProblem);
393
434
  if (process.env.OVERSKY_AUTONOMOUS_SESSION === '1' &&
394
435
  finalEnvelope &&
395
436
  Object.prototype.hasOwnProperty.call(finalEnvelope, 'maxConcurrentRuns')) {
@@ -7,6 +7,7 @@ const base_command_1 = require("../../base-command");
7
7
  const commitments_1 = require("../../lib/commitments");
8
8
  const format_1 = require("../../lib/format");
9
9
  const commitment_event_wakes_1 = require("../../lib/commitment-event-wakes");
10
+ const commitment_attention_copy_1 = require("../../lib/commitment-attention-copy");
10
11
  /**
11
12
  * The headline used to stop at "configured sources may wake the Agent within lifecycle and execution
12
13
  * budgets", over a column marked ACTIVE, which read as bound while `trigger-status` said no event
@@ -84,6 +85,18 @@ class CommitmentsWakeCapabilities extends base_command_1.BaseCommand {
84
85
  }
85
86
  for (const line of wakePolicyLines(result))
86
87
  this.log(line);
88
+ // The owner's allowlist of event sources (OSK-21113): the response does not carry it, and for
89
+ // unset, [], a bogus name and a real source this output was byte-identical. Read from the
90
+ // Commitment's own policy; unread, the line is left out rather than claim "all allowed".
91
+ try {
92
+ const policy = (await commitments_1.commitmentApi.get(args.commitmentId))?.commitment?.policy;
93
+ if (policy) {
94
+ this.log(`Event sources: ${(0, commitment_attention_copy_1.describeEventSources)(policy.executionBudget?.allowedEventSources)}`);
95
+ }
96
+ }
97
+ catch {
98
+ // The wake table below does not depend on it.
99
+ }
87
100
  const visible = new Set(result.visibleCapabilities.map((item) => item.id));
88
101
  (0, format_1.renderTable)(result.eligibleCapabilities.map((item) => ({
89
102
  id: item.id,
@@ -58,6 +58,10 @@ class CompassArchive extends base_command_1.BaseCommand {
58
58
  this.log(JSON.stringify(result, null, 2));
59
59
  return;
60
60
  }
61
+ if (result.alreadyArchived) {
62
+ this.log(`Already archived. ${result.id} is unchanged — no new revision was written.`);
63
+ return;
64
+ }
61
65
  this.log(`Archived ${result.id}. Revisions remain readable via \`compass revisions\`.`);
62
66
  }
63
67
  }
@@ -38,10 +38,24 @@ class CompassCommitments extends base_command_1.BaseCommand {
38
38
  return;
39
39
  }
40
40
  const rows = response.data ?? [];
41
+ // The list carries no compass status, so one read says whether it governs these at all. Best
42
+ // effort: when it fails the rows still print, with no claim either way about governance.
43
+ let status;
44
+ try {
45
+ status = (await compasses_1.compassApi.get(args.compassId, { agentId: flags.agent })).status;
46
+ }
47
+ catch {
48
+ status = undefined;
49
+ }
50
+ const archived = status === 'archived';
41
51
  if (rows.length === 0) {
42
- this.log('No commitments are governed by this compass.');
52
+ this.log(archived
53
+ ? 'No commitments are bound to this compass.'
54
+ : 'No commitments are governed by this compass.');
43
55
  return;
44
56
  }
57
+ if (archived)
58
+ this.log((0, compasses_1.describeBoundCommitments)(rows.length, status));
45
59
  (0, format_1.renderTable)(rows.map((row) => ({
46
60
  id: String(row.id ?? ''),
47
61
  title: String(row.title ?? ''),
@@ -5,6 +5,7 @@ const core_1 = require("@oclif/core");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const prompt_1 = require("../../lib/prompt");
7
7
  const compasses_1 = require("../../lib/compasses");
8
+ const compass_body_help_1 = require("../../lib/compass-body-help");
8
9
  const web_url_1 = require("../../lib/web-url");
9
10
  /**
10
11
  * Stamp the owning agent onto a compass the API returned without one.
@@ -33,11 +34,17 @@ function withCompassAgentContext(compass, agentId) {
33
34
  * without turning the CLI into a schema.
34
35
  */
35
36
  class CompassCreate extends base_command_1.BaseCommand {
36
- static description = 'Create a compass for an agent';
37
+ static description = `Create a compass for an agent.
38
+
39
+ ${compass_body_help_1.COMPASS_BODY_HELP}
40
+
41
+ To govern a Commitment with this compass, put "compassId" in the Commitment's --from-json create body (see: commitments create --help). The binding is fixed at creation.`;
37
42
  static examples = [
38
43
  '<%= config.bin %> compass create --agent agent_abc --title "Production reliability" --domain reliability',
39
44
  '<%= config.bin %> compass create --agent agent_abc --title "FinOps" --invariant "Cloud spend stays under $10k/mo"',
40
45
  '<%= config.bin %> compass create --agent agent_abc --json-body ./compass.json',
46
+ `${compass_body_help_1.COMPASS_BODY_EXAMPLE}
47
+ <%= config.bin %> compass create --agent agent_abc --json-body compass.json`,
41
48
  ];
42
49
  static flags = {
43
50
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
@@ -45,11 +52,12 @@ class CompassCreate extends base_command_1.BaseCommand {
45
52
  title: core_1.Flags.string({ description: 'Human-readable direction, e.g. "Production reliability"' }),
46
53
  domain: core_1.Flags.string({ description: 'Broad subject area, e.g. "aws" or "finops"' }),
47
54
  invariant: core_1.Flags.string({
48
- description: 'Invariant target description (repeatable). The state the agent keeps true.',
55
+ description: 'Invariant target description (repeatable, at most 25). The state the agent keeps true. ' +
56
+ 'Each gets the id invariant_<position> and no domainKind: use --json-body for ids and domains.',
49
57
  multiple: true,
50
58
  }),
51
59
  'json-body': core_1.Flags.string({
52
- description: 'Path to a JSON file with the full body (merged under the flags)',
60
+ description: 'Path to a JSON file with the full body (merged under the flags). Field names and limits are listed in the description above.',
53
61
  }),
54
62
  };
55
63
  async run() {
@@ -1,6 +1,7 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  export default class CompassEffectivePolicy extends BaseCommand {
3
3
  static description: string;
4
+ static examples: string[];
4
5
  static args: {
5
6
  compassId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
7
  };
@@ -5,7 +5,13 @@ const base_command_1 = require("../../base-command");
5
5
  const compasses_1 = require("../../lib/compasses");
6
6
  class CompassEffectivePolicy extends base_command_1.BaseCommand {
7
7
  static description = 'Read a Compass policy revision, effective defaults and field provenance';
8
- static args = { compassId: core_1.Args.string({ description: 'Compass ID', required: true, ignoreStdin: true }) };
8
+ static examples = [
9
+ '<%= config.bin %> compass effective-policy agch_abc123 --agent agent_xyz',
10
+ '<%= config.bin %> compass effective-policy agch_abc123 --agent agent_xyz --json',
11
+ ];
12
+ static args = {
13
+ compassId: core_1.Args.string({ description: 'Compass ID', required: true, ignoreStdin: true }),
14
+ };
9
15
  static flags = {
10
16
  agent: core_1.Flags.string({ description: 'Agent ID that owns the Compass', required: true }),
11
17
  json: core_1.Flags.boolean({ description: 'Output the complete policy and provenance as JSON' }),
@@ -19,12 +25,7 @@ class CompassEffectivePolicy extends base_command_1.BaseCommand {
19
25
  this.log(JSON.stringify(policy, null, 2));
20
26
  return;
21
27
  }
22
- this.log(`Compass ${policy.compassId}, revision ${policy.compassVersion}: ${policy.status}`);
23
- this.log(`Applies to runtime: ${policy.appliesToRuntime === true ? 'yes' : 'no'}`);
24
- this.log(`Autonomy defaults: ${JSON.stringify(policy.autonomy ?? {})}`);
25
- this.log(`Communication defaults: ${JSON.stringify(policy.communication ?? {})}`);
26
- this.log(`Authority: ${policy.authority ?? 'unknown'}`);
27
- this.log(`Field provenance: ${JSON.stringify(policy.fieldProvenance ?? {})}`);
28
+ (0, compasses_1.renderEffectivePolicy)(policy, (line) => this.log(line));
28
29
  }
29
30
  catch (error) {
30
31
  this.handleApiError(error);
@@ -6,6 +6,13 @@ import { BaseCommand } from '../../base-command';
6
6
  * The ledger therefore records that someone chose to go back, and versions 4–8
7
7
  * stay readable instead of vanishing. That is the difference between an audit
8
8
  * trail and an undo button.
9
+ *
10
+ * Like `update`, a revert is version-fenced: `--expected-version` pins the
11
+ * version the caller looked at, and without it the current version is read at
12
+ * the start and said so on stderr, so a concurrent edit is a named conflict
13
+ * rather than a surprise. A revert restores the configuration and keeps the
14
+ * compass's current status; it says when it wrote nothing and when it removed
15
+ * enforced rules.
9
16
  */
10
17
  export default class CompassRevert extends BaseCommand {
11
18
  static description: string;
@@ -18,6 +25,7 @@ export default class CompassRevert extends BaseCommand {
18
25
  agent: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
19
26
  'to-version': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
20
27
  reason: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
28
+ 'expected-version': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
29
  };
22
30
  run(): Promise<void>;
23
31
  }