@skrr-ai/cli 0.1.128 → 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 (103) hide show
  1. package/dist/base-command.d.ts +12 -0
  2. package/dist/base-command.js +65 -0
  3. package/dist/commands/agenda/trigger/create.js +9 -1
  4. package/dist/commands/agenda/trigger/list.d.ts +2 -1
  5. package/dist/commands/agenda/trigger/list.js +55 -6
  6. package/dist/commands/agents/create.js +3 -1
  7. package/dist/commands/agents/reflect.d.ts +6 -0
  8. package/dist/commands/agents/reflect.js +45 -6
  9. package/dist/commands/agents/schedule-overview.d.ts +17 -0
  10. package/dist/commands/agents/schedule-overview.js +33 -9
  11. package/dist/commands/agents/triggers/policy.js +3 -1
  12. package/dist/commands/browser/persistent-profile.d.ts +22 -0
  13. package/dist/commands/browser/persistent-profile.js +46 -0
  14. package/dist/commands/commitments/create.js +3 -1
  15. package/dist/commands/commitments/effective-policy.js +6 -0
  16. package/dist/commands/commitments/explain.js +10 -1
  17. package/dist/commands/commitments/plan.d.ts +14 -0
  18. package/dist/commands/commitments/plan.js +32 -0
  19. package/dist/commands/commitments/preflight.d.ts +1 -1
  20. package/dist/commands/commitments/preflight.js +12 -4
  21. package/dist/commands/commitments/show.d.ts +9 -0
  22. package/dist/commands/commitments/show.js +14 -1
  23. package/dist/commands/compass/revisions.d.ts +6 -2
  24. package/dist/commands/compass/revisions.js +13 -5
  25. package/dist/commands/compass/tactic.d.ts +5 -0
  26. package/dist/commands/compass/tactic.js +14 -0
  27. package/dist/commands/followups/plans/create.js +1 -1
  28. package/dist/commands/harnesses/list.js +1 -1
  29. package/dist/commands/northstars/create.js +6 -1
  30. package/dist/commands/northstars/governance/configure.js +1 -1
  31. package/dist/commands/northstars/intents/decide.js +2 -3
  32. package/dist/commands/northstars/list.js +1 -1
  33. package/dist/commands/northstars/review-now.js +5 -0
  34. package/dist/commands/northstars/reviews.js +15 -3
  35. package/dist/commands/northstars/show.js +1 -1
  36. package/dist/commands/spaces/auto-executor/runs.d.ts +9 -1
  37. package/dist/commands/spaces/auto-executor/runs.js +18 -1
  38. package/dist/commands/spaces/heartbeats/create.js +7 -5
  39. package/dist/commands/spaces/heartbeats/status.js +10 -0
  40. package/dist/commands/spaces/heartbeats/update.js +5 -3
  41. package/dist/commands/store/artifacts/releases.d.ts +20 -0
  42. package/dist/commands/store/artifacts/releases.js +85 -0
  43. package/dist/commands/store/library.d.ts +14 -0
  44. package/dist/commands/store/library.js +71 -0
  45. package/dist/commands/tasks/archive.js +6 -0
  46. package/dist/commands/tasks/list.d.ts +48 -0
  47. package/dist/commands/tasks/list.js +141 -9
  48. package/dist/commands/tasks/pause-schedule.js +1 -1
  49. package/dist/commands/tasks/remind.js +23 -11
  50. package/dist/commands/tasks/resume-schedule.js +4 -1
  51. package/dist/commands/tasks/retry-schedule.js +6 -3
  52. package/dist/commands/tasks/schedule.js +35 -8
  53. package/dist/commands/tasks/self-schedule.d.ts +56 -0
  54. package/dist/commands/tasks/self-schedule.js +179 -21
  55. package/dist/commands/tasks/show.js +4 -0
  56. package/dist/lib/agent-trigger-listing.d.ts +26 -0
  57. package/dist/lib/agent-trigger-listing.js +63 -0
  58. package/dist/lib/agentic-stream.js +138 -41
  59. package/dist/lib/commitment-ledgers.js +3 -1
  60. package/dist/lib/commitments.d.ts +24 -0
  61. package/dist/lib/commitments.js +64 -4
  62. package/dist/lib/compasses.d.ts +2 -0
  63. package/dist/lib/iso-instant.d.ts +52 -0
  64. package/dist/lib/iso-instant.js +162 -0
  65. package/dist/lib/northstars.d.ts +87 -4
  66. package/dist/lib/northstars.js +226 -94
  67. package/dist/lib/proposals.js +6 -2
  68. package/dist/lib/reflection.d.ts +58 -0
  69. package/dist/lib/reflection.js +115 -6
  70. package/dist/lib/store-artifact-releases.d.ts +39 -0
  71. package/dist/lib/store-artifact-releases.js +12 -0
  72. package/dist/lib/store-library.d.ts +37 -0
  73. package/dist/lib/store-library.js +12 -0
  74. package/dist/lib/task-calendar.d.ts +19 -0
  75. package/dist/lib/task-calendar.js +72 -0
  76. package/dist/lib/task-operations.js +2 -0
  77. package/dist/lib/task-recurrence-outcome.js +3 -1
  78. package/dist/lib/task-recurrence-summary.d.ts +15 -0
  79. package/dist/lib/task-recurrence-summary.js +228 -0
  80. package/dist/lib/task-reminder.d.ts +28 -0
  81. package/dist/lib/task-reminder.js +50 -0
  82. package/dist/lib/task-schedule-notes.d.ts +46 -0
  83. package/dist/lib/task-schedule-notes.js +71 -0
  84. package/dist/lib/triggers.d.ts +31 -2
  85. package/dist/lib/triggers.js +38 -18
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +11 -1
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.d.ts +123 -0
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.js +160 -0
  91. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +19 -0
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +11 -1
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.d.ts +123 -0
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.js +173 -0
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +3 -1
  100. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  101. package/dist/node_modules/@skrr-ai/data-provider/index.js +6772 -6675
  102. package/oclif.manifest.json +282 -40
  103. package/package.json +1 -1
