@skrr-ai/cli 0.1.104 → 0.1.105

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 (110) hide show
  1. package/dist/base-command.js +6 -2
  2. package/dist/commands/agenda/trigger/list.js +2 -0
  3. package/dist/commands/agents/chat.js +4 -1
  4. package/dist/commands/agents/operating-plan.js +5 -2
  5. package/dist/commands/agents/triggers/create.js +3 -1
  6. package/dist/commands/agents/triggers/fire.js +2 -0
  7. package/dist/commands/agents/triggers/replay.js +2 -0
  8. package/dist/commands/agents/triggers/update.js +12 -4
  9. package/dist/commands/commitments/action-history.js +7 -2
  10. package/dist/commands/commitments/cadence-decisions.js +6 -1
  11. package/dist/commands/commitments/cadence-requests/decide.d.ts +1 -1
  12. package/dist/commands/commitments/cadence-requests/decide.js +1 -1
  13. package/dist/commands/commitments/doctor.js +7 -1
  14. package/dist/commands/commitments/observations.js +7 -1
  15. package/dist/commands/commitments/operating-review.d.ts +15 -0
  16. package/dist/commands/commitments/operating-review.js +23 -2
  17. package/dist/commands/commitments/preflight.d.ts +12 -0
  18. package/dist/commands/commitments/preflight.js +23 -9
  19. package/dist/commands/commitments/reflection.js +6 -1
  20. package/dist/commands/commitments/show.js +3 -1
  21. package/dist/commands/commitments/trigger-status.js +2 -22
  22. package/dist/commands/commitments/wake-capabilities.d.ts +13 -0
  23. package/dist/commands/commitments/wake-capabilities.js +39 -3
  24. package/dist/commands/daemon/logs.d.ts +41 -3
  25. package/dist/commands/daemon/logs.js +202 -17
  26. package/dist/commands/followups/act.js +13 -1
  27. package/dist/commands/followups/checkin.js +11 -2
  28. package/dist/commands/followups/reschedule.js +13 -1
  29. package/dist/commands/proposals/discard.d.ts +1 -1
  30. package/dist/commands/proposals/keep.d.ts +1 -1
  31. package/dist/commands/proposals/list.d.ts +7 -0
  32. package/dist/commands/proposals/list.js +31 -5
  33. package/dist/commands/proposals/show.d.ts +2 -1
  34. package/dist/commands/proposals/show.js +31 -4
  35. package/dist/commands/proposals/snooze.d.ts +1 -1
  36. package/dist/commands/requests/create.js +8 -3
  37. package/dist/commands/spaces/workflows/create.js +3 -3
  38. package/dist/commands/tasks/complete.js +4 -0
  39. package/dist/commands/tasks/create.d.ts +2 -0
  40. package/dist/commands/tasks/create.js +17 -3
  41. package/dist/commands/tasks/deliverable/add.d.ts +2 -0
  42. package/dist/commands/tasks/deliverable/add.js +22 -7
  43. package/dist/commands/tasks/deliverable/list.d.ts +13 -0
  44. package/dist/commands/tasks/deliverable/list.js +34 -1
  45. package/dist/commands/tasks/economics-compare.js +7 -1
  46. package/dist/commands/tasks/expectations.d.ts +10 -0
  47. package/dist/commands/tasks/expectations.js +31 -3
  48. package/dist/commands/tasks/list.d.ts +9 -0
  49. package/dist/commands/tasks/list.js +40 -5
  50. package/dist/commands/tasks/report.js +1 -1
  51. package/dist/commands/tasks/result/show.d.ts +7 -0
  52. package/dist/commands/tasks/result/show.js +32 -1
  53. package/dist/commands/tasks/result/submit.js +1 -1
  54. package/dist/commands/tasks/show.d.ts +6 -0
  55. package/dist/commands/tasks/show.js +23 -0
  56. package/dist/commands/tasks/update.d.ts +3 -0
  57. package/dist/commands/tasks/update.js +23 -3
  58. package/dist/commands/triggers/audit.js +6 -2
  59. package/dist/commands/triggers/create.js +3 -1
  60. package/dist/commands/triggers/fire.d.ts +1 -0
  61. package/dist/commands/triggers/fire.js +16 -1
  62. package/dist/commands/triggers/preflight.js +6 -5
  63. package/dist/commands/triggers/replay.js +2 -0
  64. package/dist/commands/triggers/update.js +12 -4
  65. package/dist/commands/workspaces/stewardship.js +5 -1
  66. package/dist/lib/agentic-stream.d.ts +9 -0
  67. package/dist/lib/agentic-stream.js +38 -18
  68. package/dist/lib/commitment-event-wakes.d.ts +8 -0
  69. package/dist/lib/commitment-event-wakes.js +39 -0
  70. package/dist/lib/commitment-ledgers.d.ts +9 -0
  71. package/dist/lib/commitment-ledgers.js +126 -0
  72. package/dist/lib/commitments.d.ts +102 -2
  73. package/dist/lib/commitments.js +208 -8
  74. package/dist/lib/delegated-cli.js +13 -2
  75. package/dist/lib/followups.d.ts +6 -0
  76. package/dist/lib/followups.js +12 -0
  77. package/dist/lib/message-content.d.ts +6 -0
  78. package/dist/lib/message-content.js +24 -0
  79. package/dist/lib/operating-plan-loops.d.ts +8 -0
  80. package/dist/lib/operating-plan-loops.js +32 -0
  81. package/dist/lib/preflight-copy.d.ts +27 -0
  82. package/dist/lib/preflight-copy.js +89 -0
  83. package/dist/lib/proposal-decision-command.d.ts +1 -1
  84. package/dist/lib/proposal-decision-command.js +27 -3
  85. package/dist/lib/proposals.d.ts +17 -0
  86. package/dist/lib/proposals.js +41 -0
  87. package/dist/lib/stewardship-output.js +57 -12
  88. package/dist/lib/task-economics-line.d.ts +30 -0
  89. package/dist/lib/task-economics-line.js +49 -0
  90. package/dist/lib/task-economics.d.ts +15 -0
  91. package/dist/lib/task-economics.js +73 -8
  92. package/dist/lib/task-recurrence-outcome.d.ts +19 -0
  93. package/dist/lib/task-recurrence-outcome.js +30 -0
  94. package/dist/lib/triggers.d.ts +42 -0
  95. package/dist/lib/triggers.js +136 -2
  96. package/dist/lib/widgetTerminalText.d.ts +8 -0
  97. package/dist/lib/widgetTerminalText.js +116 -0
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +33 -0
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +27 -0
  100. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.d.ts +28 -5
  101. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/telemetryRedaction.js +50 -5
  102. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +33 -0
  103. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +26 -0
  104. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.d.ts +28 -5
  105. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/telemetryRedaction.js +50 -5
  106. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  107. package/dist/node_modules/@skrr-ai/data-provider/index.js +7261 -7056
  108. package/docs/v1/operations.md +1 -1
  109. package/oclif.manifest.json +116 -72
  110. package/package.json +1 -1
