@skrr-ai/cli 0.1.97 → 0.1.99

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 (62) hide show
  1. package/dist/base-command.d.ts +7 -0
  2. package/dist/base-command.js +10 -1
  3. package/dist/commands/agents/create.js +3 -3
  4. package/dist/commands/agents/update.js +22 -3
  5. package/dist/commands/commitments/create.js +29 -8
  6. package/dist/commands/commitments/observations.js +3 -1
  7. package/dist/commands/commitments/update.js +69 -14
  8. package/dist/commands/machines/dedicated/show.js +1 -0
  9. package/dist/commands/machines/show.js +13 -4
  10. package/dist/commands/signals/monitors/create.d.ts +2 -2
  11. package/dist/commands/signals/monitors/create.js +2 -2
  12. package/dist/commands/signals/monitors/list.d.ts +2 -2
  13. package/dist/commands/signals/monitors/list.js +2 -2
  14. package/dist/commands/signals/monitors/pause.d.ts +2 -2
  15. package/dist/commands/signals/monitors/pause.js +2 -2
  16. package/dist/commands/signals/monitors/resume.d.ts +2 -2
  17. package/dist/commands/signals/monitors/resume.js +2 -2
  18. package/dist/commands/signals/monitors/revise.d.ts +2 -2
  19. package/dist/commands/signals/monitors/revise.js +2 -2
  20. package/dist/commands/signals/monitors/show.d.ts +2 -2
  21. package/dist/commands/signals/monitors/show.js +2 -2
  22. package/dist/commands/signals/monitors/test.d.ts +2 -2
  23. package/dist/commands/signals/monitors/test.js +2 -2
  24. package/dist/commands/signals/monitors/transitions.d.ts +2 -2
  25. package/dist/commands/signals/monitors/transitions.js +2 -2
  26. package/dist/commands/signals/monitors/validate.d.ts +2 -2
  27. package/dist/commands/signals/monitors/validate.js +2 -2
  28. package/dist/commands/signals/schemas/list.d.ts +2 -2
  29. package/dist/commands/signals/schemas/list.js +2 -2
  30. package/dist/commands/signals/schemas/show.d.ts +2 -2
  31. package/dist/commands/signals/schemas/show.js +2 -2
  32. package/dist/commands/signals/sources/list.d.ts +2 -2
  33. package/dist/commands/signals/sources/list.js +2 -2
  34. package/dist/commands/signals/sources/receipts.d.ts +2 -2
  35. package/dist/commands/signals/sources/receipts.js +2 -2
  36. package/dist/commands/signals/sources/show.d.ts +2 -2
  37. package/dist/commands/signals/sources/show.js +2 -2
  38. package/dist/commands/tasks/assign.js +3 -4
  39. package/dist/commands/tasks/complete.d.ts +1 -0
  40. package/dist/commands/tasks/complete.js +1 -0
  41. package/dist/commands/tasks/create.js +4 -2
  42. package/dist/commands/tasks/deliverable/add.d.ts +1 -0
  43. package/dist/commands/tasks/deliverable/add.js +1 -0
  44. package/dist/commands/tasks/result/submit.d.ts +1 -0
  45. package/dist/commands/tasks/result/submit.js +1 -0
  46. package/dist/commands/tasks/resume/save.d.ts +1 -0
  47. package/dist/commands/tasks/resume/save.js +1 -0
  48. package/dist/commands/tasks/retry.js +38 -6
  49. package/dist/commands/tasks/show.js +3 -1
  50. package/dist/commands/tasks/update.js +31 -4
  51. package/dist/commands/tasks/updates/add.d.ts +1 -0
  52. package/dist/commands/tasks/updates/add.js +1 -0
  53. package/dist/lib/commitments.js +34 -2
  54. package/dist/lib/dedicated-machines.d.ts +5 -7
  55. package/dist/lib/dedicated-machines.js +19 -10
  56. package/dist/lib/signals.js +22 -13
  57. package/dist/lib/space-usage.js +1 -1
  58. package/dist/node_modules/@skrr-ai/data-provider/index.js +35 -19
  59. package/dist/signal-command.d.ts +11 -0
  60. package/dist/signal-command.js +15 -0
  61. package/oclif.manifest.json +1273 -24
  62. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { BaseCommand } from '../../../base-command';
