@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,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
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const base_command_1 = require("../../base-command");
5
5
  const compasses_1 = require("../../lib/compasses");
6
+ const update_1 = require("./update");
6
7
  /**
7
8
  * `skrr compass revert <compass-id> --agent <id> --to-version 3`
8
9
  *
@@ -10,12 +11,20 @@ const compasses_1 = require("../../lib/compasses");
10
11
  * The ledger therefore records that someone chose to go back, and versions 4–8
11
12
  * stay readable instead of vanishing. That is the difference between an audit
12
13
  * trail and an undo button.
14
+ *
15
+ * Like `update`, a revert is version-fenced: `--expected-version` pins the
16
+ * version the caller looked at, and without it the current version is read at
17
+ * the start and said so on stderr, so a concurrent edit is a named conflict
18
+ * rather than a surprise. A revert restores the configuration and keeps the
19
+ * compass's current status; it says when it wrote nothing and when it removed
20
+ * enforced rules.
13
21
  */
14
22
  class CompassRevert extends base_command_1.BaseCommand {
15
23
  static description = 'Restore an earlier compass version (as a NEW forward revision)';
16
24
  static examples = [
17
25
  '<%= config.bin %> compass revert agch_abc --agent agent_xyz --to-version 3',
18
26
  '<%= config.bin %> compass revert agch_abc --agent agent_xyz --to-version 3 --reason "new SLO was wrong"',
27
+ '<%= config.bin %> compass revert agch_abc --agent agent_xyz --to-version 3 --expected-version 9',
19
28
  ];
20
29
  static args = {
21
30
  compassId: core_1.Args.string({ ignoreStdin: true, description: 'Compass ID', required: true }),
@@ -29,15 +38,30 @@ class CompassRevert extends base_command_1.BaseCommand {
29
38
  min: 1,
30
39
  }),
31
40
  reason: core_1.Flags.string({ description: 'Why — recorded on the revision ledger' }),
41
+ 'expected-version': core_1.Flags.integer({
42
+ min: 1,
43
+ description: 'Compass version this revert is based on; it is refused if the compass moved. ' +
44
+ 'Defaults to the version read at the start of this command.',
45
+ }),
32
46
  };
33
47
  async run() {
34
48
  this.requireAuth();
35
49
  const { args, flags } = await this.parse(CompassRevert);
50
+ this.compassRef = {
51
+ compassId: args.compassId,
52
+ agentId: flags.agent,
53
+ toVersion: flags['to-version'],
54
+ };
36
55
  let compass;
37
56
  try {
57
+ const expected = await (0, update_1.resolveExpectedVersion)(flags['expected-version'], {}, async () => (await compasses_1.compassApi.get(args.compassId, { agentId: flags.agent })).version);
58
+ if (expected.source === 'current' && !flags.json) {
59
+ this.logToStderr(`No --expected-version given; reverting from the current version (v${String(expected.version)}).`);
60
+ }
38
61
  compass = await compasses_1.compassApi.revert(args.compassId, {
39
62
  agentId: flags.agent,
40
63
  toVersion: flags['to-version'],
64
+ expectedVersion: expected.version,
41
65
  ...(flags.reason ? { reason: flags.reason } : {}),
42
66
  });
43
67
  }
@@ -48,7 +72,7 @@ class CompassRevert extends base_command_1.BaseCommand {
48
72
  this.log(JSON.stringify(compass, null, 2));
49
73
  return;
50
74
  }
51
- this.log(`Restored v${flags['to-version']} as v${compass.version} — the intermediate versions stay in the ledger.`);
75
+ (0, compasses_1.renderRevertResult)(compass, flags['to-version'], flags.agent, (line) => this.log(line));
52
76
  this.log('');
53
77
  (0, compasses_1.renderCompass)(compass, (line) => this.log(line));
54
78
  }