@@ -580,6 +580,18 @@ export declare function describeHarnessUnavailable(body: Record<string, unknown>
580
580
  * Exported for tests.
581
581
  */
582
582
  export declare function describeGitHubUnavailable(): string;
583
+ /**
584
+ * Frame a typed `TASK_LIST_TIMEOUT` failure as what it is — a list too broad for the database's
585
+ * time limit — rather than a control-plane outage.
586
+ *
587
+ * `GET /api/tasks` answers 503 with this code when its one aggregate is cut off at `maxTimeMS`,
588
+ * which an unscoped list over every accessible Space reaches because no index serves its sort
589
+ * across the access filter. Lead with the server's `cause`/`remedy`, falling back to the CLI's
590
+ * own sentence: the 5xx redaction boundary generalizes a bare message in production.
591
+ *
592
+ * Exported for tests.
593
+ */
594
+ export declare function describeTaskListTimeout(body: Record<string, unknown> | null, bin?: string): string;
583
595
  /**
584
596
  * Frame a typed `LABEL_SUGGESTION_UNAVAILABLE` failure as what it is — the
585
597
  * suggestion's hosted-inference call found no billing payer — rather than a
@@ -9,6 +9,7 @@ exports.commitmentActivationHint = commitmentActivationHint;
9
9
  exports.withSubject = withSubject;
10
10
  exports.describeHarnessUnavailable = describeHarnessUnavailable;
11
11
  exports.describeGitHubUnavailable = describeGitHubUnavailable;
12
+ exports.describeTaskListTimeout = describeTaskListTimeout;
12
13
  exports.describeLabelSuggestionUnavailable = describeLabelSuggestionUnavailable;
13
14
  exports.describeGitHubAppNotConfigured = describeGitHubAppNotConfigured;
14
15
  exports.describeResourceNotFound = describeResourceNotFound;
@@ -1573,6 +1574,20 @@ class BaseCommand extends core_1.Command {
1573
1574
  details: envelopeDetails(body),
1574
1575
  });
1575
1576
  }
1577
+ else if (bodyCode === 'TASK_LIST_TIMEOUT') {
1578
+ // The list was too broad for the database's time limit — a 503 the API sent on purpose,
1579
+ // with the narrower question to ask. The outage branch below would say skrr is down,
1580
+ // record an incident, and tell an agent to retry the very query that cannot finish
1581
+ // (OSK-21284).
1582
+ this.failWithCliError({
1583
+ message: say(describeTaskListTimeout(body, this.config.bin)),
1584
+ code: bodyCode,
1585
+ status: e.status,
1586
+ exit: 1,
1587
+ retryable: false,
1588
+ details: envelopeDetails(body),
1589
+ });
1590
+ }
1576
1591
  else if (bodyCode === 'LABEL_SUGGESTION_UNAVAILABLE') {
1577
1592
  // The label-suggestion route found no inference payer — a per-call
1578
1593
  // condition the API deliberately reported, not a platform outage,
@@ -1602,6 +1617,21 @@ class BaseCommand extends core_1.Command {
1602
1617
  details: envelopeDetails(body),
1603
1618
  });
1604
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
+ }
1605
1635
  else if ((e.status === 409 || e.status === 404) && (0, compass_refusals_1.compassRefusalCode)(body)) {
1606
1636
  // The Compass routes sent the code in upper-case `error`, which `refusalCodeOf` does not
1607
1637
  // read as a code, so a stale writer saw `HTTP_409` and a mistyped version `NOT_FOUND`,
@@ -2294,6 +2324,21 @@ function apiRefusal(err) {
2294
2324
  }
2295
2325
  /** The typed code the server refuses a Commitment activation with. */
2296
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
+ }
2297
2342
  /**
2298
2343
  * The remedy a refused Commitment activation carries.
2299
2344
  *
@@ -2388,6 +2433,26 @@ function describeGitHubUnavailable() {
2388
2433
  return ('GitHub is temporarily unreachable, so this request could not be completed. ' +
2389
2434
  'skrr itself answered, so this is not a skrr outage — try again shortly.');
2390
2435
  }
2436
+ /**
2437
+ * Frame a typed `TASK_LIST_TIMEOUT` failure as what it is — a list too broad for the database's
2438
+ * time limit — rather than a control-plane outage.
2439
+ *
2440
+ * `GET /api/tasks` answers 503 with this code when its one aggregate is cut off at `maxTimeMS`,
2441
+ * which an unscoped list over every accessible Space reaches because no index serves its sort
2442
+ * across the access filter. Lead with the server's `cause`/`remedy`, falling back to the CLI's
2443
+ * own sentence: the 5xx redaction boundary generalizes a bare message in production.
2444
+ *
2445
+ * Exported for tests.
2446
+ */
2447
+ function describeTaskListTimeout(body, bin = 'skrr') {
2448
+ const cause = stringProp(body, 'cause') ||
2449
+ 'The task list took too long: it spans every task you can see across all your workspaces and ' +
2450
+ 'Spaces, and the database could not return the newest page within its time limit.';
2451
+ const remedy = stringProp(body, 'remedy') ||
2452
+ `Name where to look: a workspace (\`${bin} workspaces use <workspace-id>\`, or \`--workspace <workspace-id>\`) ` +
2453
+ `or a Space (\`${bin} tasks list --space <space-id>\`).`;
2454
+ return `${cause}\n → ${remedy}`;
2455
+ }
2391
2456
  /**
2392
2457
  * Frame a typed `LABEL_SUGGESTION_UNAVAILABLE` failure as what it is — the
2393
2458
  * suggestion's hosted-inference call found no billing payer — rather than a
@@ -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
  }
@@ -12,7 +12,8 @@ import { BaseCommand } from '../../../base-command';
12
12
  export declare function isUnconfirmedTurnCapture(trigger: Record<string, unknown>): boolean;
13
13
  /**
14
14
  * `skrr agenda trigger list <agent-id>` — show all triggers registered for
15
- * an agent (any kind, not just agenda-created ones).
15
+ * an agent (any kind, not just agenda-created ones), including the ones the
16
+ * platform manages for its Commitments (the daily self-reflection check).
16
17
  */