@@ -1,24 +1,51 @@
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");
5
4
  const base_command_1 = require("../../base-command");
6
5
  const proposals_1 = require("../../lib/proposals");
7
6
  class ProposalsShow extends base_command_1.BaseCommand {
8
7
  static description = 'Show one viewer-scoped proposal and its frozen revision';
8
+ static examples = [
9
+ '<%= config.bin %> proposals show request <proposal-id>',
10
+ '<%= config.bin %> proposals show request:<proposal-id>',
11
+ ];
12
+ // The kind is validated in `resolveProposalRef`, not by oclif, so the KIND:ID that `proposals list`
13
+ // prints can be pasted whole (OSK-15371).
9
14
  static args = {
10
- kind: core_1.Args.string({ required: true, options: [...data_provider_1.PROPOSAL_KINDS], ignoreStdin: true }),
11
- id: core_1.Args.string({ required: true, ignoreStdin: true }),
15
+ kind: core_1.Args.string({
16
+ required: true,
17
+ description: 'Proposal kind, or the KIND:ID that `proposals list` prints',
18
+ ignoreStdin: true,
19
+ }),
20
+ id: core_1.Args.string({ required: false, description: 'Proposal id', ignoreStdin: true }),
12
21
  };
13
22
  static flags = { json: core_1.Flags.boolean() };
14
23
  async run() {
15
24
  this.requireAuth();
16
25
  const { args } = await this.parse(ProposalsShow);
26
+ const ref = (0, proposals_1.resolveProposalRef)(args.kind, args.id, this.config?.bin);
27
+ if ('error' in ref) {
28
+ this.failWithCliError({
29
+ message: ref.error,
30
+ code: 'PROPOSAL_REF_INVALID',
31
+ exit: 2,
32
+ retryable: false,
33
+ });
34
+ }
17
35
  let result;
18
36
  try {
19
- result = await proposals_1.proposalApi.show(args.kind, args.id);
37
+ result = await proposals_1.proposalApi.show(ref.kind, ref.id);
20
38
  }
21
39
  catch (error) {
40
+ if (error?.status === 404) {
41
+ this.failWithCliError({
42
+ message: (0, proposals_1.describeProposalNotFound)(ref.kind, ref.id, this.config?.bin),
43
+ code: 'PROPOSAL_NOT_FOUND',
44
+ status: 404,
45
+ exit: 4,
46
+ retryable: false,
47
+ });
48
+ }
22
49
  this.handleApiError(error);
23
50
  }
24
51
  this.log(JSON.stringify(result, null, 2));
@@ -3,7 +3,7 @@ export default class ProposalsSnooze extends ProposalDecisionCommand {
3
3
  static description: string;
4
4
  static args: {
5
5
  kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
- id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
+ id: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
7
7
  };
8
8
  static decision: "snooze";
9
9
  }
@@ -10,10 +10,15 @@ function hasRequestTarget(payload) {
10
10
  }
11
11
  class RequestsCreate extends base_command_1.BaseCommand {
12
12
  static description = 'Create a new request';
13
+ // Every example names a recipient: a request without one is refused, so an example that omits it
14
+ // is a command that cannot run (OSK-15375).
13
15
  static examples = [
14
- '<%= config.bin %> requests create --title "Approve budget" --type approval',
15
- '<%= config.bin %> requests create --title "Q1 review" --type task --description "Review Q1 goals"',
16
- '<%= config.bin %> requests create --from-json request.json',
16
+ '<%= config.bin %> requests create --title "Approve budget" --type approval --target-user <user-id>',
17
+ '<%= config.bin %> requests create --title "Q1 review" --type task --description "Review Q1 goals" --target-agent <agent-id>',
18
+ `cat > request.json <<'JSON'
19
+ { "title": "Approve budget", "type": "approval", "targetUserId": "<user-id>" }
20
+ JSON
21
+ <%= config.bin %> requests create --from-json request.json`,
17
22
  ];
18
23
  static flags = {
19
24
  json: core_1.Flags.boolean({ description: 'Output the created request as JSON' }),
@@ -29,15 +29,15 @@ class SpacesWorkflowsCreate extends base_command_1.BaseCommand {
29
29
  title: core_1.Flags.string({ required: true, description: 'Workflow title' }),
30
30
  description: core_1.Flags.string({ description: 'Optional description' }),
31
31
  graph: core_1.Flags.string({
32
- description: 'Required exactly once with --draft/--starter: path to graph JSON ({ nodes, edges }); inspect config with spaces workflows node-types',
32
+ description: 'One of --graph, --draft or --starter (exactly one): path to graph JSON ({ nodes, edges }); inspect config with spaces workflows node-types',
33
33
  exactlyOne: ['graph', 'draft', 'starter'],
34
34
  }),
35
35
  draft: core_1.Flags.string({
36
- description: 'Required exactly once with --graph/--starter: path to complete Canvas draft JSON ({ semantic, presentation })',
36
+ description: 'One of --graph, --draft or --starter (exactly one): path to complete Canvas draft JSON ({ semantic, presentation })',
37
37
  exactlyOne: ['graph', 'draft', 'starter'],
38
38
  }),
39
39
  starter: core_1.Flags.boolean({
40
- description: 'Required exactly once with --graph/--draft: create a valid one-node starter graph',
40
+ description: 'One of --graph, --draft or --starter (exactly one): create a valid one-node starter graph',
41
41
  exactlyOne: ['graph', 'draft', 'starter'],
42
42
  }),
43
43
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
@@ -38,6 +38,7 @@ const core_1 = require("@oclif/core");
38
38
  const base_command_1 = require("../../base-command");
39
39
  const task_closure_1 = require("../../lib/task-closure");
40
40
  const api_fetch_1 = require("../../lib/api-fetch");
41
+ const task_recurrence_outcome_1 = require("../../lib/task-recurrence-outcome");
41
42
  const outbox_1 = require("../../lib/outbox");
42
43
  const prompt_1 = require("../../lib/prompt");
43
44
  const session_task_write_1 = require("../../lib/session-task-write");
@@ -356,6 +357,9 @@ class TasksComplete extends base_command_1.BaseCommand {
356
357
  for (const sentence of (0, task_closure_1.closureSnapshotLines)(result.task?.resolution?.closure)) {
357
358
  this.log(sentence);
358
359
  }
360
+ const recurrenceLine = (0, task_recurrence_outcome_1.recurrenceOutcomeLine)(result.recurrence);
361
+ if (recurrenceLine)
362
+ this.log(recurrenceLine);
359
363
  for (const advisory of result.advisories || [])
360
364
  this.warn(advisory.message);
361
365
  if (url)
@@ -71,6 +71,8 @@ export default class TasksCreate extends BaseCommand {
71
71
  per: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
72
72
  effort: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
73
73
  risk: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
74
+ confidence: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
75
+ rationale: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
74
76
  estimate: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
75
77
  tag: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
76
78
  'label-id': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -226,13 +226,27 @@ class TasksCreate extends base_command_1.BaseCommand {
226
226
  }),
227
227
  'start-date': core_1.Flags.string({ description: 'Planned start date (ISO string)' }),
228
228
  'due-date': core_1.Flags.string({ description: 'Due date (ISO string)' }),
229
- savings: core_1.Flags.string({ description: 'Expected USD savings (requires --per)' }),
229
+ savings: core_1.Flags.string({
230
+ description: 'Expected USD savings (requires --per). An estimate you write as a person is recorded as Human confirmed under your name, and an Agent edit is then refused with a 409; an Agent writes an Agent estimate. Re-sending the same values changes nothing.',
231
+ }),
230
232
  per: core_1.Flags.string({
231
233
  options: ['month', 'year', 'once'],
232
234
  description: 'Savings period (requires --savings)',
233
235
  }),
234
- effort: core_1.Flags.string({ description: 'Expected effort in hours, nonnegative' }),
235
- risk: core_1.Flags.string({ options: ['low', 'medium', 'high'], description: 'Expected risk' }),
236
+ effort: core_1.Flags.string({
237
+ description: 'Expected effort in hours, nonnegative (part of the estimate; see --savings)',
238
+ }),
239
+ risk: core_1.Flags.string({
240
+ options: ['low', 'medium', 'high'],
241
+ description: 'Expected risk (part of the estimate; see --savings)',
242
+ }),
243
+ confidence: core_1.Flags.string({
244
+ options: ['low', 'medium', 'high'],
245
+ description: 'How sure the estimate is (part of the estimate; see --savings). An earlier confidence stays on the claim until you replace it.',
246
+ }),
247
+ rationale: core_1.Flags.string({
248
+ description: 'Why the estimate is what it is, up to 500 characters (part of the estimate). An earlier rationale stays on the claim, under its new basis, until you replace it.',
249
+ }),
236
250
  estimate: core_1.Flags.integer({
237
251
  // Names the prerequisite AND how to satisfy it. The first version of this
238
252
  // help said the scale was reachable only from the web UI, which was true
@@ -46,3 +46,5 @@ export default class TasksDeliverableAdd extends BaseCommand {
46
46
  */
47
47
  private archive;
48
48
  }
49
+ /** The sentence for a `--status` an archived copy overrides, or null when nothing was overridden. */
50
+ export declare function statusNotApplied(requestedStatus: string | undefined): string | null;
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.defaultDeliverableIdempotencyKey = defaultDeliverableIdempotencyKey;
37
+ exports.statusNotApplied = statusNotApplied;
37
38
  const fs = __importStar(require("node:fs"));
38
39
  const path = __importStar(require("node:path"));
39
40
  const core_1 = require("@oclif/core");
@@ -277,7 +278,15 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
277
278
  `Run skrr tasks deliverable list ${opts.taskId} --json to find the exact version before submitting a Result.`);
278
279
  }
279
280
  if (opts.json) {
280
- this.log(JSON.stringify({ ...resourceReceipt, archived, identity: opts.identity }, null, 2));
281
+ // `--status` is overridden for an archived copy, and --json used to say nothing of it: the
282
+ // receipt's observation read `available` and the requested status appeared nowhere (OSK-15437).
283
+ const notes = statusNotApplied(opts.requestedStatus);
284
+ this.log(JSON.stringify({
285
+ ...resourceReceipt,
286
+ archived,
287
+ identity: opts.identity,
288
+ ...(notes ? { requestedStatus: opts.requestedStatus, notes: [notes] } : {}),
289
+ }, null, 2));
281
290
  return true;
282
291
  }
283
292
  const recorded = resourceReceipt?.link.id
@@ -286,16 +295,22 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
286
295
  const version = resourceReceipt?.version.id ? `version ${resourceReceipt.version.id}; ` : '';
287
296
  this.log(`${recorded} — archived copy ${asset.id} ` +
288
297
  `(${version}sha256 pinned; observed available; self reported).`);
289
- if (opts.requestedStatus && opts.requestedStatus !== 'available') {
290
- // Not silent: the platform WATCHED these bytes land, which is a stronger
291
- // and verified statement than the one that was asked for. Who vouched
292
- // is unchanged — still `self_reported`, still nobody else's word.
293
- this.log(`--status ${opts.requestedStatus} did not apply: an archived copy is observed available.`);
294
- }
298
+ const notApplied = statusNotApplied(opts.requestedStatus);
299
+ // Not silent: the platform WATCHED these bytes land, which is a stronger
300
+ // and verified statement than the one that was asked for. Who vouched
301
+ // is unchanged — still `self_reported`, still nobody else's word.
302
+ if (notApplied)
303
+ this.log(notApplied);
295
304
  return true;
296
305
  }
297
306
  }
298
307
  exports.default = TasksDeliverableAdd;
308
+ /** The sentence for a `--status` an archived copy overrides, or null when nothing was overridden. */
309
+ function statusNotApplied(requestedStatus) {
310
+ return requestedStatus && requestedStatus !== 'available'
311
+ ? `--status ${requestedStatus} did not apply: an archived copy is observed available.`
312
+ : null;
313
+ }
299
314
  /**
300
315
  * The local file a coordinate already names, if it names one. `location` is
301
316
  * checked first because it is where the thing IS; `identity` is the fallback
@@ -1,4 +1,17 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * Which listed Deliverables are an EARLIER version of one that is also listed.
4
+ *
5
+ * v1 and v2 of the same coordinate share a Resource and both read `available`, so nothing said which
6
+ * was current (OSK-15437). The newest observation of a Resource is its current version; a tie, or a
7
+ * row with no observation time, is left unmarked rather than guessed. Returns link id -> the link id
8
+ * of the newer version.
9
+ */
10
+ export declare function earlierVersions(items: Array<{
11
+ linkId?: string;
12
+ resourceId?: string;
13
+ observedAt?: string;
14
+ }>): Map<string, string>;
2
15
  export default class TasksDeliverableList extends BaseCommand {
3
16
  static description: string;
4
17
  static args: {
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.earlierVersions = earlierVersions;
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
@@ -37,6 +38,35 @@ function versionLabel(item) {
37
38
  const id = (item.resourceVersionId || '').replace(/^wrv_/, '');
38
39
  return id ? `version ${id.slice(0, 12)}` : 'version unpinned';
39
40
  }
41
+ /**
42
+ * Which listed Deliverables are an EARLIER version of one that is also listed.
43
+ *
44
+ * v1 and v2 of the same coordinate share a Resource and both read `available`, so nothing said which
45
+ * was current (OSK-15437). The newest observation of a Resource is its current version; a tie, or a
46
+ * row with no observation time, is left unmarked rather than guessed. Returns link id -> the link id
47
+ * of the newer version.
48
+ */
49
+ function earlierVersions(items) {
50
+ const newest = new Map();
51
+ const times = (item) => Date.parse(item.observedAt ?? '');
52
+ for (const item of items) {
53
+ const at = times(item);
54
+ if (!item.linkId || !item.resourceId || Number.isNaN(at))
55
+ continue;
56
+ const current = newest.get(item.resourceId);
57
+ if (!current || at > current.at)
58
+ newest.set(item.resourceId, { linkId: item.linkId, at });
59
+ }
60
+ const marks = new Map();
61
+ for (const item of items) {
62
+ const at = times(item);
63
+ const latest = item.resourceId ? newest.get(item.resourceId) : undefined;
64
+ if (item.linkId && latest && !Number.isNaN(at) && at < latest.at) {
65
+ marks.set(item.linkId, latest.linkId);
66
+ }
67
+ }
68
+ return marks;
69
+ }
40
70
  class TasksDeliverableList extends base_command_1.BaseCommand {
41
71
  static description = 'List Deliverables with exact version, location, and evidence state';
42
72
  static args = {
@@ -61,11 +91,14 @@ class TasksDeliverableList extends base_command_1.BaseCommand {
61
91
  return this.log(JSON.stringify(result, null, 2));
62
92
  if (!result.data.length)
63
93
  return this.log('No Deliverables recorded.');
94
+ const earlier = earlierVersions(result.data);
64
95
  for (const item of result.data) {
96
+ const newer = item.linkId ? earlier.get(item.linkId) : undefined;
65
97
  this.log(`${item.linkId} · ${item.kind}${item.format ? `/${item.format}` : ''} · ` +
66
98
  `${item.title || item.identity || '(untitled)'}\n` +
67
99
  ` ${item.location || 'no locator'} · ${fidelityLabel(item.versionFidelity)} · ` +
68
- `${versionLabel(item)} · ${item.status} · ${item.evidenceLevel}`);
100
+ `${versionLabel(item)} · ${item.status} · ${item.evidenceLevel}` +
101
+ (newer ? `\n an earlier version of this Deliverable; the newer one is ${newer}` : ''));
69
102
  }
70
103
  }
71
104
  }
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  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
+ const task_economics_1 = require("../../lib/task-economics");
6
7
  class TasksEconomicsCompare extends base_command_1.BaseCommand {
7
8
  static description = 'Show a Task economics estimate beside retained platform readings (no measurement binding is created)';
8
9
  static examples = [
@@ -28,6 +29,11 @@ class TasksEconomicsCompare extends base_command_1.BaseCommand {
28
29
  async run() {
29
30
  this.requireAuth();
30
31
  const { args, flags } = await this.parse(TasksEconomicsCompare);
32
+ // Named by flag here; the server names the same faults by parameter (OSK-15368).
33
+ if (flags.fire.length > 2)
34
+ this.error('Give --fire once or twice: at most two readings can be compared.', { exit: 2 });
35
+ if (new Set(flags.fire).size !== flags.fire.length)
36
+ this.error('The two --fire ids must differ.', { exit: 2 });
31
37
  const taskRef = await this.resolveEntityRef('task', args.id);
32
38
  let response;
33
39
  try {
@@ -45,7 +51,7 @@ class TasksEconomicsCompare extends base_command_1.BaseCommand {
45
51
  return;
46
52
  }
47
53
  const readings = response.platformReadings;
48
- this.log(`Estimate (revision ${response.economicsRevision}): ${JSON.stringify(response.economics)}`);
54
+ this.log(`Estimate (revision ${response.economicsRevision}): ${(0, task_economics_1.describeEstimate)(response.economics)}`);
49
55
  if (readings.status === 'unavailable') {
50
56
  this.log(`Platform readings unavailable: ${readings.reason}`);
51
57
  }
@@ -1,4 +1,14 @@
1
+ import type { JudgedExpectation } from '@skrr-ai/data-provider';
1
2
  import { BaseCommand } from '../../base-command';
3
+ export declare function evidenceGap(expectation: JudgedExpectation): string;
4
+ /**
5
+ * A judgement made about a Result that is no longer the current one (OSK-15431).
6
+ *
7
+ * An assessment stands for the specification, not for one Result, so submitting a superseding Result
8
+ * does not retire it, and the count after the v2 submit included judgements of v1 with nothing to say
9
+ * so. Only true when the assessment named a Result and the Task has a different current one.
10
+ */
11
+ export declare function judgedOnEarlierResult(expectation: JudgedExpectation, currentResultId: string | null | undefined): boolean;
2
12
  export default class TasksExpectations extends BaseCommand {
3
13
  static description: string;
4
14
  static examples: string[];
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.evidenceGap = evidenceGap;
4
+ exports.judgedOnEarlierResult = judgedOnEarlierResult;
3
5
  const core_1 = require("@oclif/core");
4
6
  const data_provider_1 = require("@skrr-ai/data-provider");
5
7
  const data_provider_2 = require("@skrr-ai/data-provider");
@@ -59,6 +61,11 @@ function evidenceGap(expectation) {
59
61
  const { provided, required } = expectation.evidenceShortfall;
60
62
  return ` (vouched at ${provided}, asked for ${required})`;
61
63
  }
64
+ // `owed` reads "not produced yet"; when a deliverable of the right kind is recorded but does not
65
+ // qualify, say why instead of implying nothing was produced (OSK-15457).
66
+ if (expectation.state === 'owed' && expectation.nearMiss) {
67
+ return ` (one was recorded, but ${expectation.nearMiss})`;
68
+ }
62
69
  if (!PINNED_STATES.has(expectation.state))
63
70
  return '';
64
71
  if (!expectation.evidenceLevel || !expectation.requiredEvidence)
@@ -85,7 +92,19 @@ function assessorSuffix(expectation) {
85
92
  return '';
86
93
  return ` (${(0, task_closure_1.describeAssessor)(expectation.assessedByKind)})`;
87
94
  }
88
- function line(expectation) {
95
+ /**
96
+ * A judgement made about a Result that is no longer the current one (OSK-15431).
97
+ *
98
+ * An assessment stands for the specification, not for one Result, so submitting a superseding Result
99
+ * does not retire it, and the count after the v2 submit included judgements of v1 with nothing to say
100
+ * so. Only true when the assessment named a Result and the Task has a different current one.
101
+ */
102
+ function judgedOnEarlierResult(expectation, currentResultId) {
103
+ return Boolean(expectation.assessedResultId &&
104
+ currentResultId &&
105
+ expectation.assessedResultId !== currentResultId);
106
+ }
107
+ function line(expectation, currentResultId) {
89
108
  const label = (STATE_LABEL[expectation.state] || expectation.state) + assessorSuffix(expectation);
90
109
  const from = expectation.source === 'space' ? ' [space default]' : '';
91
110
  /*
@@ -98,7 +117,10 @@ function line(expectation) {
98
117
  * thing is owed. The fallback is for a row that predates the resolved field.
99
118
  */
100
119
  const sentence = expectation.sentence || expectation.text;
101
- return ` ${expectation.id.padEnd(18)} ${label.padEnd(22)} ${sentence}${evidenceGap(expectation)}${from}`;
120
+ const earlier = judgedOnEarlierResult(expectation, currentResultId)
121
+ ? ' (judged on an earlier Result)'
122
+ : '';
123
+ return ` ${expectation.id.padEnd(18)} ${label.padEnd(22)} ${sentence}${evidenceGap(expectation)}${earlier}${from}`;
102
124
  }
103
125
  class TasksExpectations extends base_command_1.BaseCommand {
104
126
  static description = 'Show what must be true before the Task can close and the evidence-backed assessment state';
@@ -142,7 +164,13 @@ class TasksExpectations extends base_command_1.BaseCommand {
142
164
  }
143
165
  this.log('Expectations:');
144
166
  for (const expectation of acceptance.expectations) {
145
- this.log(line(expectation));
167
+ this.log(line(expectation, acceptance.currentResultId));
168
+ }
169
+ const carried = acceptance.expectations.filter((e) => judgedOnEarlierResult(e, acceptance.currentResultId)).length;
170
+ if (carried > 0) {
171
+ this.log(`${carried} judgement${carried === 1 ? ' was' : 's were'} made on an earlier Result. ` +
172
+ `${carried === 1 ? 'It still counts' : 'They still count'}: an assessment stands for the ` +
173
+ 'Task specification, not for one Result. Re-assess if the current Result changes the answer.');
146
174
  }
147
175
  const gating = acceptance.expectations.filter((e) => e.gating);
148
176
  // `met: true` is the GATE's answer — a waiver unblocks closure exactly as a
@@ -35,6 +35,15 @@ export declare function emptyTasksMessage(flags: ListFlags, bin?: string): strin
35
35
  * naming the stage sat unread in the same payload.
36
36
  */
37
37
  export declare function nextActionText(t: Record<string, unknown>): string;
38
+ /**
39
+ * The NEXT column's width. Bounded, because a sentence in an unbounded column wraps the table into
40
+ * unreadability on a narrow terminal, but widened when a row says what a Task was closed over: the
41
+ * two things it names come last in the sentence, and a column cut before them hid exactly the
42
+ * disclosure the row exists to make (OSK-15429).
43
+ */
44
+ export declare function nextColumnWidth(rows: Array<{
45
+ next: string;
46
+ }>): number;
38
47
  /**
39
48
  * The REF column's value, aliased to `ref` under `--json` because `identifier`
40
49
  * is the one name an agent scanning 43 keys does not guess (OSK-10692).
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.agentSessionOpenDefault = agentSessionOpenDefault;
4
4
  exports.emptyTasksMessage = emptyTasksMessage;
5
5
  exports.nextActionText = nextActionText;
6
+ exports.nextColumnWidth = nextColumnWidth;
6
7
  exports.taskRef = taskRef;
7
8
  exports.buildListParams = buildListParams;
8
9
  exports.statusFilterWarnings = statusFilterWarnings;
@@ -13,6 +14,7 @@ const web_url_1 = require("../../lib/web-url");
13
14
  const format_1 = require("../../lib/format");
14
15
  const list_output_1 = require("../../lib/list-output");
15
16
  const tasks_1 = require("../../lib/tasks");
17
+ const task_economics_1 = require("../../lib/task-economics");
16
18
  /**
17
19
  * Names of `tasks list` flags that narrow the result set. Used to distinguish
18
20
  * "no tasks anywhere" from "no tasks matching your filters" — the first case
@@ -174,9 +176,38 @@ function statusTypeList(flags) {
174
176
  */
175
177
  function nextActionText(t) {
176
178
  const actionability = t.actionability;
177
- return actionability
178
- ? `${String(actionability.nextActor ?? '-')}: ${String(actionability.reason || actionability.reasonCode || actionability.state || '-')}`
179
- : String(t.executionMode ?? '-');
179
+ if (!actionability)
180
+ return String(t.executionMode ?? '-');
181
+ // A finished Task has no next action, which is all this column said about one closed over unmet
182
+ // Expectations, a missing required Review, or past a Review by explicit force ("none: Task workflow
183
+ // type is completed"). The list row already carries the persisted closure snapshot, so say what it
184
+ // was closed over with the shared renderer (OSK-15429). Only when something is owed or overridden:
185
+ // a clean close reads as before.
186
+ if (actionability.state === 'terminal') {
187
+ const closed = closedOverText(t);
188
+ if (closed)
189
+ return `${String(actionability.nextActor ?? '-')}: closed. ${closed}`;
190
+ }
191
+ return `${String(actionability.nextActor ?? '-')}: ${String(actionability.reason || actionability.reasonCode || actionability.state || '-')}`;
192
+ }
193
+ /** What a finished Task's persisted closure snapshot says was overridden or is still owed, or null. */
194
+ function closedOverText(t) {
195
+ const closure = t.resolution?.closure;
196
+ const outstanding = (0, data_provider_1.describeClosureOutstanding)(closure);
197
+ const parts = [
198
+ ...(0, data_provider_1.describeClosureOverrides)(closure),
199
+ ...(outstanding ? [`Outstanding at close: ${outstanding}.`] : []),
200
+ ];
201
+ return parts.length > 0 ? parts.join(' ') : null;
202
+ }
203
+ /**
204
+ * The NEXT column's width. Bounded, because a sentence in an unbounded column wraps the table into
205
+ * unreadability on a narrow terminal, but widened when a row says what a Task was closed over: the
206
+ * two things it names come last in the sentence, and a column cut before them hid exactly the
207
+ * disclosure the row exists to make (OSK-15429).
208
+ */
209
+ function nextColumnWidth(rows) {
210
+ return rows.some((row) => row.next.includes(': closed. ')) ? 140 : 46;
180
211
  }
181
212
  /**
182
213
  * The REF column's value, aliased to `ref` under `--json` because `identifier`
@@ -647,6 +678,7 @@ class TasksList extends base_command_1.BaseCommand {
647
678
  : 'Unknown',
648
679
  effort: economics?.effortHours !== undefined ? `${economics.effortHours}h` : 'Unknown',
649
680
  risk: economics?.risk ?? 'Unknown',
681
+ score: (0, task_economics_1.economicsScoreText)(economics),
650
682
  basis: economics?.basis && Object.prototype.hasOwnProperty.call(basis, economics.basis)
651
683
  ? basis[economics.basis]
652
684
  : 'Unknown',
@@ -662,6 +694,7 @@ class TasksList extends base_command_1.BaseCommand {
662
694
  { key: 'savings', header: 'SAVINGS' },
663
695
  { key: 'effort', header: 'EFFORT' },
664
696
  { key: 'risk', header: 'RISK' },
697
+ { key: 'score', header: 'SCORE' },
665
698
  { key: 'basis', header: 'BASIS' },
666
699
  ]
667
700
  : []),
@@ -669,14 +702,16 @@ class TasksList extends base_command_1.BaseCommand {
669
702
  { key: 'assignees', header: 'ASSIGNEES' },
670
703
  // Bounded: the reason is a sentence, and an unbounded column wraps the
671
704
  // table into unreadability on a narrow terminal.
672
- { key: 'next', header: 'NEXT', maxWidth: 46 },
705
+ { key: 'next', header: 'NEXT', maxWidth: nextColumnWidth(rows) },
673
706
  // Only meaningful on a search — the server computes it from the same
674
707
  // relevance rank that orders the rows, and a plain listing has none.
675
708
  ...(flags.q ? [{ key: 'match', header: 'MATCH' }] : []),
676
709
  { key: 'id', header: 'ID' },
677
710
  ], (line) => this.log(line));
678
711
  if (flags.rank)
679
- this.log('\nRanked only within this fetched page; estimates are claims, not measured savings.');
712
+ this.log('\nRanked only within this fetched page; estimates are claims, not measured savings.\n' +
713
+ 'SCORE = annualized USD savings / max(effort hours, 0.5) x risk factor (low 1, medium 0.6, high 0.25); ' +
714
+ '— means savings, effort or risk is missing, so the row is not scored and sorts last.');
680
715
  if (r?.next_cursor) {
681
716
  this.log('');
682
717
  this.log(`Next page: --cursor ${r.next_cursor}`);
@@ -171,7 +171,7 @@ class TasksReport extends base_command_1.BaseCommand {
171
171
  this.log(JSON.stringify({ ...result, url }, null, 2));
172
172
  return;
173
173
  }
174
- this.log(`Submitted Result v${result.result.version} on task ${task.id}.${url ? ` ${url}` : ''}`);
174
+ this.log(`Submitted Result v${result.result.version} (${result.result.id}) on task ${task.id}.${url ? ` ${url}` : ''}`);
175
175
  }
176
176
  }
177
177
  exports.default = TasksReport;
@@ -1,4 +1,11 @@
1
+ import type { TaskResultRecord } from '@skrr-ai/data-provider';
1
2
  import { BaseCommand } from '../../../base-command';
3
+ /**
4
+ * What a reviewer needs to accept ONE exact Result (OSK-15430). `result show` printed the summary and
5
+ * the conclusion, and kept the Result id (the value `tasks review --result-id` requires), the linked
6
+ * deliverables, the evidence, the remaining work and the Result it supersedes for `--json`.
7
+ */
8
+ export declare function describeResultDetail(current: TaskResultRecord, all?: TaskResultRecord[]): string[];
2
9
  export default class TasksResultShow extends BaseCommand {
3
10
  static description: string;
4
11
  static args: {
@@ -1,10 +1,39 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeResultDetail = describeResultDetail;
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
6
7
  const task_resolver_1 = require("../../../lib/task-resolver");
7
8
  const task_legacy_1 = require("../../../lib/task-legacy");
9
+ /**
10
+ * What a reviewer needs to accept ONE exact Result (OSK-15430). `result show` printed the summary and
11
+ * the conclusion, and kept the Result id (the value `tasks review --result-id` requires), the linked
12
+ * deliverables, the evidence, the remaining work and the Result it supersedes for `--json`.
13
+ */
14
+ function describeResultDetail(current, all = []) {
15
+ const lines = [];
16
+ if (current.supersedesResultId) {
17
+ const previous = all.find((row) => row.id === current.supersedesResultId);
18
+ lines.push(`Supersedes: ${current.supersedesResultId}${previous ? ` (v${previous.version})` : ''}`);
19
+ }
20
+ const links = current.resourceLinkIds ?? [];
21
+ lines.push(`Deliverables: ${links.length > 0 ? links.join(', ') : 'none linked'}`);
22
+ const evidence = current.evidenceRefs ?? [];
23
+ lines.push(`Evidence: ${evidence.length > 0
24
+ ? evidence.map((ref) => `${ref.kind}:${ref.id}`).join(', ')
25
+ : 'none attached'}`);
26
+ const remaining = current.remainingWork ?? [];
27
+ if (remaining.length > 0) {
28
+ lines.push('Remaining work:');
29
+ for (const item of remaining)
30
+ lines.push(` - ${item.summary}`);
31
+ }
32
+ else {
33
+ lines.push('Remaining work: none declared');
34
+ }
35
+ return lines;
36
+ }
8
37
  class TasksResultShow extends base_command_1.BaseCommand {
9
38
  static description = 'Show the current Task Result and its exact Review';
10
39
  static args = {
@@ -32,9 +61,11 @@ class TasksResultShow extends base_command_1.BaseCommand {
32
61
  if (!current)
33
62
  return this.log(await (0, task_legacy_1.emptyCanonicalView)(task.id, 'Result'));
34
63
  const review = result.reviews.find((row) => row.resultId === current.id);
35
- this.log(`Result v${current.version}\n${current.summary}`);
64
+ this.log(`Result v${current.version} ${current.id}\n${current.summary}`);
36
65
  if (current.conclusion)
37
66
  this.log(`Conclusion (${current.conclusion.type}): ${JSON.stringify(current.conclusion.value)}`);
67
+ for (const line of describeResultDetail(current, result.results))
68
+ this.log(line);
38
69
  // Only "awaiting" while a review can still be recorded. A closed Task
39
70
  // refuses one outright (`TASK_REOPEN_REQUIRED`), so the old wording named a
40
71
  // state nothing could ever leave — and a reader could not tell it from a
@@ -211,7 +211,7 @@ class TasksResultSubmit extends base_command_1.BaseCommand {
211
211
  // managed instructions told the agent to read the status the call returns,
212
212
  // which the call did not carry. Submitting a Result is not closing a Task;
213
213
  // the response now reports where it stands and this prints exactly that.
214
- this.log(`Submitted Result v${result.result.version} for ${task.id}.`);
214
+ this.log(`Submitted Result v${result.result.version} (${result.result.id}) for ${task.id}.`);
215
215
  for (const line of (0, task_closure_1.closureReport)(result.closure, { closing: false }))
216
216
  this.log(line);
217
217
  if (url)
@@ -50,6 +50,12 @@ export declare function completionLines(projection: {
50
50
  } | null | undefined, { read }?: {
51
51
  read?: boolean;
52
52
  }): string[];
53
+ /**
54
+ * The review-policy line, or null for the default. Only a non-default policy is printed: `none` is
55
+ * every Task that never asked, and printing it would add a line to all of them. Without this the
56
+ * policy was readable in --json alone (OSK-15434).
57
+ */
58
+ export declare function reviewPolicyLine(policy: unknown): string | null;
53
59
  export declare function labelsLine(labelIds?: string[], nameById?: Map<string, string>): string;
54
60
  /**
55
61
  * The `Executor:` value — who does this task, and whether anyone said so.