@skrr-ai/cli 0.1.129 → 0.1.130

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 (37) hide show
  1. package/dist/base-command.js +30 -0
  2. package/dist/commands/agenda/trigger/create.js +9 -1
  3. package/dist/commands/agents/reflect.d.ts +6 -0
  4. package/dist/commands/agents/reflect.js +45 -6
  5. package/dist/commands/agents/schedule-overview.d.ts +17 -0
  6. package/dist/commands/agents/schedule-overview.js +33 -9
  7. package/dist/commands/agents/triggers/policy.js +3 -1
  8. package/dist/commands/northstars/create.js +6 -1
  9. package/dist/commands/northstars/list.js +1 -1
  10. package/dist/commands/northstars/show.js +1 -1
  11. package/dist/commands/spaces/heartbeats/status.js +10 -0
  12. package/dist/commands/tasks/archive.js +6 -0
  13. package/dist/commands/tasks/pause-schedule.js +1 -1
  14. package/dist/commands/tasks/resume-schedule.js +4 -1
  15. package/dist/commands/tasks/schedule.js +6 -1
  16. package/dist/commands/tasks/self-schedule.d.ts +56 -0
  17. package/dist/commands/tasks/self-schedule.js +179 -21
  18. package/dist/lib/northstars.d.ts +44 -4
  19. package/dist/lib/northstars.js +108 -92
  20. package/dist/lib/reflection.d.ts +58 -0
  21. package/dist/lib/reflection.js +115 -6
  22. package/dist/lib/task-operations.js +2 -0
  23. package/dist/lib/task-schedule-notes.d.ts +46 -0
  24. package/dist/lib/task-schedule-notes.js +71 -0
  25. package/dist/lib/triggers.d.ts +18 -0
  26. package/dist/lib/triggers.js +16 -6
  27. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
  28. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
  29. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  30. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +2 -0
  31. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
  32. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
  33. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  34. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  35. package/dist/node_modules/@skrr-ai/data-provider/index.js +840 -826
  36. package/oclif.manifest.json +20 -12
  37. package/package.json +1 -1
@@ -1617,6 +1617,21 @@ class BaseCommand extends core_1.Command {
1617
1617
  details: envelopeDetails(body),
1618
1618
  });
1619
1619
  }
