@skrr-ai/cli 0.1.95 → 0.1.96

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 (185) hide show
  1. package/README.md +22 -0
  2. package/dist/commands/agent/message/send.d.ts +2 -0
  3. package/dist/commands/agent/message/send.js +75 -5
  4. package/dist/commands/agents/chat.js +1 -1
  5. package/dist/commands/agents/create.d.ts +0 -3
  6. package/dist/commands/agents/create.js +0 -26
  7. package/dist/commands/agents/egress.d.ts +17 -0
  8. package/dist/commands/agents/egress.js +84 -0
  9. package/dist/commands/agents/mailbox/messages.d.ts +14 -0
  10. package/dist/commands/agents/mailbox/messages.js +52 -0
  11. package/dist/commands/agents/mailbox/show.d.ts +13 -0
  12. package/dist/commands/agents/mailbox/show.js +52 -0
  13. package/dist/commands/agents/operating-plan.js +8 -0
  14. package/dist/commands/agents/roles/capture.d.ts +16 -0
  15. package/dist/commands/agents/roles/capture.js +53 -0
  16. package/dist/commands/agents/roles/install.d.ts +1 -0
  17. package/dist/commands/agents/roles/install.js +8 -1
  18. package/dist/commands/agents/roles/preview.d.ts +2 -0
  19. package/dist/commands/agents/roles/preview.js +27 -3
  20. package/dist/commands/agents/update.d.ts +0 -3
  21. package/dist/commands/agents/update.js +1 -31
  22. package/dist/commands/agents/usage-cap.d.ts +4 -1
  23. package/dist/commands/agents/usage-cap.js +49 -10
  24. package/dist/commands/code/jobs/show.js +12 -0
  25. package/dist/commands/commitments/action-proposals/decide.d.ts +2 -0
  26. package/dist/commands/commitments/action-proposals/decide.js +12 -0
  27. package/dist/commands/commitments/action-proposals.js +7 -0
  28. package/dist/commands/commitments/create.d.ts +1 -0
  29. package/dist/commands/commitments/create.js +17 -1
  30. package/dist/commands/commitments/delivery.d.ts +1 -0
  31. package/dist/commands/commitments/delivery.js +11 -1
  32. package/dist/commands/commitments/doctor.js +4 -0
  33. package/dist/commands/commitments/effective-policy.js +26 -0
  34. package/dist/commands/commitments/endpoints/create.js +5 -2
  35. package/dist/commands/commitments/observations.d.ts +10 -0
  36. package/dist/commands/commitments/observations.js +88 -0
  37. package/dist/commands/commitments/pause.d.ts +1 -1
  38. package/dist/commands/commitments/pause.js +4 -3
  39. package/dist/commands/commitments/preflight.js +2 -1
  40. package/dist/commands/commitments/reports.d.ts +2 -0
  41. package/dist/commands/commitments/reports.js +22 -4
  42. package/dist/commands/commitments/update.d.ts +1 -0
  43. package/dist/commands/commitments/update.js +35 -5
  44. package/dist/commands/grants/create.d.ts +1 -0
  45. package/dist/commands/grants/create.js +8 -0
  46. package/dist/commands/grants/rotate.d.ts +5 -0
  47. package/dist/commands/grants/rotate.js +13 -1
  48. package/dist/commands/grants/update.d.ts +12 -0
  49. package/dist/commands/grants/update.js +27 -0
  50. package/dist/commands/machines/hosted/start.js +7 -1
  51. package/dist/commands/machines/list.js +4 -4
  52. package/dist/commands/measures/confirm.d.ts +1 -7
  53. package/dist/commands/measures/confirm.js +15 -14
  54. package/dist/commands/measures/list.d.ts +1 -7
  55. package/dist/commands/measures/list.js +1 -7
  56. package/dist/commands/measures/record.js +4 -4
  57. package/dist/commands/measures/show.d.ts +11 -0
  58. package/dist/commands/measures/show.js +26 -0
  59. package/dist/commands/northstars/governance/configure.d.ts +1 -0
  60. package/dist/commands/northstars/governance/configure.js +8 -1
  61. package/dist/commands/proposals/discard.d.ts +5 -0
  62. package/dist/commands/proposals/discard.js +8 -0
  63. package/dist/commands/proposals/keep.d.ts +5 -0
  64. package/dist/commands/proposals/keep.js +8 -0
  65. package/dist/commands/proposals/list.d.ts +15 -0
  66. package/dist/commands/proposals/list.js +74 -0
  67. package/dist/commands/proposals/show.d.ts +12 -0
  68. package/dist/commands/proposals/show.js +27 -0
  69. package/dist/commands/proposals/snooze.d.ts +5 -0
  70. package/dist/commands/proposals/snooze.js +8 -0
  71. package/dist/commands/signals/monitors/create.d.ts +9 -0
  72. package/dist/commands/signals/monitors/create.js +29 -0
  73. package/dist/commands/signals/monitors/list.d.ts +8 -0
  74. package/dist/commands/signals/monitors/list.js +16 -0
  75. package/dist/commands/signals/monitors/pause.d.ts +11 -0
  76. package/dist/commands/signals/monitors/pause.js +17 -0
  77. package/dist/commands/signals/monitors/resume.d.ts +11 -0
  78. package/dist/commands/signals/monitors/resume.js +17 -0
  79. package/dist/commands/signals/monitors/revise.d.ts +12 -0
  80. package/dist/commands/signals/monitors/revise.js +25 -0
  81. package/dist/commands/signals/monitors/show.d.ts +11 -0
  82. package/dist/commands/signals/monitors/show.js +17 -0
  83. package/dist/commands/signals/monitors/test.d.ts +12 -0
  84. package/dist/commands/signals/monitors/test.js +25 -0
  85. package/dist/commands/signals/monitors/transitions.d.ts +11 -0
  86. package/dist/commands/signals/monitors/transitions.js +17 -0
  87. package/dist/commands/signals/monitors/validate.d.ts +9 -0
  88. package/dist/commands/signals/monitors/validate.js +24 -0
  89. package/dist/commands/signals/schemas/list.d.ts +8 -0
  90. package/dist/commands/signals/schemas/list.js +16 -0
  91. package/dist/commands/signals/schemas/show.d.ts +11 -0
  92. package/dist/commands/signals/schemas/show.js +17 -0
  93. package/dist/commands/signals/sources/list.d.ts +8 -0
  94. package/dist/commands/signals/sources/list.js +16 -0
  95. package/dist/commands/signals/sources/receipts.d.ts +11 -0
  96. package/dist/commands/signals/sources/receipts.js +17 -0
  97. package/dist/commands/signals/sources/show.d.ts +11 -0
  98. package/dist/commands/signals/sources/show.js +17 -0
  99. package/dist/commands/spaces/usage.d.ts +12 -0
  100. package/dist/commands/spaces/usage.js +28 -0
  101. package/dist/commands/spaces/workflows/run.js +3 -0
  102. package/dist/commands/spaces/workflows/runs.js +2 -1
  103. package/dist/commands/spaces/workflows/update.d.ts +6 -1
  104. package/dist/commands/spaces/workflows/update.js +34 -2
  105. package/dist/commands/tasks/cascade-status.d.ts +2 -0
  106. package/dist/commands/tasks/cascade-status.js +27 -1
  107. package/dist/commands/tasks/create.d.ts +4 -0
  108. package/dist/commands/tasks/create.js +16 -0
  109. package/dist/commands/tasks/deliverable/add.js +12 -3
  110. package/dist/commands/tasks/economics-compare.d.ts +15 -0
  111. package/dist/commands/tasks/economics-compare.js +60 -0
  112. package/dist/commands/tasks/expectations/evaluate.d.ts +13 -0
  113. package/dist/commands/tasks/expectations/evaluate.js +43 -0
  114. package/dist/commands/tasks/list.d.ts +1 -0
  115. package/dist/commands/tasks/list.js +44 -1
  116. package/dist/commands/tasks/show.d.ts +5 -0
  117. package/dist/commands/tasks/show.js +17 -0
  118. package/dist/commands/tasks/update.d.ts +4 -0
  119. package/dist/commands/tasks/update.js +9 -0
  120. package/dist/commands/tasks/workflow-findings.js +2 -2
  121. package/dist/commands/triggers/create.d.ts +2 -0
  122. package/dist/commands/triggers/create.js +15 -0
  123. package/dist/commands/workspaces/grants.d.ts +12 -0
  124. package/dist/commands/workspaces/grants.js +31 -0
  125. package/dist/commands/workspaces/stewardship.d.ts +13 -0
  126. package/dist/commands/workspaces/stewardship.js +34 -0
  127. package/dist/lib/agent-config.d.ts +0 -12
  128. package/dist/lib/agent-config.js +1 -27
  129. package/dist/lib/agent-mailbox.d.ts +35 -0
  130. package/dist/lib/agent-mailbox.js +55 -0
  131. package/dist/lib/commitment-endpoints.d.ts +2 -1
  132. package/dist/lib/commitment-endpoints.js +2 -5
  133. package/dist/lib/commitment-product.d.ts +3 -1
  134. package/dist/lib/commitment-product.js +10 -0
  135. package/dist/lib/commitment-terms.js +1 -1
  136. package/dist/lib/commitments.d.ts +17 -7
  137. package/dist/lib/commitments.js +173 -85
  138. package/dist/lib/compasses.d.ts +14 -0
  139. package/dist/lib/compasses.js +25 -11
  140. package/dist/lib/grants.d.ts +5 -1
  141. package/dist/lib/grants.js +9 -1
  142. package/dist/lib/hosted-machines.d.ts +7 -0
  143. package/dist/lib/mailbox-human-command.d.ts +11 -0
  144. package/dist/lib/mailbox-human-command.js +41 -0
  145. package/dist/lib/measures.d.ts +28 -2
  146. package/dist/lib/measures.js +38 -14
  147. package/dist/lib/message-intent.d.ts +6 -2
  148. package/dist/lib/message-intent.js +1 -0
  149. package/dist/lib/northstars.js +2 -1
  150. package/dist/lib/proposal-decision-command.d.ts +19 -0
  151. package/dist/lib/proposal-decision-command.js +64 -0
  152. package/dist/lib/proposals.d.ts +8 -0
  153. package/dist/lib/proposals.js +22 -0
  154. package/dist/lib/roles.d.ts +141 -6
  155. package/dist/lib/roles.js +8 -2
  156. package/dist/lib/signals.d.ts +20 -0
  157. package/dist/lib/signals.js +24 -0
  158. package/dist/lib/space-usage.d.ts +3 -0
  159. package/dist/lib/space-usage.js +17 -0
  160. package/dist/lib/stewardship-output.d.ts +4 -0
  161. package/dist/lib/stewardship-output.js +82 -0
  162. package/dist/lib/task-asset-upload.d.ts +3 -1
  163. package/dist/lib/task-asset-upload.js +1 -0
  164. package/dist/lib/task-economics.d.ts +16 -0
  165. package/dist/lib/task-economics.js +22 -0
  166. package/dist/lib/triggers.d.ts +11 -3
  167. package/dist/lib/triggers.js +58 -1
  168. package/dist/lib/usage-discovery.d.ts +10 -0
  169. package/dist/lib/usage-discovery.js +24 -3
  170. package/dist/lib/workflow-run-budget.d.ts +12 -0
  171. package/dist/lib/workflow-run-budget.js +37 -0
  172. package/dist/lib/workspaces.d.ts +48 -0
  173. package/dist/lib/workspaces.js +8 -0
  174. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +4 -0
  175. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +14 -1
  176. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.d.ts +9 -0
  177. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.js +49 -0
  178. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +4 -0
  179. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +12 -0
  180. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.d.ts +9 -0
  181. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.js +44 -0
  182. package/dist/node_modules/@skrr-ai/auth-core/package.json +10 -0
  183. package/dist/node_modules/@skrr-ai/data-provider/index.js +8017 -7086
  184. package/oclif.manifest.json +3872 -1167
  185. package/package.json +22 -1