@@ -1,4 +1,6 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /** What to say under a list the ledger had more rows for. */
3
+ export declare function revisionsFooter(shown: number): string;
2
4
  /**
3
5
  * `skrr compass revisions <compass-id> --agent <id>` — the append-only ledger.
4
6
  *
@@ -1,8 +1,15 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.revisionsFooter = revisionsFooter;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const compasses_1 = require("../../lib/compasses");
7
+ /** What to say under a list the ledger had more rows for. */
8
+ function revisionsFooter(shown) {
9
+ return shown >= 100
10
+ ? `Showing ${shown} of at least ${shown + 1} revisions. --limit is capped at 100, so older ones cannot be listed yet.`
11
+ : `Showing ${shown} of at least ${shown + 1} revisions — use --limit (up to 100) to see more.`;
12
+ }
6
13
  /**
7
14
  * `skrr compass revisions <compass-id> --agent <id>` — the append-only ledger.
8
15
  *
@@ -21,7 +28,11 @@ class CompassRevisions extends base_command_1.BaseCommand {
21
28
  static flags = {
22
29
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
23
30
  agent: core_1.Flags.string({ description: 'Agent ID that owns the compass', required: true }),
24
- limit: core_1.Flags.integer({ description: 'Max revisions (1..100)', min: 1, max: 100 }),
31
+ limit: core_1.Flags.integer({
32
+ description: 'Max revisions (1..100, default 20); newest first',
33
+ min: 1,
34
+ max: 100,
35
+ }),
25
36
  };
26
37
  async run() {
27
38
  this.requireAuth();
@@ -40,7 +51,12 @@ class CompassRevisions extends base_command_1.BaseCommand {
40
51
  this.log(JSON.stringify(response, null, 2));
41
52
  return;
42
53
  }
43
- (0, compasses_1.renderRevisionList)(response.data ?? [], (line) => this.log(line));
54
+ const rows = response.data ?? [];
55
+ (0, compasses_1.renderRevisionList)(rows, (line) => this.log(line));
56
+ // The API reads one row past the page, so `has_more` is true only when older rows exist. A
57
+ // 22-revision ledger used to list 20 rows and stop, with v1 (the create) silently missing.
58
+ if (response.has_more)
59
+ this.log(revisionsFooter(rows.length));
44
60
  }
45
61
  }
46
62
  exports.default = CompassRevisions;
@@ -2,9 +2,11 @@ import { BaseCommand } from '../../base-command';
2
2
  /**
3
3
  * `skrr compass tactic <compass-id> --agent <id> --description "..."`
4
4
  *
5
- * Appends to the bounded, rolling "what has worked before" list. It is a
6
- * separate verb from `update` on purpose: expressing an append as a full-object
7
- * write would let two concurrent learners clobber each other's entry.
5
+ * Appends to the bounded, rolling "what has worked before" list, or updates the
6
+ * entry that carries the same `--id`. It is a separate verb from `update` on
7
+ * purpose: expressing an append as a full-object write would let two concurrent
8
+ * learners clobber each other's entry. It says which of appended / updated /
9
+ * unchanged happened, and names any tactic that rolled off the end.
8
10
  */