1620
+ else if (commitmentBodyErrorCode(body)) {
1621
+ // These Commitment routes send the code in upper-case `error`, which `refusalCodeOf` does not
1622
+ // read as a code, so the documented code (the guide's refusal table) never reached `error.code`:
1623
+ // a program saw `HTTP_400` or `HTTP_409` and had to know to look in `details.error` (OSK-21102).
1624
+ // Read them by name, as the activation and Compass refusals are, rather than widening
1625
+ // `refusalCodeOf` for every route. The message is the server's, unchanged.
1626
+ this.failWithCliError({
1627
+ message: say(bodyMessage || e.message),
1628
+ code: commitmentBodyErrorCode(body),
1629
+ status: e.status,
1630
+ exit: 1,
1631
+ retryable: false,
1632
+ details: envelopeDetails(body),
1633
+ });
1634
+ }
1620
1635
  else if ((e.status === 409 || e.status === 404) && (0, compass_refusals_1.compassRefusalCode)(body)) {
1621
1636
  // The Compass routes sent the code in upper-case `error`, which `refusalCodeOf` does not
1622
1637
  // read as a code, so a stale writer saw `HTTP_409` and a mistyped version `NOT_FOUND`,
@@ -2309,6 +2324,21 @@ function apiRefusal(err) {
2309
2324
  }
2310
2325
  /** The typed code the server refuses a Commitment activation with. */
2311
2326
  const COMMITMENT_ACTIVATION_BLOCKED = 'COMMITMENT_ACTIVATION_BLOCKED';
2327
+ /**
2328
+ * Commitment refusals the routes send in upper-case `error` (OSK-21102): the retired `mode` and
2329
+ * `policy.autonomyMode` fields, a cadence window no single wall-clock pattern holds, and an edit that
2330
+ * tries to activate a draft (which only `resume` may do).
2331
+ */
2332
+ const COMMITMENT_BODY_ERROR_CODES = new Set([
2333
+ 'COMMITMENT_MODE_RETIRED',
2334
+ 'COMMITMENT_POLICY_RETIRED',
2335
+ 'COMMITMENT_CADENCE_WINDOW_UNSUPPORTED',
2336
+ 'COMMITMENT_RESUME_REQUIRES_EXPLICIT_ACTION',
2337
+ ]);
2338
+ function commitmentBodyErrorCode(body) {
2339
+ const value = stringProp(body, 'error');
2340
+ return value && COMMITMENT_BODY_ERROR_CODES.has(value) ? value : undefined;
2341
+ }
2312
2342
  /**
2313
2343
  * The remedy a refused Commitment activation carries.
2314
2344
  *
@@ -48,7 +48,9 @@ class AgendaTriggerCreate extends base_command_1.BaseCommand {
48
48
  description: 'IANA timezone for the cron (defaults to UTC). Only used with --kind=schedule.',
49
49
  }),
50
50
  'delay-minutes': core_1.Flags.integer({
51
- description: 'Minutes from now to fire — required when --kind=self_scheduled. Range: 5..43200 (5min..30d).',
51
+ description: 'Minutes from now to fire — required when --kind=self_scheduled. ' +
52
+ `Range: ${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MIN_MINUTES}..${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MAX_MINUTES} ` +
53
+ `(${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MIN_MINUTES} minutes..24 hours).`,
52
54
  }),
53
55
  'max-fires-per-day': core_1.Flags.integer({
54
56
  description: 'Daily cap on fires. Required for self_scheduled (1..96); defaults to 96 for schedule when omitted.',
@@ -91,6 +93,12 @@ class AgendaTriggerCreate extends base_command_1.BaseCommand {
91
93
  if (typeof flags['delay-minutes'] !== 'number') {
92
94
  this.error('--delay-minutes is required when --kind=self_scheduled', { exit: 2 });
93
95
  }
96
+ // The server holds the same range, but its refusal arrives after the round trip. The sentence
97
+ // is data-provider's, so what is said here and what the route says cannot differ (OSK-21500).
98
+ const delayRefusal = (0, data_provider_1.describeAgendaDelayMinutesRefusal)(flags['delay-minutes']);
99
+ if (delayRefusal) {
100
+ this.error(`--delay-minutes: ${delayRefusal}`, { exit: 2 });
101
+ }
94
102
  if (typeof flags['max-fires-per-day'] !== 'number') {
95
103
  this.error('--max-fires-per-day is required when --kind=self_scheduled', { exit: 2 });
96
104
  }
@@ -32,5 +32,11 @@ export default class AgentsReflect extends BaseCommand {
32
32
  run(): Promise<void>;
33
33
  private runDisable;
34
34
  private runEnableOrRetune;
35
+ /**
36
+ * Parse `--set-cadence` and apply the reflection window. A cadence that is not one
37
+ * (`0`, `1 month`, empty) is a usage error naming the rule, raised before anything is
38
+ * written, not an API failure.
39
+ */
40
+ private boundRequestedCadence;
35
41
  private runStatus;
36
42
  }