@@ -1,8 +1,49 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseObservationWindow = parseObservationWindow;
4
+ exports.renderObservationWindow = renderObservationWindow;
3
5
  const core_1 = require("@oclif/core");
6
+ const data_provider_1 = require("@skrr-ai/data-provider");
4
7
  const base_command_1 = require("../../base-command");
5
8
  const commitments_1 = require("../../lib/commitments");
9
+ /** Explicit ISO FROM/TO intervals avoid local-time or rolling-window ambiguity. */
10
+ function parseObservationWindow(value) {
11
+ const parts = value.split('/');
12
+ const timestamp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
13
+ const [from, to] = parts.map((entry) => new Date(entry).getTime());
14
+ const realCalendarDay = (part) => {
15
+ const day = part.slice(0, 10);
16
+ const parsed = new Date(`${day}T00:00:00Z`);
17
+ return Number.isFinite(parsed.getTime()) && parsed.toISOString().slice(0, 10) === day;
18
+ };
19
+ if (parts.length !== 2 ||
20
+ !parts.every((part) => timestamp.test(part) && realCalendarDay(part)) ||
21
+ !Number.isFinite(from) ||
22
+ !Number.isFinite(to) ||
23
+ to <= from ||
24
+ to - from > data_provider_1.OBSERVATION_WINDOW_MAX_MS)
25
+ throw new Error('Window requires ISO FROM/TO with an explicit timezone and a positive span of at most 90 days');
26
+ return { from: new Date(from).toISOString(), to: new Date(to).toISOString() };
27
+ }
28
+ function renderObservationWindow(response, log) {
29
+ // Always announce partial data before showing figures, including baseline truncation.
30
+ if (response.truncated)
31
+ log('TRUNCATED: bounded sample only; figures are incomplete.');
32
+ log('Agent-reported samples; as-of interval coverage is not independent verification.');
33
+ const display = (label, value) => {
34
+ const window = value;
35
+ log(`${label}: [${window.from}, ${window.to})`);
36
+ log(`Samples: ${window.count}; numeric: ${window.numericCount}; unreadable: ${window.gapCount}; uncovered ms: ${window.gapMs}`);
37
+ log(`Mean: ${window.mean ?? 'unknown'}; sum: ${window.sum ?? 'unknown'}; first: ${window.first ?? 'unknown'}; last: ${window.last ?? 'unknown'}`);
38
+ };
39
+ if (response.baseline) {
40
+ display('Baseline', response.baseline);
41
+ display('After', response.after);
42
+ log(`Comparison: ${response.sufficient ? `delta=${response.delta}; ratio=${response.ratio}` : `unavailable (${response.reason})`}`);
43
+ }
44
+ else
45
+ display('Window', response.summary);
46
+ }
6
47
  /**
7
48
  * `skrr commitments observations <id>` — what this commitment's runs saw.
8
49
  *
@@ -30,6 +71,18 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
30
71
  };
31
72
  static flags = {
32
73
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
74
+ latest: core_1.Flags.boolean({
75
+ description: 'Latest recorded attempt per current declared key (not history)',
76
+ }),
77
+ key: core_1.Flags.string({ description: 'Observation key for --window' }),
78
+ window: core_1.Flags.string({
79
+ description: 'Half-open ISO FROM/TO interval, at most 90 days',
80
+ dependsOn: ['key'],
81
+ }),
82
+ 'compare-to': core_1.Flags.string({
83
+ description: 'Baseline ISO FROM/TO interval',
84
+ dependsOn: ['window'],
85
+ }),
33
86
  limit: core_1.Flags.integer({ description: 'Rows to include', min: 1, max: 200 }),
34
87
  'declared-only': core_1.Flags.boolean({
35
88
  description: 'Only keys the operator declared — the rows that can move the loop',
@@ -45,10 +98,42 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
45
98
  async run() {
46
99
  this.requireAuth();
47
100
  const { args, flags } = await this.parse(CommitmentsObservations);
101
+ if (flags.latest && (flags.window || flags.cursor || flags['changed-only'] || flags.since))
102
+ this.error('--latest cannot be combined with --window, --cursor, --changed-only, or --since');
48
103
  let response;
104
+ if (flags.window) {
105
+ try {
106
+ if (flags['declared-only'] || flags['changed-only'] || flags.since || flags.cursor)
107
+ this.error('--window cannot be combined with list filters');
108
+ const query = {
109
+ key: flags.key,
110
+ ...parseObservationWindow(flags.window),
111
+ limit: flags.limit,
112
+ };
113
+ const baseline = flags['compare-to'] ? parseObservationWindow(flags['compare-to']) : null;
114
+ response = baseline
115
+ ? await commitments_1.commitmentApi.observationCompare(args.commitmentId, {
116
+ ...query,
117
+ baselineFrom: baseline.from,
118
+ baselineTo: baseline.to,
119
+ })
120
+ : await commitments_1.commitmentApi.observationSeries(args.commitmentId, query);
121
+ }
122
+ catch (err) {
123
+ this.handleApiError(err);
124
+ }
125
+ if (flags.json)
126
+ this.log(JSON.stringify(response, null, 2));
127
+ else
128
+ renderObservationWindow(response, (line) => this.log(line));
129
+ return;
130
+ }
131
+ if (flags.key)
132
+ this.error('--key requires --window');
49
133
  try {
50
134
  response = await commitments_1.commitmentApi.observations(args.commitmentId, {
51
135
  limit: flags.limit,
136
+ latest: flags.latest || undefined,
52
137
  declaredOnly: flags['declared-only'] || undefined,
53
138
  changedOnly: flags['changed-only'] || undefined,
54
139
  since: flags.since,
@@ -66,6 +151,7 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
66
151
  // facts; a filter that empties the list must not claim the first
67
152
  // (OSK-10953).
68
153
  const filters = [
154
+ flags.latest ? '--latest' : null,
69
155
  flags['declared-only'] ? '--declared-only' : null,
70
156
  flags['changed-only'] ? '--changed-only' : null,
71
157
  flags.since ? `--since ${flags.since}` : null,
@@ -73,6 +159,8 @@ class CommitmentsObservations extends base_command_1.BaseCommand {
73
159
  const emptyMessage = filters.length
74
160
  ? `No observations match ${filters.join(' ')}.`
75
161
  : 'No observations recorded.';
162
+ if (response?.truncated)
163
+ this.log(`TRUNCATED: ${response.omitted} current keys omitted; increase --limit (max 200).`);
76
164
  (0, commitments_1.renderCommitmentObservations)(response, (line) => this.log(line), { emptyMessage });
77
165
  }
78
166
  (0, commitments_1.renderListPageHint)(response, (line) => this.log(line));
@@ -1,7 +1,7 @@
1
1
  import { BaseCommand } from '../../base-command';
2
2
  /**
3
3
  * `skrr commitments pause <id>` — stop the control loop without losing the
4
- * contract or its history. The kill-switch for a noisy or misfiring commitment.
4
+ * contract or its history. An already executing turn may continue.
5
5
  */