9
11
  export default class CompassTactic extends BaseCommand {
10
12
  static description: string;
@@ -6,12 +6,14 @@ const compasses_1 = require("../../lib/compasses");
6
6
  /**
7
7
  * `skrr compass tactic <compass-id> --agent <id> --description "..."`
8
8
  *
9
- * Appends to the bounded, rolling "what has worked before" list. It is a
10
- * separate verb from `update` on purpose: expressing an append as a full-object
11
- * write would let two concurrent learners clobber each other's entry.
9
+ * Appends to the bounded, rolling "what has worked before" list, or updates the
10
+ * entry that carries the same `--id`. It is a separate verb from `update` on
11
+ * purpose: expressing an append as a full-object write would let two concurrent
12
+ * learners clobber each other's entry. It says which of appended / updated /
13
+ * unchanged happened, and names any tactic that rolled off the end.
12
14
  */
13
15
  class CompassTactic extends base_command_1.BaseCommand {
14
- static description = 'Append a learned tactic to a compass';
16
+ static description = 'Record a learned tactic on a compass (appends, or updates the tactic with the same --id)';
15
17
  static examples = [
16
18
  '<%= config.bin %> compass tactic agch_abc --agent agent_xyz --description "Roll back before paging on a deploy regression"',
17
19
  ];
@@ -23,7 +25,7 @@ class CompassTactic extends base_command_1.BaseCommand {
23
25
  agent: core_1.Flags.string({ description: 'Agent ID that owns the compass', required: true }),
24
26
  description: core_1.Flags.string({ description: 'The tactic, in one sentence', required: true }),
25
27
  id: core_1.Flags.string({
26
- description: 'Stable tactic ID (updates that entry instead of adding one)',
28
+ description: 'Stable tactic ID. A known ID with a different description updates that entry; with the same description nothing is written.',
27
29
  }),
28
30
  reason: core_1.Flags.string({ description: 'Why — recorded on the revision ledger' }),
29
31
  };
@@ -46,8 +48,7 @@ class CompassTactic extends base_command_1.BaseCommand {
46
48
  this.log(JSON.stringify(compass, null, 2));
47
49
  return;
48
50
  }
49
- const count = (compass.learnedTactics ?? []).length;
50
- this.log(`Appended. ${compass.id} now carries ${count} learned tactic(s) (v${compass.version}).`);
51
+ (0, compasses_1.renderTacticResult)(compass, (line) => this.log(line));
51
52
  }
52
53
  }
53
54
  exports.default = CompassTactic;
@@ -6,6 +6,7 @@ const base_command_1 = require("../../base-command");
6
6
  const prompt_1 = require("../../lib/prompt");
7
7
  const compasses_1 = require("../../lib/compasses");
8
8
  const web_url_1 = require("../../lib/web-url");
9
+ const compass_body_help_1 = require("../../lib/compass-body-help");
9
10
  /**
10
11
  * The server fences every compass write on the version the caller last saw
11
12
  * (`expectedVersion`, required), so a stale writer gets a conflict instead of
@@ -33,7 +34,11 @@ async function resolveExpectedVersion(explicit, fileBody, fetchCurrentVersion) {
33
34
  * winning for some domain — replacement makes the new set the whole answer.
34
35
  */