@@ -55,12 +55,16 @@ class AgentsReflect extends base_command_1.BaseCommand {
55
55
  this.requireAuth();
56
56
  const { args, flags } = await this.parse(AgentsReflect);
57
57
  const agentId = args.id;
58
+ // Only the status view prints whether the loop's fires run, so only it asks the server for
59
+ // that verdict (a policy read); enabling, retuning and pausing do not.
60
+ const isStatus = !flags.disable && !flags.enable && flags['set-cadence'] === undefined;
58
61
  let existing;
59
62
  try {
60
63
  const list = await triggers_1.triggerApi.list((0, triggers_1.withQuery)('/api/triggers', {
61
64
  scope: 'agent',
62
65
  refEntity: (0, triggers_1.agentRefEntity)(agentId),
63
66
  limit: 200,
67
+ include: isStatus ? 'autonomy' : undefined,
64
68
  }));
65
69
  existing = (0, reflection_1.findReflectionTrigger)(list.triggers ?? []);
66
70
  }
@@ -103,14 +107,23 @@ class AgentsReflect extends base_command_1.BaseCommand {
103
107
  // Resolve the target cadence: explicit --set-cadence, else keep the existing
104
108
  // interval when just --enabling, else the default.
105
109
  let minutes;
110
+ let bounds = null;
106
111
  if (flags['set-cadence'] !== undefined) {
107
- minutes = (0, reflection_1.clampCadenceMinutes)((0, reflection_1.parseCadenceToMinutes)(flags['set-cadence']));
112
+ bounds = this.boundRequestedCadence(flags['set-cadence']);
113
+ minutes = bounds.minutes;
108
114
  }
109
115
  else {
110
116
  minutes =
111
117
  (existing && (0, reflection_1.reflectionCadenceMinutes)(existing)) || reflection_1.DEFAULT_REFLECTION_CADENCE_MINUTES;
112
118
  }
113
119
  const cadence = (0, reflection_1.describeCadenceMinutes)(minutes);
120
+ // What was ASKED for, beside what was applied: a request the window moved is said so
121
+ // (OSK-21493), in the text and in `--json`, instead of only the clamped number.
122
+ const clampNote = bounds ? (0, reflection_1.describeCadenceClamp)(bounds) : null;
123
+ const requested = {
124
+ requestedCadence: bounds ? (0, reflection_1.describeCadenceMinutes)(bounds.requestedMinutes) : null,
125
+ ...(clampNote ? { clamped: true, note: clampNote } : {}),
126
+ };
114
127
  if (existing?.id) {
115
128
  const patch = {
116
129
  enabled: true,
@@ -124,10 +137,12 @@ class AgentsReflect extends base_command_1.BaseCommand {
124
137
  }
125
138
  const updated = await triggers_1.triggerApi.update(existing.id, patch);
126
139
  if (flags.json) {
127
- this.log(JSON.stringify({ ok: true, agentId, action: 'retuned', cadence, trigger: updated }, null, 2));
140
+ this.log(JSON.stringify({ ok: true, agentId, action: 'retuned', ...requested, cadence, trigger: updated }, null, 2));
128
141
  return;
129
142
  }
130
143
  this.log(`Self-reflection loop retuned — agent ${agentId} now reflects every ${cadence}.`);
144
+ if (clampNote)
145
+ this.log(`Note: ${clampNote}`);
131
146
  this.log(`Trigger: ${existing.id}`);
132
147
  return;
133
148
  }
@@ -138,12 +153,30 @@ class AgentsReflect extends base_command_1.BaseCommand {
138
153
  });
139
154
  const created = await triggers_1.triggerApi.create(payload);
140
155
  if (flags.json) {
141
- this.log(JSON.stringify({ ok: true, agentId, action: 'enabled', cadence, trigger: created }, null, 2));
156
+ this.log(JSON.stringify({ ok: true, agentId, action: 'enabled', ...requested, cadence, trigger: created }, null, 2));
142
157
  return;
143
158
  }
144
159
  this.log(`Self-reflection loop enabled — agent ${agentId} will reflect every ${cadence}.`);
160
+ if (clampNote)
161
+ this.log(`Note: ${clampNote}`);
145
162
  this.log(`Trigger: ${created.id ?? '(created)'}`);
146
- this.log(`Next run: ${(0, triggers_1.nextRunLabel)(created)}`);
163
+ this.log(`Next run: ${(0, reflection_1.reflectionNextRunLabel)(created)}`);
164
+ }
165
+ /**
166
+ * Parse `--set-cadence` and apply the reflection window. A cadence that is not one
167
+ * (`0`, `1 month`, empty) is a usage error naming the rule, raised before anything is
168
+ * written, not an API failure.
169
+ */
170
+ boundRequestedCadence(raw) {
171
+ try {
172
+ return (0, reflection_1.applyCadenceBounds)((0, reflection_1.parseCadenceToMinutes)(raw));
173
+ }
174
+ catch (err) {
175
+ if (err instanceof reflection_1.InvalidCadenceError) {
176
+ this.failWithCliError({ message: err.message, code: err.code, exit: 2, retryable: false });
177
+ }
178
+ throw err;
179
+ }
147
180
  }
148
181
  runStatus(agentId, existing, json) {
149
182
  if (!existing) {
@@ -165,7 +198,8 @@ class AgentsReflect extends base_command_1.BaseCommand {
165
198
  enabled: existing.enabled !== false,
166
199
  cadence,
167
200
  cadenceMinutes: minutes ?? null,
168
- nextRun: (0, triggers_1.nextRunLabel)(existing),
201
+ nextRun: (0, reflection_1.reflectionNextRunLabel)(existing),
202
+ ...(0, reflection_1.reflectionAutonomyFields)(existing),
169
203
  trigger: existing,
170
204
  }, null, 2));
171
205
  return;
@@ -173,8 +207,13 @@ class AgentsReflect extends base_command_1.BaseCommand {
173
207
  this.log(`Self-reflection loop for agent ${agentId}:`);
174
208
  this.log(` Status: ${existing.enabled === false ? 'paused' : 'active'}`);
175
209
  this.log(` Cadence: every ${cadence}`);
176
- this.log(` Next run: ${(0, triggers_1.nextRunLabel)(existing)}`);
210
+ this.log(` Next run: ${(0, reflection_1.reflectionNextRunLabel)(existing)}`);
177
211
  this.log(` Trigger: ${existing.id ?? '-'}`);
212
+ // "active" with a next run says the loop is armed, not that its fires run: under some
213
+ // initiative modes they only create an inbox suggestion (OSK-21606).
214
+ const autonomyNote = (0, reflection_1.reflectionAutonomyNote)(existing, this.config.bin);
215
+ if (autonomyNote)
216
+ this.log(` Note: ${autonomyNote}`);
178
217
  if (existing.enabled === false) {
179
218
  this.log('');
180
219
  this.log('Re-enable it with: ' + `${this.config.bin} agents reflect ${agentId} --enable`);
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ import { type TriggerDefinition } from '../../lib/triggers';
2
3
  /**
3
4
  * `skrr agents schedule-overview <agent-id>` — the one-call "full visibility"
4
5
  * view an agent needs when it wakes to think: PAST (recent fires) + FUTURE
@@ -25,3 +26,19 @@ export default class AgentsScheduleOverview extends BaseCommand {
25
26
  };
26
27
  run(): Promise<void>;
27
28
  }
29
+ /** The reflection loop as `--json` reports it. A paused loop has no next run (`-`). */
30
+ export declare function reflectionSummary(reflection: TriggerDefinition): {
31
+ autonomy?: "runs" | "suggests_only";
32
+ note?: string;
33
+ triggerId: string | undefined;
34
+ enabled: boolean;
35
+ cadence: string;
36
+ nextRun: string;
37
+ };
38
+ /** The reflection loop as the text view prints it. */
39
+ export declare function reflectionLine(reflection: TriggerDefinition): string;
40
+ /**
41
+ * The line under the loop when the server says its fires do not run for the owner (OSK-21606),
42
+ * else null. "active · next <time>" only says the loop is armed.
43
+ */
44
+ export declare function reflectionNoteLine(reflection: TriggerDefinition, bin: string): string | null;
@@ -1,5 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.reflectionSummary = reflectionSummary;
4
+ exports.reflectionLine = reflectionLine;
5
+ exports.reflectionNoteLine = reflectionNoteLine;
3
6
  const core_1 = require("@oclif/core");
4
7
  const data_provider_1 = require("@skrr-ai/data-provider");
5
8
  const base_command_1 = require("../../base-command");
@@ -51,6 +54,8 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
51
54
  scope: 'agent',
52
55
  refEntity: (0, triggers_1.agentRefEntity)(agentId),
53
56
  limit: 200,
57
+ // Whether the reflection loop's fires run: its row carries the verdict (OSK-21606).
58
+ include: 'autonomy',
54
59
  })),