2
- export default class SourcesList extends BaseCommand {
1
+ import { SignalCommand } from '../../../signal-command';
2
+ export default class SourcesList extends SignalCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const base_command_1 = require("../../../base-command");
4
+ const signal_command_1 = require("../../../signal-command");
5
5
  const signals_1 = require("../../../lib/signals");
6
- class SourcesList extends base_command_1.BaseCommand {
6
+ class SourcesList extends signal_command_1.SignalCommand {
7
7
  static description = 'List Signal sources (read-only; credentials are never shown)';
8
8
  static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
9
9
  async run() {
@@ -1,5 +1,5 @@
1
- import { BaseCommand } from '../../../base-command';
2
- export default class SourcesReceipts extends BaseCommand {
1
+ import { SignalCommand } from '../../../signal-command';
2
+ export default class SourcesReceipts extends SignalCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const base_command_1 = require("../../../base-command");
4
+ const signal_command_1 = require("../../../signal-command");
5
5
  const signals_1 = require("../../../lib/signals");
6
- class SourcesReceipts extends base_command_1.BaseCommand {
6
+ class SourcesReceipts extends signal_command_1.SignalCommand {
7
7
  static description = 'List recent redacted receipts of a Signal source';
8
8
  static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
9
9
  static args = { id: core_1.Args.string({ required: true, ignoreStdin: true }) };
@@ -1,5 +1,5 @@
1
- import { BaseCommand } from '../../../base-command';
2
- export default class SourcesShow extends BaseCommand {
1
+ import { SignalCommand } from '../../../signal-command';
2
+ export default class SourcesShow extends SignalCommand {
3
3
  static description: string;
4
4
  static flags: {
5
5
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const base_command_1 = require("../../../base-command");
4
+ const signal_command_1 = require("../../../signal-command");
5
5
  const signals_1 = require("../../../lib/signals");
6
- class SourcesShow extends base_command_1.BaseCommand {
6
+ class SourcesShow extends signal_command_1.SignalCommand {
7
7
  static description = 'Show one Signal source (read-only)';
8
8
  static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
9
9
  static args = { id: core_1.Args.string({ required: true, ignoreStdin: true }) };
@@ -196,11 +196,10 @@ class TasksAssign extends base_command_1.BaseCommand {
196
196
  if (flags.json) {
197
197
  this.log(JSON.stringify({
198
198
  ...(0, web_url_1.withTaskUrl)(this.cliConfig.baseURL, updated),
199
- // What each free-text reference actually became — so a caller can
200
- // verify the resolution rather than trust it.
199
+ // Keep request details separate from the canonical assignee arrays
200
+ // returned by the server, especially when removing co-assignees.
201
+ assignmentAction: flags.remove ? 'remove' : flags.replace ? 'replace' : 'add',
201
202
  resolved: [...people.resolved, ...agents.resolved],
202
- assigneeUserIds: userIds,
203
- assigneeAgentIds: agentIds,
204
203
  }, null, 2));
205
204
  return;
206
205
  }
@@ -10,6 +10,7 @@ export declare function describePendingReview({ statusType, taskId, resultId, bi
10
10
  }>;
11
11
  }): string | null;
12
12
  export default class TasksComplete extends BaseCommand {
13
+ static autonomousCredentialPolicy: "bound-session";
13
14
  static description: string;
14
15
  static examples: string[];
15
16
  static args: {
@@ -99,6 +99,7 @@ function describePendingReview({ statusType, taskId, resultId, bin, advisories,
99
99
  'may close without one — an override is recorded on the resolution.)');
100
100
  }
101
101
  class TasksComplete extends base_command_1.BaseCommand {
102
+ static autonomousCredentialPolicy = 'bound-session';
102
103
  static description = 'Submit one versioned Result and advance the Task workflow';
103
104
  static examples = [
104
105
  '<%= config.bin %> tasks complete <task> --summary "Implemented and verified the fix" --deliverable <resource-link-id>',
@@ -159,9 +159,11 @@ class TasksCreate extends base_command_1.BaseCommand {
159
159
  description: 'Task priority',
160
160
  options: [...tasks_1.TASK_PRIORITIES],
161
161
  }),
162
- parent: core_1.Flags.string({ description: 'Parent task ID' }),
162
+ parent: core_1.Flags.string({
163
+ description: 'Structural parent task ID. Child statuses roll up; all terminal children can automatically close the parent, with unmet obligations recorded. Use --spawned-by for supporting work without status rollup.',
164
+ }),
163
165
  'spawned-by': core_1.Flags.string({
164
- description: 'Task whose execution produced this one (lineage). Distinct from --parent (containment) and blockers (ordering).',
166
+ description: 'Task whose execution produced this one (lineage only, without parent status rollup). Use --parent for structural containment and blockers for ordering.',
165
167
  }),
166
168
  'spawn-reason': core_1.Flags.string({
167
169
  description: 'Why this work exists',
@@ -10,6 +10,7 @@ export declare function defaultDeliverableIdempotencyKey(task: string, payload:
10
10
  * optional structured one.
11
11
  */
12
12
  export default class TasksDeliverableAdd extends BaseCommand {
13
+ static autonomousCredentialPolicy: "bound-session";
13
14
  static description: string;
14
15
  static examples: string[];
15
16
  static args: {
@@ -59,6 +59,7 @@ function defaultDeliverableIdempotencyKey(task, payload) {
59
59
  * optional structured one.
60
60
  */
61
61
  class TasksDeliverableAdd extends base_command_1.BaseCommand {
62
+ static autonomousCredentialPolicy = 'bound-session';
62
63
  static description = 'Record something this task produced (a commit, a page, a URL, a file, a message, a dataset).\n' +
63
64
  '\n' +
64
65
  'IDENTITY is the part before @; LOCATION is the part after it. They are not the same ' +
@@ -1,5 +1,6 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
2
  export default class TasksResultSubmit extends BaseCommand {
3
+ static autonomousCredentialPolicy: "bound-session";
3
4
  static description: string;
4
5
  static examples: string[];
5
6
  static args: {
@@ -53,6 +53,7 @@ function parseJson(value, label) {
53
53
  }
54
54
  }
55
55
  class TasksResultSubmit extends base_command_1.BaseCommand {
56
+ static autonomousCredentialPolicy = 'bound-session';
56
57
  static description = 'Submit the Task Result; this does not self-accept it or turn a domain conclusion into a Review';
57
58
  static examples = [
58
59
  '<%= config.bin %> tasks result submit <task> --summary "NO-SHIP until Gates A-D pass" --conclusion-type release_recommendation --conclusion-json \'{"posture":"no_ship"}\'',
@@ -1,5 +1,6 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
2
  export default class TasksResumeSave extends BaseCommand {
3
+ static autonomousCredentialPolicy: "bound-session";
3
4
  static description: string;
4
5
  static args: {
5
6
  task: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
@@ -9,6 +9,7 @@ const api_fetch_1 = require("../../../lib/api-fetch");
9
9
  const session_task_write_1 = require("../../../lib/session-task-write");
10
10
  const prompt_1 = require("../../../lib/prompt");
11
11
  class TasksResumeSave extends base_command_1.BaseCommand {
12
+ static autonomousCredentialPolicy = 'bound-session';
12
13
  static description = 'Save recovery state without publishing a visible Task Update';
13
14
  static args = {
14
15
  task: core_1.Args.string({ required: false, ignoreStdin: true, description: 'Task id or ref' }),
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const task_resolver_1 = require("../../lib/task-resolver");
7
+ const start_1 = require("./start");
7
8
  /**
8
9
  * OSK-3815 — the CLI verb for the `retry_task` action.
9
10
  *
@@ -72,8 +73,9 @@ class TasksRetry extends base_command_1.BaseCommand {
72
73
  // Step 1 — clear the terminal state. `todo` rather than `backlog`: the task
73
74
  // was already triaged into the working set once, and a retry is not a
74
75
  // decision to deprioritise it.
76
+ let movedTask;
75
77
  try {
76
- await data_provider_1.dataService.moveTask({
78
+ movedTask = await data_provider_1.dataService.moveTask({
77
79
  taskId: taskRef,
78
80
  status: 'todo',
79
81
  });
@@ -81,13 +83,23 @@ class TasksRetry extends base_command_1.BaseCommand {
81
83
  catch (err) {
82
84
  this.handleApiError(err, { subject: `Task ${taskRef}` });
83
85
  }
86
+ // This receipt describes the first committed transition. Dispatch may
87
+ // change the Task again, so it is not an overall current-state snapshot.
88
+ const statusTransition = { requestedStatus: 'todo', task: movedTask };
84
89
  if (flags['no-start']) {
85
90
  if (flags.json) {
86
- this.log(JSON.stringify({ taskId: taskRef, status: 'todo', started: false }));
91
+ this.log(JSON.stringify({
92
+ taskId: taskRef,
93
+ status: movedTask?.status,
94
+ started: false,
95
+ statusTransition,
96
+ }));
87
97
  return;
88
98
  }
89
- this.log(`Cleared failed state on ${taskRef} — now todo, not started.`);
90
- this.log(`Start it with: skrr tasks start ${taskRef}`);
99
+ this.log(`Cleared failed state on ${taskRef}; execution was not requested.`);
100
+ if (typeof movedTask?.status === 'string')
101
+ this.log(`Status: ${movedTask.status}`);
102
+ this.log(`Start it with: ${this.config.bin} tasks start ${taskRef}`);
91
103
  return;
92
104
  }
93
105
  let result;
@@ -95,12 +107,32 @@ class TasksRetry extends base_command_1.BaseCommand {
95
107
  result = await data_provider_1.dataService.startTask(taskRef, flags.skill ? { skillId: flags.skill } : undefined);
96
108
  }
97
109
  catch (err) {
110
+ const waiting = (0, start_1.waitingForMachine)(err);
111
+ if (waiting) {
112
+ if (flags.json) {
113
+ this.log(JSON.stringify({
114
+ waiting: true,
115
+ code: 'TASK_WAITING_FOR_MACHINE',
116
+ taskId: taskRef,
117
+ started: false,
118
+ statusTransition,
119
+ ...waiting,
120
+ }, null, 2));
121
+ return;
122
+ }
123
+ this.log(`Task ${taskRef} is waiting for the machine to start; the task will run by itself once it is ready.`);
124
+ if (waiting.message)
125
+ this.log(waiting.message);
126
+ return;
127
+ }
128
+ const observedStatus = typeof movedTask?.status === 'string' ? ` (returned status '${movedTask.status}')` : '';
98
129
  this.handleApiError(err, {
99
- subject: `Task ${taskRef} (failed state was cleared — it is now 'todo')`,
130
+ subject: `Task ${taskRef} (status transition committed${observedStatus}; start was not confirmed. ` +
131
+ `Check ${this.config.bin} tasks show ${taskRef}, then use ${this.config.bin} tasks start ${taskRef} if needed)`,
100
132
  });
101
133
  }
102
134
  if (flags.json) {
103
- this.log(JSON.stringify({ ...result, taskId: taskRef, status: 'todo', started: true }, null, 2));
135
+ this.log(JSON.stringify({ ...result, taskId: taskRef, started: true, statusTransition }, null, 2));
104
136
  return;
105
137
  }
106
138
  this.log(`Retried task ${taskRef}`);
@@ -518,7 +518,9 @@ class TasksShow extends base_command_1.BaseCommand {
518
518
  : ''}`);
519
519
  const actionability = t.actionability;
520
520
  if (actionability) {
521
- this.log(`Next action: ${actionability.nextActor ?? '-'} · ${actionability.state ?? '-'} · ${actionability.reasonCode ?? '-'}`);
521
+ this.log(actionability.state === 'terminal'
522
+ ? 'Workflow: ended · no next executor'
523
+ : `Next action: ${actionability.nextActor ?? '-'} · ${actionability.state ?? '-'} · ${actionability.reasonCode ?? '-'}`);
522
524
  this.log(`Why: ${actionability.reason ?? '-'}`);
523
525
  await this.printBlockerDetail(t, actionability);
524
526
  }
@@ -308,20 +308,47 @@ class TasksUpdate extends base_command_1.BaseCommand {
308
308
  const field = expectationsPatch !== undefined || body.expectations !== undefined
309
309
  ? 'Changing Expectations'
310
310
  : 'Changing the description';
311
- this.log(`${field} advances the Task specification, so ${count} ${noun} ` +
312
- 'will stop counting and must be recorded again.');
311
+ const disclosure = [
312
+ `${field} advances the Task specification, so ${count} ${noun} ` +
313
+ 'will stop counting and must be recorded again.',
314
+ ];
313
315
  /*
314
316
  * NAME them. The count alone made the warning unactionable — and it is
315
317
  * task-WIDE, so Expectations you are not touching lose their judgements
316
318
  * too, which a bare number actively hides.
317
319
  */
318
320
  for (const row of settled) {
319
- this.log(` ${row.id} ${row.sentence || row.text || ''}`);
321
+ disclosure.push(` ${row.id} ${row.sentence || row.text || ''}`);
320
322
  }
321
323
  if (settled.length > 0) {
322
- this.log('Revocation covers every Expectation on this Task, including ones this edit ' +
324
+ disclosure.push('Revocation covers every Expectation on this Task, including ones this edit ' +
323
325
  'does not touch.');
324
326
  }
327
+ if (flags.json) {
328
+ // JSON callers acknowledge with --yes. Neither disclosure prose nor
329
+ // an interactive prompt may precede the single machine-readable result.
330
+ this.failWithCliError({
331
+ message: disclosure.join('\n') +
332
+ '\nNo change made — re-run with `--yes` to accept the revocation.',
333
+ code: 'TASK_SPEC_CHANGE_CONFIRMATION_REQUIRED',
334
+ exit: 2,
335
+ retryable: false,
336
+ details: {
337
+ taskId: taskRef,
338
+ assessmentCount: acceptanceUnreadable ? null : settled.length,
339
+ acceptanceReadable: !acceptanceUnreadable,
340
+ affectedExpectations: settled.map((row) => ({
341
+ id: row.id,
342
+ text: row.sentence || row.text || '',
343
+ })),
344
+ revocationScope: 'all_expectations',
345
+ confirmationFlag: '--yes',
346
+ disclosure,
347
+ },
348
+ });
349
+ }
350
+ for (const line of disclosure)
351
+ this.log(line);
325
352
  const ok = await this.confirm('Continue? [y/N] ');
326
353
  if (!ok) {
327
354
  /*
@@ -1,5 +1,6 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
2
  export default class TasksUpdatesAdd extends BaseCommand {
3
+ static autonomousCredentialPolicy: "bound-session";
3
4
  static description: string;
4
5
  static examples: string[];
5
6
  static args: {
@@ -60,6 +60,7 @@ function parseJsonObject(value, label) {
60
60
  return parsed;
61
61
  }
62
62
  class TasksUpdatesAdd extends base_command_1.BaseCommand {
63
+ static autonomousCredentialPolicy = 'bound-session';
63
64
  static description = 'Record a meaningful Task Update, optionally with recovery state';
64
65
  static examples = [
65
66
  '<%= config.bin %> tasks updates add <task> --type progress --summary "Migration review complete" --data \'{"completedSinceLast":["Checked migrations 001-014"],"nextActions":["Review authorization"]}\'',
@@ -1037,6 +1037,23 @@ columns, empty, log) {
1037
1037
  }
1038
1038
  (0, format_1.renderTable)(rows, columns, log);
1039
1039
  }
1040
+ function describeRunCount(usage, key) {
1041
+ const value = usage?.[key];
1042
+ const status = usage?.[`${key}Status`];
1043
+ if (status === 'unknown' ||
1044
+ typeof value !== 'number' ||
1045
+ !Number.isSafeInteger(value) ||
1046
+ value < 0) {
1047
+ return 'unknown';
1048
+ }
1049
+ // A native session proves a floor, not the provider's complete internal
1050
+ // activity. Preserve that distinction in the human ledger, too.
1051
+ if (status === 'lower_bound')
1052
+ return `at least ${value}`;
1053
+ if (status !== undefined && status !== null)
1054
+ return 'unknown';
1055
+ return String(value);
1056
+ }
1040
1057
  /** Human view for `commitments ledger`; JSON remains the automation surface. */
1041
1058
  function renderCommitmentLedger(ledger, log) {
1042
1059
  const checks = records(ledger.checks);
@@ -1073,12 +1090,16 @@ function renderCommitmentLedger(ledger, log) {
1073
1090
  id: text(item.id),
1074
1091
  status: text(item.status),
1075
1092
  attempt: `${text(item.attempt, '0')}/${text(item.maxAttempts, '-')}`,
1093
+ turns: describeRunCount(record(item.usage), 'turns'),
1094
+ tools: describeRunCount(record(item.usage), 'toolCallCount'),
1076
1095
  reason: text(firstValue(item, ['reason', 'error']), ''),
1077
1096
  finished: text(firstValue(item, ['completedAt', 'updatedAt', 'startedAt'])),
1078
1097
  })), [
1079
1098
  { key: 'id', header: 'RUN ID', verbatim: true },
1080
1099
  { key: 'status', header: 'STATUS', maxWidth: 12 },
1081
1100
  { key: 'attempt', header: 'ATTEMPT' },
1101
+ { key: 'turns', header: 'TURNS' },
1102
+ { key: 'tools', header: 'TOOLS' },
1082
1103
  { key: 'reason', header: 'REASON', maxWidth: 36 },
1083
1104
  { key: 'finished', header: 'LAST UPDATED', maxWidth: 24 },
1084
1105
  ], 'No runs recorded.', log);
@@ -1247,13 +1268,24 @@ function formatCommitmentCycles(page) {
1247
1268
  */
1248
1269
  function formatCycleLatency(cycle) {
1249
1270
  const latency = cycle.latency;
1271
+ const history = cycle.historyDetail;
1272
+ const disclosure = history?.state === 'compacted'
1273
+ ? [
1274
+ ` Detailed history was compacted${history.at ? ` at ${history.at}` : ''}; available reason and source timestamps were retained.`,
1275
+ ]
1276
+ : history?.state === 'unavailable'
1277
+ ? [' Detailed history is unavailable; why it is missing was not recorded.']
1278
+ : [];
1250
1279
  const missingFor = (stage) => (latency?.pendingStages ? latency.pendingStages.includes(stage) : latency?.pending)
1251
1280
  ? 'pending'
1252
1281
  : 'unmeasurable';
1253
1282
  if (latency?.measured === false) {
1254
- return [` latency: ${latency.pending ? 'pending' : 'unmeasurable'}`];
1283
+ return [...disclosure, ` latency: ${latency.pending ? 'pending' : 'unmeasurable'}`];
1255
1284
  }
1256
- return Object.entries(latency?.stages || {}).map(([stage, ms]) => ` ${stage}: ${typeof ms === 'number' ? `${ms}ms` : missingFor(stage)}`);
1285
+ return [
1286
+ ...disclosure,
1287
+ ...Object.entries(latency?.stages || {}).map(([stage, ms]) => ` ${stage === 'event_to_turn' ? 'event_to_turn (server handoff)' : stage}: ${typeof ms === 'number' ? `${ms}ms` : missingFor(stage)}`),
1288
+ ];
1257
1289
  }
1258
1290
  const ANSWER_GLYPH = { yes: '✓', no: '✗', 'n/a': '–' };
1259
1291
  /** The six questions for the latest Cycle, the first ✗ carrying its fix. */
@@ -633,15 +633,13 @@ export declare function dedicatedLeaseDisplayName(lease: DedicatedLease | Harnes
633
633
  /** `$12.34` — cents are what the API speaks; dollars are what a person reads. */
634
634
  export declare function formatUsdCents(cents: number | null | undefined): string;
635
635
  /**
636
- * `$1.23 this month for machines of this type · $500.00 cap for this machine`,
637
- * or why that sentence cannot be written.
636
+ * Name the monthly total that the machine's configured cap is checked against.
638
637
  *
639
- * The two figures are different scopes and the sentence has to say so. The spend
640
- * is a shared pot the server computes for (owner, team, machineProduct), so every
638
+ * The spend is a shared pot the server computes for (owner, team, machineProduct), so every
641
639
  * lease of the same product reports the same number — a machine created minutes
642
- * ago with no active seconds still reports the month's total. The cap is this
643
- * lease's own. Printed as "$1.23 this month of $500.00 cap" the only available
644
- * reading was that this machine had spent it.
640
+ * ago with no active seconds still reports the month's total. The cap is set on
641
+ * this lease, but admission compares it against that shared projected total.
642
+ * An old response without spendScope cannot establish who owns the total.
645
643
  */
646
644
  export declare function dedicatedSpendSummary(lease: DedicatedLease): string;
647
645
  /**
@@ -673,15 +673,13 @@ function formatUsdCents(cents) {
673
673
  return `${sign}$${(Math.abs(cents) / 100).toFixed(2)}`;
674
674
  }
675
675
  /**
676
- * `$1.23 this month for machines of this type · $500.00 cap for this machine`,
677
- * or why that sentence cannot be written.
676
+ * Name the monthly total that the machine's configured cap is checked against.
678
677
  *
679
- * The two figures are different scopes and the sentence has to say so. The spend
680
- * is a shared pot the server computes for (owner, team, machineProduct), so every
678
+ * The spend is a shared pot the server computes for (owner, team, machineProduct), so every
681
679
  * lease of the same product reports the same number — a machine created minutes
682
- * ago with no active seconds still reports the month's total. The cap is this
683
- * lease's own. Printed as "$1.23 this month of $500.00 cap" the only available
684
- * reading was that this machine had spent it.
680
+ * ago with no active seconds still reports the month's total. The cap is set on
681
+ * this lease, but admission compares it against that shared projected total.
682
+ * An old response without spendScope cannot establish who owns the total.
685
683
  */
686
684
  function dedicatedSpendSummary(lease) {
687
685
  const billing = lease.billing;
@@ -690,14 +688,22 @@ function dedicatedSpendSummary(lease) {
690
688
  const cap = billing.monthlySpendLimitCents !== undefined
691
689
  ? billing.monthlySpendLimitCents
692
690
  : billing.spendingLimitCents;
691
+ const scope = billing.spendScope?.kind === 'owner_product'
692
+ ? 'your machines of this type'
693
+ : billing.spendScope?.kind === 'workspace_product'
694
+ ? "this workspace's machines of this type"
695
+ : 'reported machines (scope unavailable)';
696
+ const capSummary = cap === null
697
+ ? 'no monthly cap'
698
+ : `${formatUsdCents(cap)} cap for this machine, checked against the projected monthly total`;
693
699
  if (billing.spendStatusUnavailable) {
694
- return `spend unavailable right now · ${cap === null ? 'no monthly cap' : `cap ${formatUsdCents(cap)}`}`;
700
+ return `spend unavailable right now for ${scope} · ${capSummary}`;
695
701
  }
696
702
  const spent = billing.currentMonthSpendCents;
697
703
  const projected = billing.projectedCurrentMonthSpendCents;
698
704
  const parts = [
699
- `${formatUsdCents(spent)} this month for machines of this type`,
700
- cap === null ? 'no monthly cap' : `${formatUsdCents(cap)} cap for this machine`,
705
+ `${formatUsdCents(spent)} this month for ${scope}`,
706
+ capSummary,
701
707
  ];
702
708
  if (typeof projected === 'number' && projected !== spent) {
703
709
  parts.push(`projected ${formatUsdCents(projected)}`);
@@ -815,6 +821,9 @@ function describeDedicatedLease(lease, harnesses = [], opts = {}) {
815
821
  ? ''
816
822
  : (0, harnesses_1.describeHealthReasonSentences)(health.reasons, dedicatedLeaseState(lease));
817
823
  row('health', health.assignable ? 'can take work' : `not assignable${why ? `: ${why}` : ''}`);
824
+ if (health.assignable) {
825
+ row('provider auth', `separate from runtime health; inspect saved sign-in: skrr machines dedicated sign-in-status ${dedicatedLeaseId(lease)} (credentials supplied for a run may differ)`);
826
+ }
818
827
  }
819
828
  row('spend', lease.billing ? dedicatedSpendSummary(lease) : undefined);
820
829
  const deletionPlan = dedicatedDeletionLines(lease);
@@ -2,23 +2,32 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.signalsApi = void 0;
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const workspace_header_1 = require("./workspace-header");
6
+ function inSignalWorkspace(operation) {
7
+ if (!(0, workspace_header_1.withWorkspaceHeader)({})['X-Workspace-Id']) {
8
+ throw new Error(process.env.OVERSKY_AUTONOMOUS_SESSION === '1'
9
+ ? 'Signal commands require a workspace-bound session. This session has no canonical workspace; --workspace and local selections cannot change its scope. No Signal request was sent.'
10
+ : 'Signal commands require a selected workspace. Pass --workspace <workspace-id>, or run `skrr workspaces list --json` then `skrr workspaces use <workspace-id>`. No Signal request was sent.');
11
+ }
12
+ return operation();
13
+ }
5
14
  /**
6
15
  * Secret-free Signal lifecycle (`/api/signal-monitors`, read-only sources and schemas).
7
16
  * Source creation, credentials and schema review hand out one-display secrets and stay
8
17
  * in the reviewed UI until a headless secret-display contract is approved.
9
18
  */
10
19
  exports.signalsApi = {
11
- listMonitors: () => data_provider_1.request.get('/api/signal-monitors'),
12
- showMonitor: (id) => data_provider_1.request.get(`/api/signal-monitors/${encodeURIComponent(id)}`),
13
- validateMonitor: (body) => data_provider_1.request.post('/api/signal-monitors/validate', body),
14
- createMonitor: (body) => data_provider_1.request.post('/api/signal-monitors', body),
15
- testMonitor: (id, body) => data_provider_1.request.post(`/api/signal-monitors/${encodeURIComponent(id)}/test`, body),
16
- reviseMonitor: (id, body) => data_provider_1.request.post(`/api/signal-monitors/${encodeURIComponent(id)}/revisions`, body),
17
- setMonitorStatus: (id, status) => data_provider_1.request.patch(`/api/signal-monitors/${encodeURIComponent(id)}`, { status }),
18
- monitorTransitions: (id) => data_provider_1.request.get(`/api/signal-monitors/${encodeURIComponent(id)}/transitions`),
19
- listSources: () => data_provider_1.request.get('/api/signal-sources'),
20
- showSource: (id) => data_provider_1.request.get(`/api/signal-sources/${encodeURIComponent(id)}`),
21
- sourceReceipts: (id) => data_provider_1.request.get(`/api/signal-sources/${encodeURIComponent(id)}/receipts`),
22
- listSchemas: () => data_provider_1.request.get('/api/signal-schemas'),
23
- showSchema: (id) => data_provider_1.request.get(`/api/signal-schemas/${encodeURIComponent(id)}`),
20
+ listMonitors: () => inSignalWorkspace(() => data_provider_1.request.get('/api/signal-monitors')),
21
+ showMonitor: (id) => inSignalWorkspace(() => data_provider_1.request.get(`/api/signal-monitors/${encodeURIComponent(id)}`)),
22
+ validateMonitor: (body) => inSignalWorkspace(() => data_provider_1.request.post('/api/signal-monitors/validate', body)),
23
+ createMonitor: (body) => inSignalWorkspace(() => data_provider_1.request.post('/api/signal-monitors', body)),
24
+ testMonitor: (id, body) => inSignalWorkspace(() => data_provider_1.request.post(`/api/signal-monitors/${encodeURIComponent(id)}/test`, body)),
25
+ reviseMonitor: (id, body) => inSignalWorkspace(() => data_provider_1.request.post(`/api/signal-monitors/${encodeURIComponent(id)}/revisions`, body)),
26
+ setMonitorStatus: (id, status) => inSignalWorkspace(() => data_provider_1.request.patch(`/api/signal-monitors/${encodeURIComponent(id)}`, { status })),
27
+ monitorTransitions: (id) => inSignalWorkspace(() => data_provider_1.request.get(`/api/signal-monitors/${encodeURIComponent(id)}/transitions`)),
28
+ listSources: () => inSignalWorkspace(() => data_provider_1.request.get('/api/signal-sources')),
29
+ showSource: (id) => inSignalWorkspace(() => data_provider_1.request.get(`/api/signal-sources/${encodeURIComponent(id)}`)),
30
+ sourceReceipts: (id) => inSignalWorkspace(() => data_provider_1.request.get(`/api/signal-sources/${encodeURIComponent(id)}/receipts`)),
31
+ listSchemas: () => inSignalWorkspace(() => data_provider_1.request.get('/api/signal-schemas')),
32
+ showSchema: (id) => inSignalWorkspace(() => data_provider_1.request.get(`/api/signal-schemas/${encodeURIComponent(id)}`)),
24
33
  };
@@ -10,7 +10,7 @@ const usd = (amount) => amount > 0 && amount < 0.01 ? '<$0.01' : `$${amount.toFi
10
10
  function formatSpaceSpend(summary) {
11
11
  const attribution = `Attribution since: ${summary.coverage.attributionSince || 'not certified'}; not attributed: ${summary.coverage.notAttributed.join(', ')}`;
12
12
  if (summary.runCount === 0)
13
- return `No runs in window\n${attribution}`;
13
+ return `No attributed runs in window\n${attribution}`;
14
14
  const cost = summary.costUsd === null ? 'Not fully attributed' : usd(summary.costUsd);
15
15
  const known = summary.knownCostUsd === null ? 'unavailable' : usd(summary.knownCostUsd);
16
16
  return `${cost}; known subtotal ${known}; ${summary.runCount} cost records; ${summary.byCostStatus.unavailable} unavailable, ${summary.byCostStatus.partial} partial\n${attribution}`;