@skrr-ai/cli 0.1.127 → 0.1.128

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 (113) hide show
  1. package/dist/base-command.d.ts +23 -0
  2. package/dist/base-command.js +72 -0
  3. package/dist/commands/agents/chat.js +7 -0
  4. package/dist/commands/commitments/action-history.js +1 -1
  5. package/dist/commands/commitments/cases.js +3 -3
  6. package/dist/commands/commitments/check-now.d.ts +10 -2
  7. package/dist/commands/commitments/check-now.js +53 -3
  8. package/dist/commands/commitments/create.d.ts +6 -14
  9. package/dist/commands/commitments/create.js +61 -6
  10. package/dist/commands/commitments/doctor.js +3 -1
  11. package/dist/commands/commitments/effective-policy.d.ts +11 -1
  12. package/dist/commands/commitments/effective-policy.js +84 -4
  13. package/dist/commands/commitments/hypotheses.js +12 -12
  14. package/dist/commands/commitments/next-tick.js +5 -0
  15. package/dist/commands/commitments/observations.js +18 -17
  16. package/dist/commands/commitments/pack/list.js +2 -1
  17. package/dist/commands/commitments/pack/plan.d.ts +6 -0
  18. package/dist/commands/commitments/pack/plan.js +19 -0
  19. package/dist/commands/commitments/preflight.d.ts +19 -0
  20. package/dist/commands/commitments/preflight.js +108 -10
  21. package/dist/commands/commitments/trigger-status.js +8 -0
  22. package/dist/commands/commitments/update.d.ts +1 -0
  23. package/dist/commands/commitments/update.js +45 -4
  24. package/dist/commands/commitments/wake-capabilities.js +13 -0
  25. package/dist/commands/compass/archive.js +4 -0
  26. package/dist/commands/compass/commitments.js +15 -1
  27. package/dist/commands/compass/create.js +11 -3
  28. package/dist/commands/compass/effective-policy.d.ts +1 -0
  29. package/dist/commands/compass/effective-policy.js +8 -7
  30. package/dist/commands/compass/revert.d.ts +8 -0
  31. package/dist/commands/compass/revert.js +25 -1
  32. package/dist/commands/compass/revisions.d.ts +2 -0
  33. package/dist/commands/compass/revisions.js +18 -2
  34. package/dist/commands/compass/tactic.d.ts +5 -3
  35. package/dist/commands/compass/tactic.js +8 -7
  36. package/dist/commands/compass/update.js +19 -3
  37. package/dist/commands/followups/act.js +1 -1
  38. package/dist/commands/followups/cancel.js +13 -0
  39. package/dist/commands/followups/observability.js +20 -1
  40. package/dist/commands/followups/plans/create.js +12 -1
  41. package/dist/commands/followups/show.js +13 -0
  42. package/dist/commands/followups/watch.js +1 -1
  43. package/dist/commands/goals/planner-config.d.ts +11 -0
  44. package/dist/commands/goals/planner-config.js +16 -0
  45. package/dist/commands/machines/update.d.ts +5 -3
  46. package/dist/commands/machines/update.js +70 -9
  47. package/dist/commands/northstars/create.js +3 -1
  48. package/dist/commands/northstars/governance/configure.d.ts +4 -0
  49. package/dist/commands/northstars/governance/configure.js +75 -9
  50. package/dist/commands/northstars/intents/decide.d.ts +6 -0
  51. package/dist/commands/northstars/intents/decide.js +69 -15
  52. package/dist/commands/northstars/intents/list.d.ts +2 -1
  53. package/dist/commands/northstars/intents/list.js +15 -5
  54. package/dist/commands/northstars/intents.d.ts +26 -0
  55. package/dist/commands/northstars/intents.js +49 -0
  56. package/dist/commands/northstars/list.d.ts +1 -0
  57. package/dist/commands/northstars/list.js +24 -3
  58. package/dist/commands/northstars/recommendations/list.d.ts +1 -1
  59. package/dist/commands/northstars/recommendations/list.js +3 -3
  60. package/dist/commands/northstars/recommendations.d.ts +19 -0
  61. package/dist/commands/northstars/recommendations.js +42 -0
  62. package/dist/commands/northstars/review-now.d.ts +1 -0
  63. package/dist/commands/northstars/review-now.js +10 -4
  64. package/dist/commands/northstars/reviews.d.ts +1 -0
  65. package/dist/commands/northstars/reviews.js +19 -3
  66. package/dist/commands/northstars/update.js +8 -1
  67. package/dist/commands/proposals/list.js +11 -2
  68. package/dist/commands/proposals/show.js +9 -1
  69. package/dist/commands/spaces/auto-executor/preview.d.ts +14 -1
  70. package/dist/commands/spaces/auto-executor/preview.js +35 -1
  71. package/dist/commands/spaces/auto-executor/run.d.ts +17 -0
  72. package/dist/commands/spaces/auto-executor/run.js +60 -2
  73. package/dist/commands/spaces/files/upload.js +1 -2
  74. package/dist/commands/spaces/index.js +23 -0
  75. package/dist/generated/agent-guides.json +69 -69
  76. package/dist/lib/agentic-stream.d.ts +9 -0
  77. package/dist/lib/agentic-stream.js +14 -0
  78. package/dist/lib/command-miss.js +11 -2
  79. package/dist/lib/commitment-attention-copy.d.ts +96 -0
  80. package/dist/lib/commitment-attention-copy.js +209 -0
  81. package/dist/lib/commitment-loop-status.d.ts +6 -0
  82. package/dist/lib/commitment-loop-status.js +16 -6
  83. package/dist/lib/commitments.d.ts +88 -0
  84. package/dist/lib/commitments.js +188 -0
  85. package/dist/lib/compass-body-help.d.ts +20 -0
  86. package/dist/lib/compass-body-help.js +61 -0
  87. package/dist/lib/compass-refusals.d.ts +35 -0
  88. package/dist/lib/compass-refusals.js +50 -0
  89. package/dist/lib/compasses.d.ts +80 -4
  90. package/dist/lib/compasses.js +232 -12
  91. package/dist/lib/followups.d.ts +22 -1
  92. package/dist/lib/followups.js +21 -1
  93. package/dist/lib/format.d.ts +11 -0
  94. package/dist/lib/format.js +29 -0
  95. package/dist/lib/initiatives.d.ts +9 -2
  96. package/dist/lib/initiatives.js +13 -2
  97. package/dist/lib/machines.d.ts +14 -1
  98. package/dist/lib/machines.js +24 -0
  99. package/dist/lib/northstars.d.ts +158 -24
  100. package/dist/lib/northstars.js +538 -26
  101. package/dist/lib/proposal-decision-command.js +21 -1
  102. package/dist/lib/proposals.d.ts +26 -1
  103. package/dist/lib/proposals.js +163 -0
  104. package/dist/lib/rate-limit-retry.d.ts +5 -0
  105. package/dist/lib/rate-limit-retry.js +19 -0
  106. package/dist/lib/roles.d.ts +1 -1
  107. package/dist/lib/space-charter.js +9 -0
  108. package/dist/lib/task-asset-upload.js +1 -2
  109. package/dist/lib/triggers.d.ts +1 -1
  110. package/dist/lib/triggers.js +11 -3
  111. package/dist/node_modules/@skrr-ai/data-provider/index.js +6504 -6493
  112. package/oclif.manifest.json +350 -34
  113. package/package.json +4 -4
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.COMMITMENT_CLI_EVALUATORS = exports.COMMITMENT_CREATE_KEYS = exports.COMMITMENT_REMEDIATION_ACTIONS = exports.COMMITMENT_FEEDBACK_KINDS = exports.commitmentApi = exports.commitmentPackApi = exports.MAX_COMMITMENT_INTERVAL_MS = exports.MIN_COMMITMENT_INTERVAL_MS = exports.COMMITMENT_EXECUTION_ISOLATION_MODES = exports.COMMITMENT_AUTHORABLE_CADENCE_MODES = exports.COMMITMENT_KIND_VALUES = exports.COMMITMENT_STATUSES = exports.COMMITMENT_CADENCE_MAX_TIMES = exports.COMMITMENT_CADENCE_TIME_PATTERN = exports.COMMITMENT_CADENCE_MODES = exports.COMMITMENT_TEMPLATE_IDS = exports.COMMITMENT_WATCHED_SOURCES = void 0;
4
+ exports.activationRefusalLines = activationRefusalLines;
4
5
  exports.permissionLines = permissionLines;