6
6
  export default class CommitmentsPause extends BaseCommand {
7
7
  static description: string;
@@ -6,10 +6,10 @@ const web_url_1 = require("../../lib/web-url");
6
6
  const commitments_1 = require("../../lib/commitments");
7
7
  /**
8
8
  * `skrr commitments pause <id>` — stop the control loop without losing the
9
- * contract or its history. The kill-switch for a noisy or misfiring commitment.
9
+ * contract or its history. An already executing turn may continue.
10
10
  */
11
11
  class CommitmentsPause extends base_command_1.BaseCommand {
12
- static description = 'Pause a commitment — its control loop stops checking';
12
+ static description = 'Pause a commitment — stops new work; a turn already running may continue until it finishes or reaches its deadline';
13
13
  static examples = ['<%= config.bin %> commitments pause <commitment-id>'];
14
14
  static args = {
15
15
  id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
@@ -30,6 +30,7 @@ class CommitmentsPause extends base_command_1.BaseCommand {
30
30
  return;
31
31
  }
32
32
  this.log(`Paused commitment ${args.id}.`);
33
+ this.log('Stops new work. A turn already running may continue until it finishes or reaches its deadline.');
33
34
  // The status write is the fence and it has landed. The teardown of the
34
35
  // wake tree behind it is best-effort, and used to be silent — so a pause
35
36
  // that left triggers live looked exactly like one that did not. Say which
@@ -39,7 +40,7 @@ class CommitmentsPause extends base_command_1.BaseCommand {
39
40
  .teardown;
40
41
  if (teardown?.status === 'degraded') {
41
42
  this.warn('The control loop is stopped, but some scheduled wakes may not have been retired yet. ' +
42
- 'They cannot run anything — every wake is refused while this Commitment is paused — ' +
43
+ 'They cannot start new work — every wake is refused while this Commitment is paused — ' +
43
44
  `and the reconciler retries automatically. Check with: skrr commitments trigger-status ${args.id}`);
44
45
  }
45
46
  (0, commitments_1.renderCommitmentSummary)(commitment, (line) => this.log(line));
@@ -153,7 +153,8 @@ class CommitmentsPreflight extends base_command_1.BaseCommand {
153
153
  for (const endpoint of report.endpoints || []) {
154
154
  this.log(` ${endpoint.available ? 'ok ' : 'BLOCK'} ${endpoint.label || endpoint.destination}` +
155
155
  ` — ${(endpoint.capabilities || []).join(', ') || 'no capabilities'}` +
156
- (endpoint.reason ? ` (${endpoint.reason})` : ''));
156
+ (endpoint.reason ? ` (${endpoint.reason})` : '') +
157
+ (!endpoint.available && endpoint.detail ? ` — ${endpoint.detail}` : ''));
157
158
  }
158
159
  this.log(`\nAttention: ${report.attention?.allowed ? 'delivery allowed now' : `suppressed by ${report.attention?.suppressedBy || 'policy'}`}`);
159
160
  if (report.verdict === 'blocked')
@@ -19,7 +19,9 @@ export default class CommitmentsReports extends BaseCommand {
19
19
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
20
  add: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
21
  target: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ review: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
23
  on: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
+ format: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
25
  remove: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
26
  };
25
27
  run(): Promise<void>;
@@ -6,16 +6,18 @@ const base_command_1 = require("../../base-command");
6
6
  const commitments_1 = require("../../lib/commitments");
7
7
  const commitment_terms_1 = require("../../lib/commitment-terms");
8
8
  /** Destinations that address one specific place and therefore need a target. */
9
- const DESTINATIONS_NEEDING_TARGET = new Set(['slack_channel']);
9
+ const DESTINATIONS_NEEDING_TARGET = new Set(Object.values(data_provider_1.COMMITMENT_DESTINATION_REGISTRY)
10
+ .filter((destination) => destination.requiresTarget)
11
+ .map((destination) => destination.key));
10
12
  /**
11
13
  * Keep the CLI safe across a rolling data-provider build. Older installed
12
14
  * packages expose the compatibility destination list but not the narrowed
13
15
  * authoring list; derive the same restriction instead of crashing command
14
- * discovery or accidentally offering the inert email transport.
16
+ * discovery.
15
17
  */
16
18
  const AUTHORABLE_REPORT_DESTINATIONS = Array.isArray(data_provider_1.COMMITMENT_AUTHORABLE_REPORT_DESTINATIONS)
17
19
  ? data_provider_1.COMMITMENT_AUTHORABLE_REPORT_DESTINATIONS
18
- : data_provider_1.COMMITMENT_REPORT_DESTINATIONS.filter((destination) => destination !== 'email');
20
+ : data_provider_1.COMMITMENT_REPORT_DESTINATIONS;
19
21
  /**
20
22
  * Identity of a report target = destination + its target id. The same channel
21
23
  * may legitimately appear twice with different `on` conditions (a digest on
@@ -41,6 +43,7 @@ class CommitmentsReports extends base_command_1.BaseCommand {
41
43
  '<%= config.bin %> commitments reports <commitment-id>',
42
44
  '<%= config.bin %> commitments reports <commitment-id> --add slack_channel --target C0123456 --on material_change',
43
45
  '<%= config.bin %> commitments reports <commitment-id> --add main --on completion',
46
+ '<%= config.bin %> commitments reports <commitment-id> --add user_notify --target <user-id> --review owner',
44
47
  '<%= config.bin %> commitments reports <commitment-id> --remove slack_channel:C0123456',
45
48
  ];
46
49
  static args = {
@@ -53,12 +56,20 @@ class CommitmentsReports extends base_command_1.BaseCommand {
53
56
  options: [...AUTHORABLE_REPORT_DESTINATIONS],
54
57
  }),
55
58
  target: core_1.Flags.string({
56
- description: 'Channel id for slack_channel. App and owner-DM destinations need no target.',
59
+ description: 'Channel id for slack_channel, or the user id for user_notify. App, owner-DM and email destinations need no target (email goes to your verified account address).',
60
+ }),
61
+ review: core_1.Flags.string({
62
+ description: 'Hold the exact text for approver approval before it is sent to this target (owner)',
63
+ options: [...data_provider_1.COMMITMENT_REPORT_REVIEW_MODES],
57
64
  }),
58
65
  on: core_1.Flags.string({
59
66
  description: 'When to report (default: material_change)',
60
67
  options: [...data_provider_1.COMMITMENT_REPORT_TRIGGERS],
61
68
  }),
69
+ format: core_1.Flags.string({
70
+ description: 'Optional report format (absent: text)',
71
+ options: ['text', 'report_v1'],
72
+ }),
62
73
  remove: core_1.Flags.string({
63
74
  description: 'Report target to remove: "<destination>" or "<destination>:<target>"',
64
75
  }),
@@ -66,6 +77,8 @@ class CommitmentsReports extends base_command_1.BaseCommand {
66
77
  async run() {
67
78
  this.requireAuth();
68
79
  const { args, flags } = await this.parse(CommitmentsReports);
80
+ if (flags.format && !flags.add)
81
+ this.error('--format requires --add.', { exit: 1 });
69
82
  if (flags.add && flags.remove) {
70
83
  this.error('Pass --add or --remove, not both.', { exit: 1 });
71
84
  }
@@ -89,6 +102,9 @@ class CommitmentsReports extends base_command_1.BaseCommand {
89
102
  try {
90
103
  const current = await (0, commitment_terms_1.loadCommitment)(args.id);
91
104
  const reports = current.reports ?? [];
105
+ if (flags.review && !flags.add) {
106
+ throw new Error('--review applies to a report being added (--add).');
107
+ }
92
108
  if (flags.add) {
93
109
  const destination = flags.add;
94
110
  if (DESTINATIONS_NEEDING_TARGET.has(destination) && !flags.target) {
@@ -102,8 +118,10 @@ class CommitmentsReports extends base_command_1.BaseCommand {
102
118
  const next = {
103
119
  destination,
104
120
  ...(flags.target ? { destinationTarget: flags.target } : {}),
121
+ ...(flags.review ? { review: flags.review } : {}),
105
122
  on: flags.on ?? 'material_change',
106
123
  enabled: true,
124
+ ...(flags.format ? { format: flags.format } : {}),
107
125
  };
108
126
  payload = { reports: (0, commitment_terms_1.appendUnique)(reports, next, reportKey, 'Report target') };
109
127
  }
@@ -30,6 +30,7 @@ export default class CommitmentsUpdate extends BaseCommand {
30
30
  yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
31
  'from-json': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
32
32
  'if-version': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
+ 'task-review-policy': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
34
  title: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
35
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
36
  'agent-directive': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -1,7 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const core_1 = require("@oclif/core");
4
3
  const data_provider_1 = require("@skrr-ai/data-provider");
4
+ const core_1 = require("@oclif/core");
5
+ const commitment_product_1 = require("../../lib/commitment-product");
6
+ const data_provider_2 = require("@skrr-ai/data-provider");
5
7
  const base_command_1 = require("../../base-command");
6
8
  const prompt_1 = require("../../lib/prompt");
7
9
  const web_url_1 = require("../../lib/web-url");
@@ -52,6 +54,10 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
52
54
  'if-version': core_1.Flags.integer({
53
55
  description: 'Only update this observed contract version; returns a conflict if it changed',
54
56
  }),
57
+ 'task-review-policy': core_1.Flags.string({
58
+ options: [...data_provider_1.TASK_REVIEW_POLICIES],
59
+ description: 'Owner choice for generated Tasks; required records a blocker but does not stop the owner close.',
60
+ }),
55
61
  title: core_1.Flags.string({ description: 'New title' }),
56
62
  description: core_1.Flags.string({ description: 'New description' }),
57
63
  'agent-directive': core_1.Flags.string({
@@ -133,9 +139,22 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
133
139
  const wantsCadence = flags.cadence !== undefined ||
134
140
  flags['cadence-mode'] !== undefined ||
135
141
  flags.timezone !== undefined;
142
+ let payload;
143
+ try {
144
+ payload = await (0, prompt_2.readJsonBody)(flags['from-json']);
145
+ }
146
+ catch (err) {
147
+ this.error(err.message, { exit: 1 });
148
+ }
149
+ const authoredDelivery = payload.delivery && typeof payload.delivery === 'object' && !Array.isArray(payload.delivery)
150
+ ? payload.delivery
151
+ : undefined;
136
152
  // Read-modify-write: only pay the extra round-trip when we must merge.
137
153
  let current;
138
- if (wantsTarget || wantsCadence) {
154
+ if (wantsTarget ||
155
+ wantsCadence ||
156
+ authoredDelivery ||
157
+ flags['task-review-policy'] !== undefined) {
139
158
  try {
140
159
  const res = await commitments_1.commitmentApi.get(args.id);
141
160
  current = res?.commitment;
@@ -147,11 +166,22 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
147
166
  this.error(`Commitment ${args.id} not found.`, { exit: 4 });
148
167
  }
149
168
  }
150
- let payload;
151
169
  try {
152
- payload = await (0, prompt_2.readJsonBody)(flags['from-json']);
153
170
  if (flags['if-version'] !== undefined)
154
171
  payload.expectedVersion = flags['if-version'];
172
+ if (authoredDelivery || flags['task-review-policy'] !== undefined) {
173
+ payload.delivery = {
174
+ ...(current?.delivery || { version: 1, mode: 'report_only' }),
175
+ ...authoredDelivery,
176
+ ...(flags['task-review-policy'] !== undefined
177
+ ? { reviewPolicy: (0, commitment_product_1.normalizeTaskReviewPolicy)(flags['task-review-policy']) }
178
+ : {}),
179
+ };
180
+ payload.expectedVersion ??= current?.version;
181
+ }
182
+ const declaredDelivery = payload.delivery;
183
+ if (declaredDelivery?.reviewPolicy !== undefined)
184
+ (0, commitment_product_1.normalizeTaskReviewPolicy)(declaredDelivery.reviewPolicy);
155
185
  if (flags.title)
156
186
  payload.title = flags.title;
157
187
  if (flags.description !== undefined)
@@ -168,7 +198,7 @@ class CommitmentsUpdate extends base_command_1.BaseCommand {
168
198
  // generic PATCH with no ceremony at all, while `commitments delete` —
169
199
  // whose data is all still on disk, and which is now restorable —
170
200
  // prompts. Ceremony should track how hard something is to undo.
171
- if ((0, data_provider_1.isTerminalCommitmentStatus)(flags.status)) {
201
+ if ((0, data_provider_2.isTerminalCommitmentStatus)(flags.status)) {
172
202
  let confirmed;
173
203
  try {
174
204
  confirmed = await (0, prompt_1.confirmDestructive)(`Set ${args.id} to ${flags.status}? This ends the Commitment — making it active again means archiving it first, then resuming.`, flags.yes);
@@ -20,6 +20,7 @@ export default class GrantsCreate extends BaseCommand {
20
20
  commitment: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
21
  provider: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
22
22
  label: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ access: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
24
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
25
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
25
26
  daemon: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -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 grants_1 = require("../../lib/grants");
6
+ const data_provider_1 = require("@skrr-ai/data-provider");
6
7
  /**
7
8
  * `skrr grants create` — give an agent a credential.
8
9
  *
@@ -29,6 +30,10 @@ class GrantsCreate extends base_command_1.BaseCommand {
29
30
  }),
30
31
  provider: core_1.Flags.string({ description: 'Provider slug (e.g. aws, stripe)', required: true }),
31
32
  label: core_1.Flags.string({ description: 'Human label', required: true }),
33
+ access: core_1.Flags.string({
34
+ description: 'Advisory provider access class (declared, not verified)',
35
+ options: [...data_provider_1.GRANT_ACCESS_CLASSES],
36
+ }),
32
37
  description: core_1.Flags.string({ description: 'Optional description' }),
33
38
  env: core_1.Flags.string({
34
39
  description: 'Credential as NAME=VALUE (repeatable)',
@@ -67,6 +72,7 @@ class GrantsCreate extends base_command_1.BaseCommand {
67
72
  : { kind: 'commitment', commitmentId: flags.commitment },
68
73
  provider: flags.provider,
69
74
  label: flags.label,
75
+ ...(flags.access ? { access: flags.access } : {}),
70
76
  description: flags.description,
71
77
  credential: { kind: 'env', env },
72
78
  runtime: { sandbox: !flags['no-sandbox'], daemon: flags.daemon },
@@ -77,6 +83,8 @@ class GrantsCreate extends base_command_1.BaseCommand {
77
83
  return;
78
84
  }
79
85
  this.log(`Stored grant ${grant.id} (${grant.label}).`);
86
+ if (grant.access)
87
+ this.log(` access: ${(0, data_provider_1.describeGrantAccess)(grant.access, grant.accessSource).label}`);
80
88
  this.log(` variables: ${grant.credential?.envKeys.join(', ') || '—'}`);
81
89
  this.log(' Values are encrypted and cannot be read back.');
82
90
  }
@@ -4,6 +4,10 @@ import { BaseCommand } from '../../base-command';
4
4
  *
5
5
  * Replacement, never a merge: a partial update could leave half of a previous
6
6
  * key pair alive alongside the new one.
7
+ *
8
+ * A declared access class describes the OLD credential, so rotating clears it
9
+ * unless `--access` re-declares it for the new one (creator only). Installation-
10
+ * backed grants keep their attested access; it refreshes through the platform.
7
11
  */
8
12
  export default class GrantsRotate extends BaseCommand {
9
13
  static description: string;
@@ -14,6 +18,7 @@ export default class GrantsRotate extends BaseCommand {
14
18
  static flags: {
15
19
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
16
20
  env: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ access: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
22
  };
18
23
  run(): Promise<void>;
19
24
  }
@@ -2,12 +2,17 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const base_command_1 = require("../../base-command");
5
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const grants_1 = require("../../lib/grants");
6
7
  /**
7
8
  * `skrr grants rotate` — replace a credential wholesale.
8
9
  *
9
10
  * Replacement, never a merge: a partial update could leave half of a previous
10
11
  * key pair alive alongside the new one.
12
+ *
13
+ * A declared access class describes the OLD credential, so rotating clears it
14
+ * unless `--access` re-declares it for the new one (creator only). Installation-
15
+ * backed grants keep their attested access; it refreshes through the platform.
11
16
  */
12
17
  class GrantsRotate extends base_command_1.BaseCommand {
13
18
  static description = 'Rotate the credential on an existing grant';
@@ -24,6 +29,10 @@ class GrantsRotate extends base_command_1.BaseCommand {
24
29
  multiple: true,
25
30
  required: true,
26
31
  }),
32
+ access: core_1.Flags.string({
33
+ options: [...data_provider_1.GRANT_ACCESS_CLASSES],
34
+ description: 'Re-declare the access class of the NEW credential (creator only). Without it, rotation clears the declared class.',
35
+ }),
27
36
  };
28
37
  async run() {
29
38
  this.requireAuth();
@@ -35,13 +44,16 @@ class GrantsRotate extends base_command_1.BaseCommand {
35
44
  catch (err) {
36
45
  this.error(err.message);
37
46
  }
38
- const grant = await grants_1.grantApi.rotate(args.grantId, { kind: 'env', env });
47
+ const grant = await grants_1.grantApi.rotate(args.grantId, { kind: 'env', env }, flags.access);
39
48
  if (flags.json) {
40
49
  this.log(JSON.stringify(grant, null, 2));
41
50
  return;
42
51
  }
43
52
  this.log(`Rotated grant ${grant.id}.`);
44
53
  this.log(` variables: ${grant.credential?.envKeys.join(', ') || '—'}`);
54
+ if (!grant.access) {
55
+ this.log(' access: not declared (rotation clears the declared class; use --access to re-declare)');
56
+ }
45
57
  }
46
58
  }
47
59
  exports.default = GrantsRotate;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class GrantsUpdate extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
+ access: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../base-command");
6
+ const grants_1 = require("../../lib/grants");
7
+ class GrantsUpdate extends base_command_1.BaseCommand {
8
+ static description = 'Update a Grant advisory access class (creator only)';
9
+ static args = { id: core_1.Args.string({ ignoreStdin: true, required: true }) };
10
+ static flags = {
11
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
12
+ access: core_1.Flags.string({
13
+ required: true,
14
+ options: [...data_provider_1.GRANT_ACCESS_CLASSES],
15
+ description: 'Declared provider access; does not verify or alter the credential',
16
+ }),
17
+ };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { args, flags } = await this.parse(GrantsUpdate);
21
+ const grant = await grants_1.grantApi.updateAccess(args.id, flags.access);
22
+ this.log(flags.json
23
+ ? JSON.stringify(grant, null, 2)
24
+ : `${grant.id}: ${(0, data_provider_1.describeGrantAccess)(grant.access, grant.accessSource).label}`);
25
+ }
26
+ }
27
+ exports.default = GrantsUpdate;
@@ -13,7 +13,7 @@ class HostedMachinesStart extends base_command_1.BaseCommand {
13
13
  ];
14
14
  static flags = {
15
15
  agent: core_1.Flags.string({
16
- description: 'Associate machine session metadata with an agent id. Use `agents attach-harness` to make it the agent default.',
16
+ description: "Agent id (an agent you can view): applies that agent's declared network access. Use `agents attach-harness` to make it the agent default.",
17
17
  }),
18
18
  conversation: core_1.Flags.string({ description: 'Bind the machine to a conversation id' }),
19
19
  environment: core_1.Flags.string({ description: 'Hosted environment id' }),
@@ -154,6 +154,12 @@ class HostedMachinesStart extends base_command_1.BaseCommand {
154
154
  }
155
155
  const id = (0, hosted_machines_1.hostedMachineId)(session);
156
156
  this.log(`Hosted Machine ${id || '(unknown)'} ${(0, hosted_machines_1.hostedMachineStatus)(session)}.`);
157
+ if (session?.egressPolicy) {
158
+ this.log(`Network: ${session.egressPolicy} (${session.egressSource || 'source unknown'}; ${session.egressAllowHosts?.length || 0} hosts)`);
159
+ }
160
+ if (session?.egressDroppedHosts?.length) {
161
+ this.log(`Workspace policy dropped: ${session.egressDroppedHosts.join(', ')}`);
162
+ }
157
163
  if (session?.previewUrl)
158
164
  this.log(`Preview: ${session.previewUrl}`);
159
165
  if (!(0, hosted_machines_1.isHostedMachineReady)(session)) {
@@ -37,16 +37,16 @@ class MachinesList extends base_command_1.BaseCommand {
37
37
  catch (err) {
38
38
  this.handleApiError(err);
39
39
  }
40
+ const all = machines ?? [];
41
+ const shown = flags.all ? all : all.filter((m) => (0, machines_1.isReachableMachine)(m));
40
42
  if (flags.json) {
41
- this.log(JSON.stringify(machines, null, 2));
43
+ this.log(JSON.stringify(shown, null, 2));
42
44
  return;
43
45
  }
44
- if (!machines || machines.length === 0) {
46
+ if (all.length === 0) {
45
47
  this.log('No machines. Install a daemon with `skrr harnesses install-token`.');
46
48
  return;
47
49
  }
48
- const all = machines;
49
- const shown = flags.all ? all : all.filter((m) => (0, machines_1.isReachableMachine)(m));
50
50
  if (shown.length === 0) {
51
51
  this.log(`No machine is reachable right now (${(0, machines_1.hiddenMachinesSummary)(all)}) — see them with --all.`);
52
52
  return;
@@ -1,11 +1,5 @@
1
1
  import { BaseCommand } from '../../base-command';
2
- /**
3
- * `skrr measures confirm` — turn a claim into ground truth.
4
- *
5
- * This is the human-in-the-loop step whose `agentOutcome` equivalent shipped
6
- * with zero callers, which left every value claim in the system permanently
7
- * unverified.
8
- */
2
+ /** Confirm a reading and display the server-derived attribution. */
9
3
  export default class MeasuresConfirm extends BaseCommand {
10
4
  static description: string;
11
5
  static examples: string[];
@@ -1,17 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const measures_1 = require("../../lib/measures");
6
- /**
7
- * `skrr measures confirm` — turn a claim into ground truth.
8
- *
9
- * This is the human-in-the-loop step whose `agentOutcome` equivalent shipped
10
- * with zero callers, which left every value claim in the system permanently
11
- * unverified.
12
- */
7
+ /** Confirm a reading and display the server-derived attribution. */
13
8
  class MeasuresConfirm extends base_command_1.BaseCommand {
14
- static description = 'Confirm an observation (marks it as ground truth)';
9
+ static description = 'Confirm an observation with credential-derived evidence attribution';
15
10
  static examples = [
16
11
  '<%= config.bin %> measures confirm <measure-id> --at 2026-07-28T00:00:00Z',
17
12
  '<%= config.bin %> measures confirm <measure-id> --latest',
@@ -22,7 +17,9 @@ class MeasuresConfirm extends base_command_1.BaseCommand {
22
17
  static flags = {
23
18
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
24
19
  at: core_1.Flags.string({ description: 'observedAt of the reading to confirm (ISO-8601)' }),
25
- latest: core_1.Flags.boolean({ description: 'Confirm the most recent unconfirmed reading' }),
20
+ latest: core_1.Flags.boolean({
21
+ description: 'Confirm the most recent reading lacking person confirmation',
22
+ }),
26
23
  };
27
24
  async run() {
28
25
  this.requireAuth();
@@ -33,19 +30,23 @@ class MeasuresConfirm extends base_command_1.BaseCommand {
33
30
  this.error('Specify --at <iso-instant> or --latest.');
34
31
  }
35
32
  const measure = await measures_1.measureApi.get(args.measureId);
36
- const unconfirmed = measure.observations.filter((o) => !o.confirmed);
37
- if (unconfirmed.length === 0) {
38
- this.log('Nothing to confirm — every reading is already confirmed.');
33
+ const selection = (0, data_provider_1.latestConfirmableMeasureReading)(measure.observations);
34
+ if (selection.ambiguous) {
35
+ this.error('Multiple retained readings have the same timestamp; the existing confirmation endpoint cannot distinguish them. No confirmation was sent.');
36
+ }
37
+ const unconfirmed = selection.reading;
38
+ if (!unconfirmed) {
39
+ this.log('Nothing to confirm — no eligible reading lacking person confirmation is available.');
39
40
  return;
40
41
  }
41
- observedAt = unconfirmed[unconfirmed.length - 1].observedAt;
42
+ observedAt = unconfirmed.observedAt;
42
43
  }
43
44
  const measure = await measures_1.measureApi.confirm(args.measureId, observedAt);
44
45
  if (flags.json) {
45
46
  this.log(JSON.stringify(measure, null, 2));
46
47
  return;
47
48
  }
48
- this.log(`Confirmed. ${measure.rollup.confirmedCount}/${measure.rollup.observationCount} readings are ground truth.`);
49
+ this.log((0, measures_1.recordConfirmationForObservation)(measure.observations.find((o) => Date.parse(o.observedAt) === Date.parse(observedAt))).message);
49
50
  }
50
51
  }
51
52
  exports.default = MeasuresConfirm;