55
60
  // Filter SERVER-side. Fetching an unfiltered page and filtering in memory
56
61
  // silently truncates the section for any user with more tasks than
@@ -72,14 +77,7 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
72
77
  if (flags.json) {
73
78
  this.log(JSON.stringify({
74
79
  agentId,
75
- reflection: reflection
76
- ? {
77
- triggerId: reflection.id,
78
- enabled: reflection.enabled !== false,
79
- cadence: describeCadence(reflection),
80
- nextRun: (0, triggers_1.nextRunLabel)(reflection),
81
- }
82
- : null,
80
+ reflection: reflection ? reflectionSummary(reflection) : null,
83
81
  upcoming,
84
82
  recentFires: fires,
85
83
  openTasks,
@@ -97,7 +95,10 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
97
95
  this.log(` none — enable with: ${this.config.bin} agents reflect ${agentId} --set-cadence 6h`);
98
96
  }
99
97
  else {
100
- this.log(` ${reflection.enabled === false ? 'paused' : 'active'} · every ${describeCadence(reflection)} · next ${(0, triggers_1.nextRunLabel)(reflection)} · ${reflection.id ?? '-'}`);
98
+ this.log(reflectionLine(reflection));
99
+ const note = reflectionNoteLine(reflection, this.config.bin);
100
+ if (note)
101
+ this.log(note);
101
102
  }
102
103
  // FUTURE.
103
104
  this.log('');
@@ -190,6 +191,29 @@ function describeCadence(trigger) {
190
191
  const minutes = (0, reflection_1.reflectionCadenceMinutes)(trigger);
191
192
  return minutes ? (0, reflection_1.describeCadenceMinutes)(minutes) : 'unknown';
192
193
  }
194
+ /** The reflection loop as `--json` reports it. A paused loop has no next run (`-`). */
195
+ function reflectionSummary(reflection) {
196
+ return {
197
+ triggerId: reflection.id,
198
+ enabled: reflection.enabled !== false,
199
+ cadence: describeCadence(reflection),
200
+ nextRun: (0, reflection_1.reflectionNextRunLabel)(reflection),
201
+ // Whether its fires run, when the server said; absent means it did not.
202
+ ...(0, reflection_1.reflectionAutonomyFields)(reflection),
203
+ };
204
+ }
205
+ /** The reflection loop as the text view prints it. */
206
+ function reflectionLine(reflection) {
207
+ return ` ${reflection.enabled === false ? 'paused' : 'active'} · every ${describeCadence(reflection)} · next ${(0, reflection_1.reflectionNextRunLabel)(reflection)} · ${reflection.id ?? '-'}`;
208
+ }
209
+ /**
210
+ * The line under the loop when the server says its fires do not run for the owner (OSK-21606),
211
+ * else null. "active · next <time>" only says the loop is armed.
212
+ */
213
+ function reflectionNoteLine(reflection, bin) {
214
+ const note = (0, reflection_1.reflectionAutonomyNote)(reflection, bin);
215
+ return note ? ` Note: ${note}` : null;
216
+ }
193
217
  function settledValue(result) {
194
218
  return result.status === 'fulfilled' ? result.value : undefined;
195
219
  }
@@ -56,7 +56,9 @@ class AgentsTriggersPolicy extends base_command_1.BaseCommand {
56
56
  description: 'Allowed trigger kind under auto_run_limited (repeatable or comma-separated)',
57
57
  multiple: true,
58
58
  }),
59
- 'max-fires-per-day': core_1.Flags.integer({ description: 'Agent initiative daily fire cap' }),
59
+ 'max-fires-per-day': core_1.Flags.integer({
60
+ description: 'Daily cap on how often this agent can wake itself: its Task returns and self_scheduled triggers, whatever cap they carry',
61
+ }),
60
62
  'max-per-hour': core_1.Flags.integer({ description: 'Agent initiative hourly fire cap' }),
61
63
  'working-hours': core_1.Flags.string({ description: 'Policy workingHours JSON object' }),
62
64
  'quiet-hours': core_1.Flags.string({ description: 'Policy quietHours JSON object' }),
@@ -15,10 +15,11 @@ const HOUR_MS = 60 * 60 * 1000;
15
15
  * `skrr northstars governance configure`.
16
16
  */
17
17
  class NorthstarsCreate extends base_command_1.BaseCommand {
18
- static description = 'Create a northstar (the portfolio layer above commitments)';
18
+ static description = `Create a northstar (the portfolio layer above commitments)\n\n${northstars_1.NORTHSTAR_WORKSPACE_CREATE_HELP}`;
19
19
  static examples = [
20
20
  '<%= config.bin %> northstars create --title "Reach $10M ARR"',
21
21
  '<%= config.bin %> northstars create --title "Reliability" --tier critical --max-active 5 --review-interval-hours 24',
22
+ '<%= config.bin %> northstars create --title "Reliability" --workspace <workspace-id>',
22
23
  ];
23
24
  static flags = {
24
25
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
@@ -98,6 +99,10 @@ class NorthstarsCreate extends base_command_1.BaseCommand {
98
99
  }
99
100
  this.log(`Created ${created.id} — ${created.title} (${created.status})`);
100
101
  this.log(`URL: ${created.url ?? '-'}`);
102
+ this.log((0, northstars_1.northstarWorkspaceLine)(created));
103
+ const workspaceNote = (0, northstars_1.createdNorthstarNote)(created);
104
+ if (workspaceNote)
105
+ this.log(workspaceNote);
101
106
  if (flags['max-active'] === 0)
102
107
  this.log('Attention max active 0 (no cap)');
103
108
  this.log(flags['review-interval-hours'] !== undefined
@@ -12,7 +12,7 @@ const northstars_1 = require("../../lib/northstars");
12
12
  * their health on a cadence.
13
13
  */
14
14
  class NorthstarsList extends base_command_1.BaseCommand {
15
- static description = 'List northstars (the portfolio layer above commitments)';
15
+ static description = `List northstars (the portfolio layer above commitments)\n\nThe WORKSPACE column is a shortened workspace id (--json has the full workspaceId) or "personal". ${northstars_1.NORTHSTAR_WORKSPACE_READ_HELP}`;
16
16
  static examples = [
17
17
  '<%= config.bin %> northstars list',
18
18
  '<%= config.bin %> northstars list --status active',
@@ -27,7 +27,7 @@ function normalizeNorthstarShowResponse(status) {
27
27
  * asking of a portfolio, which is what is actually in it.
28
28
  */
29
29
  class NorthstarsShow extends base_command_1.BaseCommand {
30
- static description = 'Show a northstar, its linked commitments, and open recommendations';
30
+ static description = `Show a northstar, its linked commitments, and open recommendations\n\nThe Workspace line is the full workspace id, or "personal". ${northstars_1.NORTHSTAR_WORKSPACE_READ_HELP}`;
31
31
  static examples = [
32
32
  '<%= config.bin %> northstars show nst_abc123',
33
33
  '<%= config.bin %> northstars show nst_abc123 --json',
@@ -49,6 +49,16 @@ class SpacesHeartbeatsStatus extends base_command_1.BaseCommand {
49
49
  this.log(`Running: ${s.isRunning === true ? 'yes' : 'no'}`);
50
50
  this.log(`Next Run: ${s.nextRunAt ? (0, format_1.localTime)(s.nextRunAt) : '-'}`);
51
51
  this.log(`BullMQ Job Key: ${s.bullmqJobKey ?? '-'}`);
52
+ // The newest fire and what became of it. `Next Run` moves whether or not a fire does anything,
53
+ // so a heartbeat the agent's policy skips on every fire would otherwise read as healthy.
54
+ const fire = s.lastFire;
55
+ if (fire && typeof fire === 'object') {
56
+ const what = [fire.outcome, fire.reason].filter(Boolean).join(': ');
57
+ this.log(`Last Fire: ${fire.firedAt ? (0, format_1.localTime)(fire.firedAt) : '-'} ${what || '-'}`);
58
+ if (fire.outcome === 'skipped') {
59
+ this.log('The last fire did nothing: the heartbeat was skipped, not run. `skrr triggers audit` lists every fire.');
60
+ }
61
+ }
52
62
  }
53
63
  }
54
64
  exports.default = SpacesHeartbeatsStatus;
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const web_url_1 = require("../../lib/web-url");
7
+ const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
7
8
  class TasksArchive extends base_command_1.BaseCommand {
8
9
  static description = 'Archive a task (reversible — use --unarchive to restore)';
9
10
  static examples = [
@@ -37,6 +38,11 @@ class TasksArchive extends base_command_1.BaseCommand {
37
38
  }
38
39
  const action = flags.unarchive ? 'unarchived' : 'archived';
39
40
  this.log(`Task ${args.id} ${action}`);
41
+ const lines = flags.unarchive
42
+ ? (0, task_schedule_notes_1.unarchiveResultLines)(result, String(args.id), this.config.bin)
43
+ : (0, task_schedule_notes_1.archiveResultLines)(result);
44
+ for (const line of lines)
45
+ this.log(line);
40
46
  }
41
47
  }
42
48
  exports.default = TasksArchive;
@@ -5,7 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const web_url_1 = require("../../lib/web-url");
7
7
  class TasksPauseSchedule extends base_command_1.BaseCommand {
8
- static description = 'Pause future recurring runs; current runs and already queued work are unchanged';
8
+ static description = 'Pause future recurring runs; current runs and already queued work are unchanged. A run that comes due while paused is skipped, not replayed, when you resume';
9
9
  static examples = [
10
10
  '<%= config.bin %> tasks pause-schedule <task-id>',
11
11
  '<%= config.bin %> tasks pause-schedule <task-id> --json',
@@ -4,8 +4,9 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const web_url_1 = require("../../lib/web-url");
7
+ const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
7
8
  class TasksResumeSchedule extends base_command_1.BaseCommand {
8
- static description = 'Resume a paused recurring task — server re-computes the next fire from the cron pattern';
9
+ static description = 'Resume a paused recurring task. A run that came due while it was paused is skipped (not replayed); the next run comes from the cron pattern';
9
10
  static examples = [
10
11
  '<%= config.bin %> tasks resume-schedule <task-id>',
11
12
  '<%= config.bin %> tasks resume-schedule <task-id> --json',
@@ -34,6 +35,8 @@ class TasksResumeSchedule extends base_command_1.BaseCommand {
34
35
  const t = result;
35
36
  const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
36
37
  this.log(`Resumed schedule on task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''}`);
38
+ for (const line of (0, task_schedule_notes_1.resumeResultLines)(t))
39
+ this.log(line);
37
40
  }
38
41
  }
39
42
  exports.default = TasksResumeSchedule;
@@ -5,6 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const web_url_1 = require("../../lib/web-url");
7
7
  const prompt_1 = require("../../lib/prompt");
8
+ const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
8
9
  const iso_instant_1 = require("../../lib/iso-instant");
9
10
  const task_recurrence_summary_1 = require("../../lib/task-recurrence-summary");
10
11
  class TasksSchedule extends base_command_1.BaseCommand {
@@ -91,7 +92,8 @@ class TasksSchedule extends base_command_1.BaseCommand {
91
92
  }
92
93
  const t = result;
93
94
  const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
94
- this.log(`Scheduled task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''} for ${payload.scheduledFor}`);
95
+ const paused = Boolean(t.schedulePaused);
96
+ this.log(`${paused ? 'Recorded the time for' : 'Scheduled'} task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''} for ${payload.scheduledFor}`);
95
97
  if (t.recurrence?.pattern) {
96
98
  // Always name the zone: the pattern is read in UTC unless one was set, and a rule with no zone beside it
97
99
  // is exactly what could not be told apart from a local-time one.
@@ -108,6 +110,9 @@ class TasksSchedule extends base_command_1.BaseCommand {
108
110
  if (t.recurrence.endDate)
109
111
  this.log(`Ends: ${t.recurrence.endDate}`);
110
112
  }
113
+ for (const line of (0, task_schedule_notes_1.scheduleResultLines)(t, String(t.id ?? t._id ?? taskRef), this.config.bin)) {
114
+ this.log(line);
115
+ }
111
116
  }
112
117
  }
113
118
  exports.default = TasksSchedule;
@@ -23,8 +23,64 @@ export default class TasksSelfSchedule extends BaseCommand {
23
23
  in: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
24
  note: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
25
25
  'max-per-day': import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
26
+ 're-enable': import("@oclif/core/interfaces").BooleanFlag<boolean>;
26
27
  show: import("@oclif/core/interfaces").BooleanFlag<boolean>;
27
28
  clear: import("@oclif/core/interfaces").BooleanFlag<boolean>;
28
29
  };
29
30
  run(): Promise<void>;
30
31
  }
32
+ /** What the server records about a return that came due (`lastDecision` on the GET). */
33
+ export interface ReturnDecision {
34
+ outcome?: string;
35
+ /** ran | skipped | failed | waiting. Absent from a server older than this field. */
36
+ verdict?: 'ran' | 'skipped' | 'failed' | 'waiting';
37
+ reason?: string | null;
38
+ /** The reason in plain words, when the server has some for it. */
39
+ reasonText?: string | null;
40
+ at?: string | null;
41
+ }
42
+ /** The part after "the return came due and": what became of it, with the reason. */
43
+ export declare function decisionClause(decision: ReturnDecision): string;
44
+ /**
45
+ * The line `--show` prints for a return whose time has passed. "returned at" is
46
+ * said only when a run really started; a skipped, failed or unrecorded return
47
+ * says what it was.
48
+ */
49
+ export declare function describeSpentReturn({ taskRef, runAt, decision, triggerId, bin, }: {
50
+ taskRef: string;
51
+ runAt: string;
52
+ decision?: ReturnDecision | null;
53
+ triggerId?: string;
54
+ bin?: string;
55
+ }): string;
56
+ /**
57
+ * `--in` as an instant. A value that does not parse says WHICH value, because
58
+ * the generic "pass --at or --in" this used to fall through to told a person who
59
+ * had passed `--in 1.5h` to pass `--in`.
60
+ *
61
+ * Seconds are accepted: the server owns the 60 s floor and answers
62
+ * `RETURN_TOO_SOON` with the bound, which is a better explanation than the CLI
63
+ * refusing the unit.
64
+ */
65
+ export declare function resolveRelativeRunAt(relative: string, now?: number): {
66
+ runAt: string;
67
+ } | {
68
+ error: string;
69
+ };
70
+ /**
71
+ * `--at` as a UTC instant. Any offset is accepted and normalised, so the server
72
+ * always receives `...Z`. A value with no zone is refused by saying so: it is a
73
+ * valid ISO-8601 string, and "Invalid ISO datetime" for it read as if the CLI
74
+ * could not parse a thing the help had just told the person to type.
75
+ */
76
+ export declare function resolveAbsoluteRunAt(absolute: string): {
77
+ runAt: string;
78
+ } | {
79
+ error: string;
80
+ };
81
+ /** `--at`, or `--in`, or neither (undefined). Absolute wins when both are given. */
82
+ export declare function resolveRunAt(at?: string, relative?: string, now?: number): {
83
+ runAt: string;
84
+ } | {
85
+ error: string;
86
+ } | undefined;