5
6
  exports.scheduleIssueLines = scheduleIssueLines;
6
7
  exports.retiredFieldsLine = retiredFieldsLine;
7
8
  exports.readCommitmentAgentDirective = readCommitmentAgentDirective;
9
+ exports.unrecognizedCreateKeys = unrecognizedCreateKeys;
10
+ exports.ignoredCreateKeys = ignoredCreateKeys;
11
+ exports.ignoredCreateKeysWarning = ignoredCreateKeysWarning;
8
12
  exports.normalizeCreateBody = normalizeCreateBody;
9
13
  exports.parseCommitmentConcurrencyLimit = parseCommitmentConcurrencyLimit;
10
14
  exports.commitmentConcurrencyEnvelopePatch = commitmentConcurrencyEnvelopePatch;
@@ -29,6 +33,8 @@ exports.withLiveAssessment = withLiveAssessment;
29
33
  exports.commitmentWriteWarnings = commitmentWriteWarnings;
30
34
  exports.renderCommitmentWriteWarnings = renderCommitmentWriteWarnings;
31
35
  exports.describeCommitmentRevision = describeCommitmentRevision;
36
+ exports.describeCommitmentPriority = describeCommitmentPriority;
37
+ exports.describeBoundCompass = describeBoundCompass;
32
38
  exports.renderCommitmentSummary = renderCommitmentSummary;
33
39
  exports.describeWakeDecision = describeWakeDecision;
34
40
  exports.renderCheckList = renderCheckList;
@@ -37,6 +43,7 @@ exports.renderCommitmentTrace = renderCommitmentTrace;
37
43
  exports.renderProactivenessScorecard = renderProactivenessScorecard;
38
44
  exports.formatCommitmentCycles = formatCommitmentCycles;
39
45
  exports.hypothesisRefusalLines = hypothesisRefusalLines;
46
+ exports.wakeOutcomeLines = wakeOutcomeLines;
40
47
  exports.formatCommitmentExplanation = formatCommitmentExplanation;
41
48
  exports.renderListPageHint = renderListPageHint;
42
49
  exports.describeObservationArtifacts = describeObservationArtifacts;