17
18
  export default class AgendaTriggerList extends BaseCommand {
18
19
  static description: string;
@@ -7,6 +7,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
7
7
  const base_command_1 = require("../../../base-command");
8
8
  const format_1 = require("../../../lib/format");
9
9
  const triggers_1 = require("../../../lib/triggers");
10
+ const agent_trigger_listing_1 = require("../../../lib/agent-trigger-listing");
10
11
  /**
11
12
  * A schedule persisted while handling a conversation turn, for which no explicit
12
13
  * confirmation was recorded.
@@ -27,10 +28,11 @@ function isUnconfirmedTurnCapture(trigger) {
27
28
  }
28
29
  /**
29
30
  * `skrr agenda trigger list <agent-id>` — show all triggers registered for
30
- * an agent (any kind, not just agenda-created ones).
31
+ * an agent (any kind, not just agenda-created ones), including the ones the
32
+ * platform manages for its Commitments (the daily self-reflection check).
31
33
  */
32
34
  class AgendaTriggerList extends base_command_1.BaseCommand {
33
- static description = 'List every trigger registered against an agent.';
35
+ static description = "List every trigger registered against an agent, including the ones the platform manages for its Commitments (SOURCE 'system'). A passive agent (--initial-always-on off) still has a daily self-reflection check.";
34
36
  static examples = [
35
37
  '<%= config.bin %> agenda trigger list <agent-id>',
36
38
  '<%= config.bin %> agenda trigger list <agent-id> --unconfirmed',
@@ -55,17 +57,50 @@ class AgendaTriggerList extends base_command_1.BaseCommand {
55
57
  catch (err) {
56
58
  this.handleApiError(err);
57
59
  }
60
+ const r = response;
61
+ const listed = r?.data ?? [];
62
+ // The per-agent route lists `scope: 'agent'` rows only; the Commitment triggers a passive agent still owns
63
+ // live under `commitment_watcher`. `--unconfirmed` is about turn captures, which are agent-scoped, so it
64
+ // skips the extra read.
65
+ let extra = [];
66
+ let extraUnreadable = null;
67
+ if (!flags.unconfirmed) {
68
+ try {
69
+ const universal = await triggers_1.triggerApi.list((0, triggers_1.withQuery)('/api/triggers', { refEntity: [`agent:${args.agentId}`], limit: 200 }));
70
+ extra = (0, agent_trigger_listing_1.triggersMissingFromAgentList)(listed, (universal?.triggers ?? []));
71
+ }
72
+ catch (err) {
73
+ extraUnreadable = err instanceof Error ? err.message : String(err);
74
+ }
75
+ }
58
76
  if (flags.json) {
59
- this.printJsonResult((0, list_output_1.listResult)(response));
77
+ this.printJsonResult((0, list_output_1.listResult)(r && typeof r === 'object'
78
+ ? {
79
+ ...r,
80
+ data: [...listed, ...extra],
81
+ ...(extraUnreadable
82
+ ? {
83
+ warnings: [
84
+ {
85
+ code: 'system_triggers_unreadable',
86
+ value: extraUnreadable,
87
+ hint: systemTriggersUnreadable(args.agentId, extraUnreadable),
88
+ },
89
+ ],
90
+ }
91
+ : {}),
92
+ }
93
+ : response));
60
94
  return;
61
95
  }
62
- const r = response;
63
- let items = r?.data ?? [];
96
+ let items = [...listed, ...extra];
64
97
  if (flags.unconfirmed) {
65
98
  items = items.filter((t) => isUnconfirmedTurnCapture(t));
66
99
  }
67
100
  if (items.length === 0) {
68
101
  this.log(flags.unconfirmed ? 'No unconfirmed turn-captured schedules.' : 'No triggers.');
102
+ if (extraUnreadable)
103
+ this.log(systemTriggersUnreadable(args.agentId, extraUnreadable));
69
104
  return;
70
105
  }
71
106
  const rows = items.map((t) => {
@@ -81,7 +116,7 @@ class AgendaTriggerList extends base_command_1.BaseCommand {
81
116
  label: String(t.label ?? '-').slice(0, 40),
82
117
  next: String(next).slice(0, 25),
83
118
  fires: String(fires),
84
- source: isUnconfirmedTurnCapture(t) ? 'turn*' : '-',
119
+ source: (0, agent_trigger_listing_1.sourceLabel)(t, isUnconfirmedTurnCapture(t)),
85
120
  };
86
121
  });
87
122
  (0, format_1.renderTable)(rows, [
@@ -93,6 +128,15 @@ class AgendaTriggerList extends base_command_1.BaseCommand {
93
128
  { key: 'fires', header: 'FIRES' },
94
129
  { key: 'source', header: 'SOURCE' },
95
130
  ], (line) => this.log(line));
131
+ if (rows.some((row) => row.source.startsWith('system'))) {
132
+ this.log('');
133
+ for (const line of (0, agent_trigger_listing_1.systemManagedLegend)(args.agentId, this.config.bin))
134
+ this.log(line);
135
+ }
136
+ if (extraUnreadable) {
137
+ this.log('');
138
+ this.log(systemTriggersUnreadable(args.agentId, extraUnreadable));
139
+ }
96
140
  if (rows.some((row) => row.source === 'turn*')) {
97
141
  // Deliberately not "the agent scheduled this by itself". The capture layer
98
142
  // cannot tell "remind me at 9" from a nudge the agent invented, so the
@@ -105,3 +149,8 @@ class AgendaTriggerList extends base_command_1.BaseCommand {
105
149
  }
106
150
  }
107
151
  exports.default = AgendaTriggerList;
152
+ /** Said, not skipped: a missing read must not look like an agent with no Commitment triggers. */
153
+ function systemTriggersUnreadable(agentId, reason) {
154
+ return (`Platform-managed (Commitment) triggers could not be read (${reason}); this list may be incomplete. ` +
155
+ `Try: skrr triggers list --ref-entity agent:${agentId}`);
156
+ }
@@ -245,7 +245,9 @@ class AgentsCreate extends base_command_1.BaseCommand {
245
245
  description: 'Default working directory for local Harness sessions',
246
246
  }),
247
247
  'initial-always-on': core_1.Flags.string({
248
- description: 'Start autonomous work after creation (default: on). Use off for a passive agent; explicit chat still works. Relationship-loop seeding is configured separately.',
248
+ description: 'Start autonomous work after creation (default: on). Use off for a passive agent; explicit chat still works. ' +
249
+ 'A passive agent is still seeded with the system-managed daily self-reflection Commitment: `agenda trigger list <agent>` shows its trigger (SOURCE "system", marked "suspended" while the platform holds it for Always-On being off) ' +
250
+ 'and `commitments list --agent <agent> --include-system` finds the Commitment to pause. Relationship-loop seeding is configured separately.',
249
251
  options: ['on', 'off'],
250
252
  }),
251
253
  'no-relationship-loops': core_1.Flags.boolean({
@@ -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' }),
@@ -0,0 +1,22 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * Whether browser sessions on THIS computer share one profile that keeps its
4
+ * logins and cache, instead of a fresh one each.
5
+ *
6
+ * The setting lives in the daemon's own config on this computer, so it is
7
+ * forwarded to the daemon rather than sent to the API: a browser that stays
8
+ * signed in is the person at the machine's decision, and a request from the
9
+ * cloud must not be able to make it (the same boundary as `browser
10
+ * cloud-agents`). The profile is the daemon's own, never the everyday Chrome.
11
+ */
12
+ export default class BrowserPersistentProfile extends Command {
13
+ static description: string;
14
+ static examples: string[];
15
+ static args: {
16
+ action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ static flags: {
19
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
20
+ };
21
+ run(): Promise<void>;
22
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const browser_1 = require("../browser");
5
+ /**
6
+ * Whether browser sessions on THIS computer share one profile that keeps its
7
+ * logins and cache, instead of a fresh one each.
8
+ *
9
+ * The setting lives in the daemon's own config on this computer, so it is
10
+ * forwarded to the daemon rather than sent to the API: a browser that stays
11
+ * signed in is the person at the machine's decision, and a request from the
12
+ * cloud must not be able to make it (the same boundary as `browser
13
+ * cloud-agents`). The profile is the daemon's own, never the everyday Chrome.
14
+ */
15
+ class BrowserPersistentProfile extends core_1.Command {
16
+ static description = 'Keep one browser profile (logins, cookies, cache) across browser sessions on this computer (off by default)';
17
+ static examples = [
18
+ '<%= config.bin %> browser persistent-profile status',
19
+ '<%= config.bin %> browser persistent-profile on',
20
+ '<%= config.bin %> browser persistent-profile off',
21
+ '<%= config.bin %> browser persistent-profile reset',
22
+ ];
23
+ static args = {
24
+ action: core_1.Args.string({
25
+ description: 'on, off, status, or reset (sign out of everything and delete the profile)',
26
+ required: true,
27
+ options: ['on', 'off', 'status', 'reset'],
28
+ // Without this oclif reads stdin for a missing positional, so the command
29
+ // hangs when run from a pipe or a non-tty instead of refusing. Enforced
30
+ // by ARG_IMPLICIT_STDIN_ENABLED in the command-hygiene audit.
31
+ ignoreStdin: true,
32
+ }),
33
+ };
34
+ static flags = {
35
+ json: core_1.Flags.boolean({ description: 'Output JSON' }),
36
+ };
37
+ async run() {
38
+ const { args, flags } = await this.parse(BrowserPersistentProfile);
39
+ await (0, browser_1.forwardBrowserCommand)(this, [
40
+ 'persistent-profile',
41
+ args.action,
42
+ ...(flags.json ? ['--json'] : []),
43
+ ]);
44
+ }
45
+ }
46
+ exports.default = BrowserPersistentProfile;
@@ -260,7 +260,9 @@ JSON
260
260
  <%= config.bin %> compass commitments <compass-id> --agent <agent-id> # lists it once it is bound`,
261
261
  ];
262
262
  static flags = {
263
- json: core_1.Flags.boolean({ description: 'Output the created commitment as JSON' }),
263
+ json: core_1.Flags.boolean({
264
+ description: 'Output the created commitment as JSON: the commitment itself, not wrapped (commitments show --json puts it under .commitment)',
265
+ }),
264
266
  'from-json': core_1.Flags.string({
265
267
  description: `JSON file (or "-" for stdin) for fields not exposed as flags. See examples below for daily times, named agent_run keys, zero budgets and "compassId" (binds a Compass; fixed at creation). ${commitment_attention_copy_1.QUIET_HOURS_HELP} ${commitment_attention_copy_1.EVENT_SOURCES_HELP}`,
266
268
  }),
@@ -7,6 +7,7 @@ const base_command_1 = require("../../base-command");
7
7
  const commitments_1 = require("../../lib/commitments");
8
8
  const commitment_product_1 = require("../../lib/commitment-product");
9
9
  const commitment_attention_copy_1 = require("../../lib/commitment-attention-copy");
10
+ const northstars_1 = require("../../lib/northstars");
10
11
  /** Which limit binds the daily action runs — the fix lives on that object. */
11
12
  const RUN_LIMIT_BINDING_LABELS = {
12
13
  northstar_allocation: 'Northstar allocation',
@@ -71,6 +72,11 @@ function budgetDayLines(policy) {
71
72
  ? 'no ceiling'
72
73
  : `${value}/day`;
73
74
  lines.push(`Northstar shared pools: action runs ${pool(shared.actionRunsPerDay, shared.actionRunsPerDayState)} · observation runs ${pool(shared.observationRunsPerDay, shared.observationRunsPerDayState)}`);
75
+ // A pool below the number of linked active commitments is still handed out at the floor of
76
+ // 1/day each: the same sentence `northstars governance show` and `review-now` print (OSK-21334).
77
+ const floorNote = (0, northstars_1.runBudgetFloorNote)(policy.portfolio?.runBudgetFloor);
78
+ if (floorNote)
79
+ lines.push(`! ${floorNote}`);
74
80
  }
75
81
  return lines;
76
82
  }
@@ -24,13 +24,22 @@ class CommitmentsExplain extends base_command_1.BaseCommand {
24
24
  this.requireAuth();
25
25
  const { args, flags } = await this.parse(CommitmentsExplain);
26
26
  try {
27
- const explanation = await commitments_1.commitmentApi.explain(args.commitmentId);
27
+ // The explanation names neither the bound Compass nor its status, so the text view reads the
28
+ // Commitment beside it (the read `show` renders). `--json` stays the explanation as the
29
+ // server sent it, with no extra request. A failed read must not fail the explanation
30
+ // (OSK-21332): it becomes a "not shown" line instead.
31
+ const [explanation, read] = await Promise.all([
32
+ commitments_1.commitmentApi.explain(args.commitmentId),
33
+ flags.json ? null : commitments_1.commitmentApi.get(args.commitmentId).catch(() => null),
34
+ ]);
28
35
  if (flags.json) {
29
36
  this.log(JSON.stringify(explanation, null, 2));
30
37
  return;
31
38
  }
32
39
  for (const line of (0, commitments_1.formatCommitmentExplanation)(explanation))
33
40
  this.log(line);
41
+ for (const line of (0, commitments_1.explainCompassLines)(args.commitmentId, read?.commitment))
42
+ this.log(line);
34
43
  }
35
44
  catch (err) {
36
45
  this.handleApiError(err);