35
36
  class CompassUpdate extends base_command_1.BaseCommand {
36
- static description = 'Update a compass (bumps the version, writes a revision)';
37
+ static description = `Update a compass (bumps the version, writes a revision).
38
+
39
+ ${compass_body_help_1.COMPASS_BODY_HELP}
40
+
41
+ On update, invariantTargets (and --invariant) REPLACE the whole list, so send every target you want to keep; identity, scope, autonomyDefaults and communicationDefaults merge field by field onto what is stored; rules replace the list. An archived compass stays archived unless you pass --status active.`;
37
42
  static examples = [
38
43
  '<%= config.bin %> compass update agch_abc --agent agent_xyz --title "Reliability & cost"',
39
44
  '<%= config.bin %> compass update agch_abc --agent agent_xyz --invariant "p99 under 300ms" --reason "new SLO"',
@@ -50,7 +55,8 @@ class CompassUpdate extends base_command_1.BaseCommand {
50
55
  domain: core_1.Flags.string({ description: 'New domain' }),
51
56
  status: core_1.Flags.string({ description: 'New status', options: [...compasses_1.COMPASS_STATUSES] }),
52
57
  invariant: core_1.Flags.string({
53
- description: 'Invariant target description (repeatable; REPLACES the list)',
58
+ description: 'Invariant target description (repeatable, at most 25; REPLACES the list). ' +
59
+ 'Each gets the id invariant_<position> and no domainKind, so it drops the ids and domains of the targets it replaces: use --json-body to keep them.',
54
60
  multiple: true,
55
61
  }),
56
62
  reason: core_1.Flags.string({ description: 'Why — recorded on the revision ledger' }),
@@ -59,7 +65,9 @@ class CompassUpdate extends base_command_1.BaseCommand {
59
65
  description: 'Compass version this change is based on; the update is refused if it moved. ' +
60
66
  'Defaults to the version read at the start of this command.',
61
67
  }),
62
- 'json-body': core_1.Flags.string({ description: 'Path to a JSON file with the full body' }),
68
+ 'json-body': core_1.Flags.string({
69
+ description: 'Path to a JSON file with the body (merged under the flags). Field names and limits are listed in the description above.',
70
+ }),
63
71
  };
64
72
  async run() {
65
73
  this.requireAuth();
@@ -81,10 +89,14 @@ class CompassUpdate extends base_command_1.BaseCommand {
81
89
  if (Object.keys(body).length <= 1) {
82
90
  this.error('Nothing to update — pass at least one field flag or --json-body.');
83
91
  }
92
+ this.compassRef = { compassId: args.compassId, agentId: flags.agent };
84
93
  let compass;
85
94
  try {
86
95
  const expected = await resolveExpectedVersion(flags['expected-version'], fileBody, async () => (await compasses_1.compassApi.get(args.compassId, { agentId: flags.agent })).version);
87
96
  body.expectedVersion = expected.version;
97
+ if (typeof expected.version === 'number') {
98
+ this.compassRef = { ...this.compassRef, expectedVersion: expected.version };
99
+ }
88
100
  if (expected.source === 'current' && !flags.json) {
89
101
  this.logToStderr(`No --expected-version given; updating from the current version (v${String(expected.version)}).`);
90
102
  }
@@ -100,6 +112,10 @@ class CompassUpdate extends base_command_1.BaseCommand {
100
112
  return;
101
113
  }
102
114
  this.log(`Updated ${withContext.id} → v${withContext.version}.`);
115
+ // An edit keeps the status (docs: it never resurrects). Say so, or the edit looks live.
116
+ if (withContext.status === 'archived' && body.status !== 'archived') {
117
+ this.log('Compass is archived: this edit has no runtime effect until it is reactivated (`--status active`).');
118
+ }
103
119
  if (url)
104
120
  this.log(`View it at ${url}`);
105
121
  this.log('');
@@ -105,7 +105,7 @@ class FollowupsAct extends base_command_1.BaseCommand {
105
105
  this.log(producerNotice);
106
106
  const row = response.followup;
107
107
  if (response.reused) {
108
- this.log(`Already armed: ${row?.id} — the same act exists and was not duplicated.`);
108
+ this.log((0, followups_1.reusedWithoutChangeMessage)(row, 'the same act on this subject'));
109
109
  return;
110
110
  }
111
111
  if (response.dropped) {
@@ -30,6 +30,16 @@ class FollowupsCancel extends base_command_1.BaseCommand {
30
30
  async run() {
31
31
  this.requireAuth();
32
32
  const { args, flags } = await this.parse(FollowupsCancel);
33
+ // Best effort, and only to warn: cancelling the SLA backstop of a paired watch leaves that watch
34
+ // armed with no deadline, and the answer used to be the same bare "cancelled".
35
+ let wasSlaFallback = false;
36
+ try {
37
+ const before = await followups_1.followupsApi.show(args.id);
38
+ wasSlaFallback = Boolean(before?.followup?.slaFallback && !before.followup.terminal);
39
+ }
40
+ catch {
41
+ // The cancel below reports any real problem with the id.
42
+ }
33
43
  let result;
34
44
  try {
35
45
  result = (await followups_1.followupsApi.cancel(args.id, {
@@ -45,6 +55,9 @@ class FollowupsCancel extends base_command_1.BaseCommand {
45
55
  return;
46
56
  }
47
57
  this.log((0, followups_1.settledLine)(args.id, result, 'cancelled'));
58
+ if (wasSlaFallback) {
59
+ this.log('That was the SLA backstop of a paired watch. The watch itself is still armed and now has no deadline: cancel or resolve it too if you meant to end the wait.');
60
+ }
48
61
  }
49
62
  }
50
63
  exports.default = FollowupsCancel;
@@ -6,6 +6,23 @@ const format_1 = require("../../lib/format");
6
6
  const followups_1 = require("../../lib/followups");
7
7
  const agent_resolver_1 = require("../../lib/agent-resolver");
8
8
  const rate = (value) => value === null ? '—' : `${Math.round(value * 100)}%`;
9
+ /**
10
+ * What today's budget turned away. A creation refused or dropped makes no row, so
11
+ * this is the only place it shows (OSK-21209). `—` is "this kind has no budget",
12
+ * `?` is "the counter could not be read", `0` is a real zero.
13
+ */
14
+ const turnedAway = (budget) => {
15
+ if (!budget)
16
+ return '—';
17
+ const { droppedToday, refusedToday } = budget;
18
+ if (droppedToday == null || refusedToday == null)
19
+ return '?';
20
+ const parts = [
21
+ droppedToday > 0 ? `${droppedToday} dropped` : null,
22
+ refusedToday > 0 ? `${refusedToday} refused` : null,
23
+ ].filter(Boolean);
24
+ return parts.length > 0 ? parts.join(' · ') : '0';
25
+ };
9
26
  /** `resolved 3 · abandoned 1` — the outcomes that happened, not five zeros. */
10
27
  const outcomes = (terminal) => Object.entries(terminal)
11
28
  .filter(([, count]) => count > 0)
@@ -97,6 +114,7 @@ class FollowupsObservability extends base_command_1.BaseCommand {
97
114
  ? `$${kind.spend.today.toFixed(2)} / $${kind.spend.total.toFixed(2)}`
98
115
  : '—',
99
116
  budget: kind.budget ? `${kind.budget.used}/${kind.budget.limit ?? '∞'}` : '—',
117
+ turnedAway: turnedAway(kind.budget),
100
118
  silence: rate(kind.silenceRate),
101
119
  })), [
102
120
  { key: 'kind', header: 'KIND' },
@@ -109,10 +127,11 @@ class FollowupsObservability extends base_command_1.BaseCommand {
109
127
  { key: 'fires', header: 'FIRES' },
110
128
  { key: 'spend', header: 'SPENT TODAY/ALL' },
111
129
  { key: 'budget', header: 'TODAY/LIMIT' },
130
+ { key: 'turnedAway', header: 'TURNED AWAY' },
112
131
  { key: 'silence', header: 'SILENT' },
113
132
  ], (line) => this.log(line));
114
133
  });
115
- this.log(`\nAs of ${report.generatedAt ?? 'now'}. SPENT is the agent turns these follow-ups woke (USD, today / all time); TODAY/LIMIT is the daily creation budget (reminders are not metered); SILENT is how often a check-in chose to stay quiet.`);
134
+ this.log(`\nAs of ${report.generatedAt ?? 'now'}. SPENT is the agent turns these follow-ups woke (USD, today / all time); TODAY/LIMIT is the daily creation budget (reminders are not metered); TURNED AWAY is what that budget kept from being created today: dropped is a check-in the API answered with no error (the CLI prints one for you; an Agent's own check-in is not told), refused is a watch or act answered FOLLOWUP_BUDGET_EXCEEDED; SILENT is how often a check-in chose to stay quiet.`);
116
135
  }
117
136
  }
118
137
  exports.default = FollowupsObservability;
@@ -5,6 +5,7 @@ const base_command_1 = require("../../../base-command");
5
5
  const prompt_1 = require("../../../lib/prompt");
6
6
  const lookbackPlans_1 = require("../../../lib/lookbackPlans");
7
7
  const lookbackPlanHelp_1 = require("../../../lib/lookbackPlanHelp");
8
+ const followups_1 = require("../../../lib/followups");
8
9
  class LookbackPlansCreate extends base_command_1.BaseCommand {
9
10
  static description = 'Schedule a bounded plan of 1–10 lookbacks, each free to stay silent\n\n' + lookbackPlanHelp_1.LOOKBACK_PLAN_INPUT_HELP +
10
11
  '\n\nCreate also accepts {"agentId":"...","pinnedConversationId":"...","plan":{...}}. Source and delivery destination are separate; pin the original conversation to return there.';
@@ -27,7 +28,17 @@ class LookbackPlansCreate extends base_command_1.BaseCommand {
27
28
  }
28
29
  try {
29
30
  const result = await lookbackPlans_1.lookbackPlansApi.create(body);
30
- this.log(flags.json ? JSON.stringify(result, null, 2) : (0, lookbackPlans_1.lookbackArmingLine)(result));
31
+ if (flags.json) {
32
+ this.log(JSON.stringify(result, null, 2));
33
+ return;
34
+ }
35
+ // The four create commands say when the server recorded weaker provenance than the CLI
36
+ // claimed; a plan is recorded the same way (`producerDowngraded` is in the route's answer)
37
+ // and only `plans show` ever said so.
38
+ const notice = (0, followups_1.producerDowngradeNotice)(result);
39
+ if (notice)
40
+ this.log(notice);
41
+ this.log((0, lookbackPlans_1.lookbackArmingLine)(result));
31
42
  }
32
43
  catch (err) {
33
44
  if ((0, lookbackPlans_1.isLookbackMutationOutcomeUnknown)(err)) {
@@ -9,6 +9,7 @@ const GATE_LABEL = {
9
9
  exempt_user_directed: 'fires without approval (you asked for it)',
10
10
  exempt_system: 'fires without approval (system)',
11
11
  user_directed_guarded: 'fires without approval (you asked for it); still held by quiet hours, approval-only action classes and the spend cap',
12
+ exempt_notification: 'delivered to you as a notification (no agent turn, so no approval step applies)',
12
13
  autonomy_ladder: "follows the agent's autonomy mode",
13
14
  approval_ladder: 'needs approval unless the autonomy mode allows it',
14
15
  };
@@ -49,6 +50,12 @@ class FollowupsShow extends base_command_1.BaseCommand {
49
50
  'settled',
50
51
  row.terminal ? `${row.terminalAt ?? '—'} by ${row.terminalBy ?? 'unknown'}` : undefined,
51
52
  ],
53
+ [
54
+ 'role',
55
+ row.slaFallback
56
+ ? 'the SLA backstop of a paired watch, not a second watch: it escalates that watch when its wait outlives the deadline, and cancelling it leaves the watch without one'
57
+ : undefined,
58
+ ],
52
59
  ['deadline', row.deadlineAt],
53
60
  [
54
61
  'waiting',
@@ -56,6 +63,12 @@ class FollowupsShow extends base_command_1.BaseCommand {
56
63
  ? `on an offline machine since ${row.parkedAt} (escalates after 60 minutes)`
57
64
  : undefined,
58
65
  ],
66
+ [
67
+ 'held',
68
+ row.held && !row.terminal
69
+ ? `moved to ${row.held.until} — ${row.held.note ?? row.held.reason ?? 'it could not run when it came due'}`
70
+ : undefined,
71
+ ],
59
72
  ['escalated to', row.escalatedInitiativeId],
60
73
  ]) {
61
74
  if (value)
@@ -242,7 +242,7 @@ class FollowupsWatch extends base_command_1.BaseCommand {
242
242
  this.log(producerNotice);
243
243
  const row = response.followup;
244
244
  if (response.reused) {
245
- this.log(`Already armed: ${row?.id} — an identical watch exists and was not duplicated.`);
245
+ this.log((0, followups_1.reusedWithoutChangeMessage)(row, 'an identical watch'));
246
246
  return;
247
247
  }
248
248
  if (response.dropped) {
@@ -35,6 +35,17 @@ export type GoalPlannerView = {
35
35
  * choose between those two, because the CLI cannot tell them apart.
36
36
  */
37
37
  tickLockedAt: string | null;
38
+ /**
39
+ * Why an armed planner will not tick, or null. The scheduler only creates the planner's trigger
40
+ * for an ACTIVE, unarchived Goal (`GoalPlanner.shouldScheduleGoal`); for any other status the
41
+ * cadence is stored and `nextTickAt` is set, but nothing ever fires. Stating the cadence alone
42
+ * read as "armed" (dogfooded: a `planned` Goal showed `nextTickAt` in the past for good).
43
+ */
44
+ notRunning: {
45
+ goalId: string | null;
46
+ reason: 'archived' | 'status';
47
+ status: string | null;
48
+ } | null;
38
49
  };
39
50
  /** Read the planner state off a Goal document (lean or typed). */
40
51
  export declare function goalPlannerView(goal: Record<string, unknown> | undefined | null): GoalPlannerView;
@@ -13,6 +13,14 @@ function goalPlannerView(goal) {
13
13
  const g = (goal ?? {});
14
14
  const tickIntervalMs = typeof g.tickIntervalMs === 'number' ? g.tickIntervalMs : null;
15
15
  const enabled = tickIntervalMs != null;
16
+ const status = str(g.status);
17
+ const notRunning = !enabled
18
+ ? null
19
+ : g.archivedAt
20
+ ? { goalId: str(g.id), reason: 'archived', status }
21
+ : status && status !== 'active'
22
+ ? { goalId: str(g.id), reason: 'status', status }
23
+ : null;
16
24
  return {
17
25
  enabled,
18
26
  tickIntervalMs,
@@ -21,6 +29,7 @@ function goalPlannerView(goal) {
21
29
  nextTickAt: enabled ? str(g.nextTickAt) : null,
22
30
  lastTickedAt: str(g.lastTickedAt),
23
31
  tickLockedAt: str(g.tickLockedAt),
32
+ notRunning,
24
33
  };
25
34
  }
26
35
  /**
@@ -44,6 +53,13 @@ function renderGoalPlannerLines(view) {
44
53
  lines.push(` nextTickAt: ${view.nextTickAt}`);
45
54
  if (view.lastTickedAt)
46
55
  lines.push(` lastTickedAt: ${view.lastTickedAt}`);
56
+ if (view.notRunning) {
57
+ const { goalId, reason, status } = view.notRunning;
58
+ lines.push(reason === 'archived'
59
+ ? ' not running: this Goal is archived, and the planner only ticks for an active Goal'
60
+ : ` not running: this Goal is ${status}, and the planner only ticks for an active Goal` +
61
+ ` — \`skrr goals update ${goalId ?? '<goal-id>'} --status active\``);
62
+ }
47
63
  if (view.tickLockedAt) {
48
64
  lines.push(` tickLockedAt: ${view.tickLockedAt} (a claimed tick has not released this lock)`);
49
65
  }
@@ -1,11 +1,11 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  /**
3
- * Rename / describe / tag a machine.
3
+ * Rename / describe / tag a machine, and choose its icon.
4
4
  *
5
5
  * `preferences` is the ONLY user-editable part of a machine — identities,
6
6
  * capabilities and lifecycle stay authoritative in the daemon, harness and
7
- * lease records this aggregate reads. The name set here is yours: it does not
8
- * rename the machine for anyone else.
7
+ * lease records this aggregate reads. The name and icon set here are yours: they
8
+ * do not rename or re-draw the machine for anyone else.
9
9
  */
10
10
  export default class MachinesUpdate extends BaseCommand {
11
11
  static description: string;
@@ -19,6 +19,8 @@ export default class MachinesUpdate extends BaseCommand {
19
19
  description: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
20
20
  tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
21
21
  'clear-tags': import("@oclif/core/interfaces").BooleanFlag<boolean>;
22
+ icon: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ 'icon-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
22
24
  };
23
25
  run(): Promise<void>;
24
26
  }