@@ -80,6 +87,7 @@ Object.defineProperty(exports, "COMMITMENT_EXECUTION_ISOLATION_MODES", { enumera
80
87
  Object.defineProperty(exports, "COMMITMENT_STATUSES", { enumerable: true, get: function () { return data_provider_2.COMMITMENT_STATUSES; } });
81
88
  Object.defineProperty(exports, "COMMITMENT_KIND_VALUES", { enumerable: true, get: function () { return data_provider_2.COMMITMENT_KIND_VALUES; } });
82
89
  const commitment_product_1 = require("./commitment-product");
90
+ const commitment_attention_copy_1 = require("./commitment-attention-copy");
83
91
  const instruction_input_1 = require("./instruction-input");
84
92
  const format_1 = require("./format");
85
93
  const triggers_1 = require("./triggers");
@@ -95,6 +103,27 @@ exports.COMMITMENT_AUTHORABLE_CADENCE_MODES = data_provider_2.COMMITMENT_CADENCE
95
103
  */
96
104
  exports.MIN_COMMITMENT_INTERVAL_MS = 60_000;
97
105
  exports.MAX_COMMITMENT_INTERVAL_MS = 365 * 24 * 60 * 60 * 1000;
106
+ /**
107
+ * The lines preflight prints when the server says activation would be refused (OSK-21258). The gate
108
+ * list marks an advisory gate `info (advisory: does not stop a run)`, which is true of a run and false
109
+ * of activation: `resume` refused the same gate with a 409 while preflight read as if nothing stopped
110
+ * it. The server owns which gates refuse; this lays out its answer and never re-derives it. Nothing
111
+ * for an allowed activation or an older server that does not send the block.
112
+ */
113
+ function activationRefusalLines(activation) {
114
+ if (!activation || activation.canActivate !== false)
115
+ return [];
116
+ const blockers = activation.blockers ?? [];
117
+ if (!blockers.length)
118
+ return ['Activation: would be refused (the server named no gate)'];
119
+ const lines = ['Activation: would be refused'];
120
+ for (const blocker of blockers) {
121
+ lines.push(` - ${blocker.label || blocker.gate}${blocker.reason ? ` (${blocker.reason})` : ''}`);
122
+ if (blocker.hint)
123
+ lines.push(` ${blocker.hint}`);
124
+ }
125
+ return lines;
126
+ }
98
127
  /**
99
128
  * The lines every Commitment read surface prints for "whose permissions does a
100
129
  * run use?" — `show`, `effective-policy` and `preflight` share it so the three
@@ -414,6 +443,86 @@ async function readCommitmentAgentDirective({ directive, file, }) {
414
443
  }
415
444
  return (0, instruction_input_1.readInstructionBody)({ body: directive, file });
416
445
  }
446
+ /**
447
+ * What a contract dumped from `show`/`list --json` carries that is the server's, not the author's. These are
448
+ * dropped on purpose and never reported.
449
+ */
450
+ const SERVER_OWNED_CONTRACT_KEYS = new Set([
451
+ 'id',
452
+ 'ownerUserId',
453
+ 'workspaceId',
454
+ 'version',
455
+ 'createdAt',
456
+ 'updatedAt',
457
+ 'createdBy',
458
+ 'updatedBy',
459
+ 'state',
460
+ 'firstActivatedAt',
461
+ 'executionEpoch',
462
+ 'alwaysOnGeneration',
463
+ 'requiresAlwaysOn',
464
+ 'assessment',
465
+ 'abilities',
466
+ 'url',
467
+ 'latestCheck',
468
+ 'operatingState',
469
+ 'triggerPlan',
470
+ 'executorHandovers',
471
+ 'priority',
472
+ 'proposal',
473
+ 'rationale',
474
+ 'droppedFields',
475
+ 'warnings',
476
+ ]);
477
+ function editDistance(a, b) {
478
+ const row = Array.from({ length: b.length + 1 }, (_, i) => i);
479
+ for (let i = 1; i <= a.length; i += 1) {
480
+ let diagonal = row[0];
481
+ row[0] = i;
482
+ for (let j = 1; j <= b.length; j += 1) {
483
+ const above = row[j];
484
+ row[j] = Math.min(row[j] + 1, row[j - 1] + 1, diagonal + (a[i - 1] === b[j - 1] ? 0 : 1));
485
+ diagonal = above;
486
+ }
487
+ }
488
+ return row[b.length];
489
+ }
490
+ /**
491
+ * The top-level keys of a `--from-json` body that `create`/`preflight` will not send. A key within two edits
492
+ * of a real one is a `typo`; the rest are `ignored`. A server-owned dump field is neither.
493
+ */
494
+ function unrecognizedCreateKeys(src) {
495
+ const known = exports.COMMITMENT_CREATE_KEYS;
496
+ const typos = [];
497
+ const ignored = [];
498
+ for (const key of Object.keys(src || {})) {
499
+ if (known.includes(key) || SERVER_OWNED_CONTRACT_KEYS.has(key))
500
+ continue;
501
+ const near = key.length >= 4
502
+ ? known.find((candidate) => candidate.length >= 4 && editDistance(key.toLowerCase(), candidate.toLowerCase()) <= 2)
503
+ : undefined;
504
+ if (near)
505
+ typos.push({ key, suggestion: near });
506
+ else
507
+ ignored.push(key);
508
+ }
509
+ return { typos, ignored };
510
+ }
511
+ /** The keys of a `--from-json` body that are neither sent nor server-owned and not a typo: to be warned about. */
512
+ function ignoredCreateKeys(input) {
513
+ const envelope = input;
514
+ const src = envelope && typeof envelope.proposal === 'object' && envelope.proposal !== null
515
+ ? envelope.proposal
516
+ : input || {};
517
+ return unrecognizedCreateKeys(src).ignored;
518
+ }
519
+ /** The one sentence `create` and `preflight` print for them. */
520
+ function ignoredCreateKeysWarning(keys) {
521
+ if (keys.length === 0)
522
+ return null;
523
+ return (`--from-json: ${keys.map((key) => `"${key}"`).join(', ')} ${keys.length === 1 ? 'is' : 'are'} not sent ` +
524
+ "(the CLI does not forward it), so the Commitment is built without it.");
525
+ }
417
526
  /**
418
527
  * Normalize a `--from-json` body into something the create endpoint accepts.
419
528
  *
@@ -433,6 +542,16 @@ function normalizeCreateBody(input) {
433
542
  const src = envelope && typeof envelope.proposal === 'object' && envelope.proposal !== null
434
543
  ? envelope.proposal
435
544
  : input || {};
545
+ // Dropping a key the body does not declare is deliberate for what a dumped contract carries (id, version,
546
+ // state, ...). It is not for a typo of a real field: `cadance` was dropped and the Commitment created with
547
+ // the default manual cadence, exit 0, while the same body over REST is a 400 (dogfood
548
+ // df-261031-commitment-authoring, OSK-21246).
549
+ const { typos } = unrecognizedCreateKeys(src);
550
+ if (typos.length > 0) {
551
+ throw new Error(`--from-json has ${typos.length === 1 ? 'a field' : 'fields'} the API does not know: ` +
552
+ `${typos.map((t) => `"${t.key}" (did you mean "${t.suggestion}"?)`).join(', ')}. ` +
553
+ 'Nothing was sent: the API refuses unknown fields, and dropping them here would create the Commitment without them.');
554
+ }
436
555
  const out = {};
437
556
  for (const key of exports.COMMITMENT_CREATE_KEYS) {
438
557
  if (src[key] !== undefined)
@@ -976,6 +1095,12 @@ function describeExecutorBlocked(commitment) {
976
1095
  * operation and reason is printed as it always was.
977
1096
  */
978
1097
  function describeOperation(operation, reason) {
1098
+ // "Cannot tell" travels as `blocked` + this reason (the server's `storedOperation`: no status
1099
+ // recorded yet for this version of the contract, or the recorded one expired). A Commitment that
1100
+ // was turned on a moment ago is in exactly that state, and `blocked` read as a refused activation
1101
+ // while `show` said `ready` seconds later (OSK-21105).
1102
+ if (reason === 'operation_read_unavailable')
1103
+ return 'not read yet — no status is recorded for the current contract, which is not a failure; `skrr commitments show` reads it live';
979
1104
  const lifecycle = typeof reason === 'string' ? /^lifecycle_([a-z_]+)$/.exec(reason) : null;
980
1105
  if (!lifecycle)
981
1106
  return `${operation ?? 'blocked'}${reason ? ` — ${reason}` : ''}`;
@@ -1135,6 +1260,37 @@ function describeCommitmentRevision(revision) {
1135
1260
  }
1136
1261
  return `Updated commitment (version ${revision.previousVersion} -> ${revision.version}):`;
1137
1262
  }
1263
+ /**
1264
+ * A commitment's priority, as one line: `high · weight 70 · from northstar`.
1265
+ *
1266
+ * Linking a commitment to a northstar rewrites its priority (`source: northstar`) and a governor
1267
+ * reprioritization changes it again, so the place a person looks to confirm either is `show`, which
1268
+ * printed everything but this and sent them to `--json`. An unset priority says so rather than
1269
+ * printing nothing.
1270
+ */
1271
+ function describeCommitmentPriority(priority) {
1272
+ const parts = [];
1273
+ if (priority?.tier)
1274
+ parts.push(priority.tier);
1275
+ if (typeof priority?.weight === 'number')
1276
+ parts.push(`weight ${priority.weight}`);
1277
+ if (priority?.source === 'northstar')
1278
+ parts.push('from northstar');
1279
+ else if (priority?.source === 'manual')
1280
+ parts.push('set manually');
1281
+ return parts.length ? parts.join(' · ') : 'not set';
1282
+ }
1283
+ /**
1284
+ * The `Compass:` line of a bound commitment. A commitment names its compass (`compassId`) but the
1285
+ * read does not carry that compass's STATUS, and an archived one governs nothing (the commitment
1286
+ * runs on defaults), so the id alone could be read as "governed by it". Only what is known is
1287
+ * stated: the id, and the command that says whether it is active (OSK-21242).
1288
+ */
1289
+ function describeBoundCompass(commitment) {
1290
+ const id = commitment.compassId ?? '-';
1291
+ const agent = commitment.agentId ?? '<agent-id>';
1292
+ return `Compass: ${id} — status not shown here; an archived compass governs nothing: \`skrr compass show ${id} --agent ${agent}\``;
1293
+ }
1138
1294
  function renderCommitmentSummary(commitment, log,
1139
1295
  /**
1140
1296
  * The most recent check, when the caller has one. Optional because pause /
@@ -1174,6 +1330,9 @@ scheduleIssues) {
1174
1330
  log(`Goal: ${commitment.goalId}`);
1175
1331
  if (commitment.northstarId)
1176
1332
  log(`Northstar: ${commitment.northstarId}`);
1333
+ if (commitment.compassId)
1334
+ log(describeBoundCompass(commitment));
1335
+ log(`Priority: ${describeCommitmentPriority(commitment.priority)}`);
1177
1336
  if (commitment.target?.outcome)
1178
1337
  log(`Outcome: ${commitment.target.outcome}`);
1179
1338
  if (commitment.target?.deadlineAt)
@@ -1184,6 +1343,13 @@ scheduleIssues) {
1184
1343
  log(`Delivery: ${(0, commitment_product_1.deliveryLabel)(commitment.delivery?.mode)}${commitment.delivery?.repo ? ` · ${commitment.delivery.repo}` : ''}${commitment.delivery?.baseBranch ? ` → ${commitment.delivery.baseBranch}` : ''}`);
1185
1344
  log(`Concurrency: ${describeCommitmentConcurrency(commitment)}`);
1186
1345
  log(`Evidence gate: ${describeEvidenceGate(commitment)}`);
1346
+ // How much it may interrupt the owner (OSK-21120). A server that sent no policy prints
1347
+ // nothing, rather than a guess that reads as "unlimited".
1348
+ if (commitment.policy) {
1349
+ for (const line of (0, commitment_attention_copy_1.nudgeSettingLines)((0, commitment_attention_copy_1.nudgeSettingsFromPolicy)(commitment.policy)))
1350
+ log(line);
1351
+ log(`Quiet hours: ${(0, commitment_attention_copy_1.describeQuietHours)(commitment.policy.quietHours)}`);
1352
+ }
1187
1353
  const criteria = commitment.target?.successCriteria || [];
1188
1354
  const evidenceHealth = latestCheck?.inputs?.evidenceHealth;
1189
1355
  const evidenceLine = describeEvidenceHealth(evidenceHealth, criteria.length);
@@ -1730,6 +1896,8 @@ function formatCommitmentCycles(page) {
1730
1896
  lines.push(` run ${run.id} report: ${run.reportPayload.status === 'rejected' ? 'structured report rejected; plain summary used' : 'structured report validated (delivery tracked separately)'}`);
1731
1897
  for (const line of hypothesisRefusalLines(run))
1732
1898
  lines.push(` ${line}`);
1899
+ for (const line of wakeOutcomeLines(run))
1900
+ lines.push(` ${line}`);
1733
1901
  if (!['machine', 'contract', 'work'].includes(run.failureClass))
1734
1902
  continue;
1735
1903
  const owner = run.failureOwner === 'model' ? 'model' : run.failureOwner || 'work';
@@ -1784,6 +1952,20 @@ const HYPOTHESIS_REFUSAL_REASON = {
1784
1952
  function hypothesisRefusalLines(run) {
1785
1953
  return (run.hypothesisRefusals || []).map((refusal) => `${refusal.count} ${refusal.count === 1 ? 'hypothesis' : 'hypotheses'} not recorded: ${HYPOTHESIS_REFUSAL_REASON[refusal.reason] || refusal.reason} (${refusal.reason})`);
1786
1954
  }
1955
+ /**
1956
+ * A wake the Agent asked for and the platform did not arm, in the server's words.
1957
+ * The only record used to be `ledger --json`; the cycle read `advanced` (OSK-21132).
1958
+ */
1959
+ function wakeOutcomeLines(run) {
1960
+ const wake = run.wake;
1961
+ if (!wake?.detail)
1962
+ return [];
1963
+ const label = wake.status === 'awaiting_approval' ? 'wake request waiting' : 'wake not armed';
1964
+ return [
1965
+ `${label}: ${wake.detail}${wake.reason ? ` (${wake.reason})` : ''}`,
1966
+ ...(wake.fix ? [` Fix: ${wake.fix}`] : []),
1967
+ ];
1968
+ }
1787
1969
  function formatCommitmentExplanation(explanation) {
1788
1970
  const cycle = explanation.cycle;
1789
1971
  const lines = [
@@ -1819,6 +2001,8 @@ function formatCommitmentExplanation(explanation) {
1819
2001
  lines.push(` run ${run.id} report: ${run.reportPayload.status === 'rejected' ? 'structured report rejected; plain summary used' : 'structured report validated (delivery tracked separately)'}`);
1820
2002
  for (const line of hypothesisRefusalLines(run))
1821
2003
  lines.push(` ${line}`);
2004
+ for (const line of wakeOutcomeLines(run))
2005
+ lines.push(` ${line}`);
1822
2006
  }
1823
2007
  }
1824
2008
  lines.push(...permissionLines(explanation.permissions));
@@ -1827,6 +2011,10 @@ function formatCommitmentExplanation(explanation) {
1827
2011
  /**
1828
2012
  * Print the continuation hint for a paged `object:'list'` response — the line
1829
2013
  * that makes history beyond the newest page reachable instead of invisible.
2014
+ *
2015
+ * Text mode ONLY. Under `--json` stdout must be one JSON document, and the same
2016
+ * facts are already in it as `has_more` / `next_cursor`: a caller that prints
2017
+ * this after the JSON makes `| json.load` fail (OSK-21118).
1830
2018
  */
1831
2019
  function renderListPageHint(page, log) {
1832
2020
  if (page?.has_more === true && page.next_cursor) {
@@ -0,0 +1,20 @@
1
+ /**
2
+ * What `--json-body` takes on `skrr compass create` and `skrr compass update` (OSK-21154).
3
+ *
4
+ * The flag's help said only "a JSON file with the full body", so the field names, the
5
+ * invariant-target limit and the quiet-hours rules were found by guessing and by being refused. One
6
+ * declaration, read by both commands' help so they cannot drift apart. The server owns every rule
7
+ * stated here (`api/server/services/AgentCompass/CompassPolicyContract.js` and the route schemas); the
8
+ * limits are written out because a help screen is read before the first request is sent, and
9
+ * `docs/cli/SKRR_COMPASS.md` ("Body shape") carries the same table.
10
+ *
11
+ * ONE SHORT LINE PER FIELD, each beginning with its full path. oclif runs a description through
12
+ * `wrap-ansi` (hard wrap at the terminal width minus 2, 80 when not a terminal) which TRIMS every
13
+ * line's leading spaces, so the indentation this list used to carry collapsed (`id` and
14
+ * `description` read as top-level fields) and its long lines broke mid-sentence. A path prefix is
15
+ * not whitespace, so the nesting survives, and no line is longer than 77 characters, so none wraps
16
+ * (`help.spec.ts` runs the real wrap over it) (OSK-21243).
17
+ */
18
+ export declare const COMPASS_BODY_HELP: string;
19
+ /** An example body: two targets (one domain-scoped, one general) and a quiet-hours window. */
20
+ export declare const COMPASS_BODY_EXAMPLE = "# compass.json\n{\n \"title\": \"Production reliability\",\n \"invariantTargets\": [\n { \"id\": \"api-latency\", \"domainKind\": \"reliability\", \"description\": \"p99 API latency stays under 300 ms\" },\n { \"id\": \"general\", \"description\": \"Nothing the owner has not seen ships on a Friday\" }\n ],\n \"autonomyDefaults\": {\n \"mode\": \"ask_first\",\n \"quietHours\": { \"enabled\": true, \"start\": \"22:00\", \"end\": \"07:00\", \"timezone\": \"America/New_York\" }\n }\n}";
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.COMPASS_BODY_EXAMPLE = exports.COMPASS_BODY_HELP = void 0;
4
+ /**
5
+ * What `--json-body` takes on `skrr compass create` and `skrr compass update` (OSK-21154).
6
+ *
7
+ * The flag's help said only "a JSON file with the full body", so the field names, the
8
+ * invariant-target limit and the quiet-hours rules were found by guessing and by being refused. One
9
+ * declaration, read by both commands' help so they cannot drift apart. The server owns every rule
10
+ * stated here (`api/server/services/AgentCompass/CompassPolicyContract.js` and the route schemas); the
11
+ * limits are written out because a help screen is read before the first request is sent, and
12
+ * `docs/cli/SKRR_COMPASS.md` ("Body shape") carries the same table.
13
+ *
14
+ * ONE SHORT LINE PER FIELD, each beginning with its full path. oclif runs a description through
15
+ * `wrap-ansi` (hard wrap at the terminal width minus 2, 80 when not a terminal) which TRIMS every
16
+ * line's leading spaces, so the indentation this list used to carry collapsed (`id` and
17
+ * `description` read as top-level fields) and its long lines broke mid-sentence. A path prefix is
18
+ * not whitespace, so the nesting survives, and no line is longer than 77 characters, so none wraps
19
+ * (`help.spec.ts` runs the real wrap over it) (OSK-21243).
20
+ */
21
+ exports.COMPASS_BODY_HELP = [
22
+ '--json-body takes the API body as a JSON file; the flags are merged over it.',
23
+ 'Each line below names one field by its full path:',
24
+ 'title, domain, reason: strings (reason is what the revision ledger shows)',
25
+ 'invariantTargets[]: what the agent keeps true',
26
+ 'invariantTargets[]: AT MOST 25: more is refused with the count sent',
27
+ 'invariantTargets[].id: optional, unique in the list (a repeat is refused)',
28
+ 'invariantTargets[].id: default invariant_<position>',
29
+ 'invariantTargets[].description: required on create',
30
+ 'invariantTargets[].domainKind: optional; leave it off ONE target',
31
+ 'invariantTargets[].domainKind: the target without one is the general fallback',
32
+ 'invariantTargets[].maintainedState[]: list of strings',
33
+ 'invariantTargets[].successMetrics[]: list of strings',
34
+ 'invariantTargets[].boundaries[]: list of strings',
35
+ 'invariantTargets[].cadence, .escalation, .stateMonitor: advanced',
36
+ 'invariantTargets[].learnedTactics[]: advanced; see docs/cli/SKRR_COMPASS.md',
37
+ 'autonomyDefaults.mode: off | suggestions_only | ask_first | auto_run_limited',
38
+ 'autonomyDefaults.mode: auto_run (short forms: suggest, auto)',
39
+ 'autonomyDefaults.quietHours = {"enabled":true,"start":"22:00","end":"07:00"}',
40
+ 'autonomyDefaults.quietHours.start, .end: HH:MM, 00:00 to 23:59',
41
+ 'autonomyDefaults.quietHours: start must differ from end; refused otherwise',
42
+ 'autonomyDefaults.quietHours.timezone: an IANA zone, e.g. America/New_York',
43
+ 'communicationDefaults: mode, destination, fallbackDestination, quietHours',
44
+ 'communicationDefaults.allowedDestinations[]: list of strings',
45
+ 'communicationDefaults.requireApprovalRiskLevels[]: list of strings',
46
+ 'communicationDefaults: any other key is stored as a note and never read',
47
+ 'rules[]: owner-enforced, Agent owner only; see docs/cli/SKRR_COMPASS.md',
48
+ ].join('\n');
49
+ /** An example body: two targets (one domain-scoped, one general) and a quiet-hours window. */
50
+ exports.COMPASS_BODY_EXAMPLE = `# compass.json
51
+ {
52
+ "title": "Production reliability",
53
+ "invariantTargets": [
54
+ { "id": "api-latency", "domainKind": "reliability", "description": "p99 API latency stays under 300 ms" },
55
+ { "id": "general", "description": "Nothing the owner has not seen ships on a Friday" }
56
+ ],
57
+ "autonomyDefaults": {
58
+ "mode": "ask_first",
59
+ "quietHours": { "enabled": true, "start": "22:00", "end": "07:00", "timezone": "America/New_York" }
60
+ }
61
+ }`;
@@ -0,0 +1,35 @@
1
+ /**
2
+ * compass-refusals.ts — the words for the two Compass refusals a person acts on.
3
+ *
4
+ * `COMPASS_VERSION_CONFLICT` (409) and `COMPASS_REVISION_NOT_FOUND` (404) used to reach the
5
+ * terminal as `HTTP_409` and `NOT_FOUND`, with the one fact that makes each actionable dropped:
6
+ * where the Compass is now, and which version was missing (OSK-21162, OSK-21152).
7
+ *
8
+ * The route sends the code in upper-case `error` (and now in `code` too). `refusalCodeOf` does
9
+ * not read the first as a code, and widening it for every route is not this module's call, so
10
+ * `compassRefusalCode` reads both spellings here, for these two codes only. No imports: this is
11
+ * pure, and `base-command` loads it.
12
+ */
13
+ export declare const COMPASS_VERSION_CONFLICT = "COMPASS_VERSION_CONFLICT";
14
+ export declare const COMPASS_REVISION_NOT_FOUND = "COMPASS_REVISION_NOT_FOUND";
15
+ export type CompassRefusalCode = typeof COMPASS_VERSION_CONFLICT | typeof COMPASS_REVISION_NOT_FOUND;
16
+ /**
17
+ * What the failing request was about, for a refusal whose remedy is a command that names it. The
18
+ * command sets this before it sends, as it sets `requestFieldFlags`. The server's own body wins
19
+ * where it carries the same fact; these cover a server that does not send it yet.
20
+ */
21
+ export interface CompassRefusalContext {
22
+ compassId?: string;
23
+ agentId?: string;
24
+ /** The version the caller based the change on. */
25
+ expectedVersion?: number;
26
+ /** The version a revert asked for. */
27
+ toVersion?: number;
28
+ }
29
+ /** The Compass refusal an API error body carries, under `code` or the legacy `error`. */
30
+ export declare function compassRefusalCode(body: Record<string, unknown> | null | undefined): CompassRefusalCode | undefined;
31
+ /**
32
+ * The refusal as a sentence, then the command to run next on its own line (the shape
33
+ * `COMMITMENT_ACTIVATION_BLOCKED` uses: `→`).
34
+ */
35
+ export declare function describeCompassRefusal(code: CompassRefusalCode, body: Record<string, unknown> | null | undefined, context: CompassRefusalContext, bin: string): string;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /**
3
+ * compass-refusals.ts — the words for the two Compass refusals a person acts on.
4
+ *
5
+ * `COMPASS_VERSION_CONFLICT` (409) and `COMPASS_REVISION_NOT_FOUND` (404) used to reach the
6
+ * terminal as `HTTP_409` and `NOT_FOUND`, with the one fact that makes each actionable dropped:
7
+ * where the Compass is now, and which version was missing (OSK-21162, OSK-21152).
8
+ *
9
+ * The route sends the code in upper-case `error` (and now in `code` too). `refusalCodeOf` does
10
+ * not read the first as a code, and widening it for every route is not this module's call, so
11
+ * `compassRefusalCode` reads both spellings here, for these two codes only. No imports: this is
12
+ * pure, and `base-command` loads it.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.COMPASS_REVISION_NOT_FOUND = exports.COMPASS_VERSION_CONFLICT = void 0;
16
+ exports.compassRefusalCode = compassRefusalCode;
17
+ exports.describeCompassRefusal = describeCompassRefusal;
18
+ exports.COMPASS_VERSION_CONFLICT = 'COMPASS_VERSION_CONFLICT';
19
+ exports.COMPASS_REVISION_NOT_FOUND = 'COMPASS_REVISION_NOT_FOUND';
20
+ /** The Compass refusal an API error body carries, under `code` or the legacy `error`. */
21
+ function compassRefusalCode(body) {
22
+ for (const key of ['code', 'error']) {
23
+ const value = body?.[key];
24
+ if (value === exports.COMPASS_VERSION_CONFLICT || value === exports.COMPASS_REVISION_NOT_FOUND)
25
+ return value;
26
+ }
27
+ return undefined;
28
+ }
29
+ function versionOf(value) {
30
+ return typeof value === 'number' && Number.isInteger(value) ? value : undefined;
31
+ }
32
+ /**
33
+ * The refusal as a sentence, then the command to run next on its own line (the shape
34
+ * `COMMITMENT_ACTIVATION_BLOCKED` uses: `→`).
35
+ */
36
+ function describeCompassRefusal(code, body, context, bin) {
37
+ const compassId = context.compassId ?? '<compass-id>';
38
+ const agentId = context.agentId ?? '<agent-id>';
39
+ const current = versionOf(body?.currentVersion);
40
+ if (code === exports.COMPASS_VERSION_CONFLICT) {
41
+ const sent = versionOf(body?.expectedVersion) ?? context.expectedVersion;
42
+ const where = current !== undefined ? `it is at v${current} now` : 'it changed while this was being saved';
43
+ return (`Compass version conflict: ${where}${sent !== undefined ? `, and this change was based on v${sent}` : ''}.\n` +
44
+ ` → Re-read it with \`${bin} compass show ${compassId} --agent ${agentId}\`, review what changed, and retry.`);
45
+ }
46
+ const asked = versionOf(body?.toVersion) ?? context.toVersion;
47
+ return (`Compass revision ${asked !== undefined ? `v${asked} ` : ''}not found` +
48
+ `${current !== undefined ? `: the Compass is at v${current}` : ''}.\n` +
49
+ ` → List its versions with \`${bin} compass revisions ${compassId} --agent ${agentId}\`, then pass one as --to-version.`);
50
+ }
@@ -16,6 +16,21 @@ export interface CompassInvariantTarget {
16
16
  cadence?: Record<string, unknown>;
17
17
  escalation?: Record<string, unknown>;
18
18
  }
19
+ /** What a tactic write did, as the API reports it (`POST /api/compasses/:id/tactics`). */
20
+ export interface CompassTacticResult {
21
+ outcome: 'appended' | 'updated' | 'unchanged';
22
+ id: string;
23
+ description: string;
24
+ /** The invariant target the tactic lives on. */
25
+ targetInvariantId: string | null;
26
+ /** Tactics on that target after the write. */
27
+ count: number;
28
+ /** Tactics that rolled off the end to stay within the cap. */
29
+ dropped: Array<{
30
+ id: string;
31
+ description: string;
32
+ }>;
33
+ }
19
34
  export interface Compass {
20
35
  id: string;
21
36
  agentId: string;
@@ -41,14 +56,20 @@ export interface Compass {
41
56
  id: string | null;
42
57
  issues: string[];
43
58
  }>;
59
+ /** `compass revert` only: the enforced rules before and after, and which ids were removed. */
60
+ rulesChanged?: {
61
+ from: number;
62
+ to: number;
63
+ removed?: string[];
64
+ };
65
+ /** `compass revert` only: true when the version restored held the current content, so nothing was written. */
66
+ unchanged?: boolean;
44
67
  invariantTargets?: CompassInvariantTarget[];
45
68
  autonomyDefaults?: Record<string, unknown>;
46
69
  communicationDefaults?: Record<string, unknown>;
47
- learnedTactics?: Array<{
48
- id?: string;
49
- description?: string;
50
- }>;
51
70
  commitments?: Array<Record<string, unknown>>;
71
+ /** What `compass tactic` did; present only on that response. */
72
+ tactic?: CompassTacticResult;
52
73
  updatedBy?: string;
53
74
  createdAt?: string;
54
75
  updatedAt?: string;
@@ -65,6 +86,12 @@ export interface CompassRevision {
65
86
  createdAt?: string;
66
87
  previousConfig?: Record<string, unknown>;
67
88
  nextConfig?: Record<string, unknown>;
89
+ metadata?: {
90
+ autoRevert?: {
91
+ fromVersion?: number;
92
+ toVersion?: number;
93
+ };
94
+ } & Record<string, unknown>;
68
95
  }
69
96
  export interface CompassTraceEvent {
70
97
  id: string;
@@ -100,9 +127,13 @@ export declare const compassApi: {
100
127
  ok: boolean;
101
128
  id: string;
102
129
  status: string;
130
+ /** True when the compass was archived already and nothing was written. */
131
+ alreadyArchived?: boolean;
103
132
  }>;
104
133
  revisions: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<{
105
134
  data: CompassRevision[];
135
+ /** True when the ledger holds rows older than this page. */
136
+ has_more?: boolean;
106
137
  }>;
107
138
  trace: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<CompassTrace>;
108
139
  revert: (id: string, body: Record<string, unknown>) => Promise<Compass>;
@@ -112,6 +143,51 @@ export declare const compassApi: {
112
143
  }>;
113
144
  };
114
145
  export declare function renderCompassList(rows: Compass[], log: (line: string) => void): void;
146
+ /**
147
+ * One setting per line (`key: value`, nested objects as dotted keys, lists joined with `;`), so a
148
+ * settings object reads as labelled lines rather than one JSON blob (OSK-21150).
149
+ */
150
+ export declare function describeSettings(value: unknown, prefix?: string): string[];
151
+ /** A quiet-hours window in words: the hours and the zone, `off`, or `not set`. */
152
+ export declare function describeQuietHours(window: unknown): string;
153
+ /**
154
+ * The mode, and the word the author wrote when it differs from it. The server stores `auto` as
155
+ * `auto_run_limited`; saying so beats showing a value nobody typed as if they had (OSK-21150).
156
+ */
157
+ export declare function describeMode(mode: unknown, authoredMode: unknown): string;
115
158
  export declare function renderCompass(compass: Compass, log: (line: string) => void): void;
159
+ /**
160
+ * How many Commitments are bound to a Compass, and whether it governs them. A Commitment stays
161
+ * bound to an archived Compass but runs on defaults: the platform reads only an ACTIVE Compass's
162
+ * direction. "Governed by this compass: 1" printed the same words for both, on the Compass side,
163
+ * while the Commitment's own preflight said it was running on defaults (OSK-21242).
164
+ */
165
+ export declare function describeBoundCommitments(count: number, status: string | undefined): string;
166
+ /**
167
+ * What `compass tactic` says back. Three outcomes, each in its own words: it used to print
168
+ * "Appended" for all of them, with a count that was always 0, and name nothing that rolled off.
169
+ * A server that does not report `tactic` is not guessed at.
170
+ */
171
+ export declare function renderTacticResult(compass: Compass, log: (line: string) => void): void;
172
+ /**
173
+ * `compass effective-policy` in words (OSK-21150): the mode beside what was written when they
174
+ * differ, who chose it, the quiet hours that apply (or why none do), the communication defaults, and
175
+ * where each field came from. It used to print each of those as one JSON line, with a null mode and
176
+ * "derived" unexplained. `--json` is unchanged.
177
+ */
178
+ export declare function renderEffectivePolicy(policy: Record<string, unknown>, log: (line: string) => void): void;
116
179
  export declare function renderRevisionList(rows: CompassRevision[], log: (line: string) => void): void;
117
180
  export declare function renderCompassTrace(trace: CompassTrace, log: (line: string) => void): void;
181
+ /**
182
+ * The ledger's name for a row. A revert is stored as `auto_revert`, which reads as something the
183
+ * platform did on its own, so a person's `compass revert` is shown as `revert`, with the versions
184
+ * it moved between: `from v6 → v2` is the version it was at and the version whose content it took.
185
+ * Display only: the stored enum is unchanged.
186
+ */
187
+ export declare function revisionTypeLabel(row: CompassRevision): string;
188
+ /**
189
+ * What `compass revert` says after it ran. Three things the old one-liner left out: whether
190
+ * anything was written, that the status is whatever it was (a revert never changes lifecycle), and
191
+ * which enforced rules the restored version no longer carries.
192
+ */
193
+ export declare function renderRevertResult(compass: Compass, toVersion: number, agentId: string, log: (line: string) => void): void;