@skrr-ai/cli 0.1.95 → 0.1.97

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 (200) 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/daemon/install.js +1 -1
  45. package/dist/commands/grants/create.d.ts +1 -0
  46. package/dist/commands/grants/create.js +8 -0
  47. package/dist/commands/grants/rotate.d.ts +5 -0
  48. package/dist/commands/grants/rotate.js +13 -1
  49. package/dist/commands/grants/update.d.ts +12 -0
  50. package/dist/commands/grants/update.js +27 -0
  51. package/dist/commands/machines/hosted/start.js +7 -1
  52. package/dist/commands/machines/list.js +4 -4
  53. package/dist/commands/measures/confirm.d.ts +1 -7
  54. package/dist/commands/measures/confirm.js +15 -14
  55. package/dist/commands/measures/list.d.ts +1 -7
  56. package/dist/commands/measures/list.js +1 -7
  57. package/dist/commands/measures/record.js +4 -4
  58. package/dist/commands/measures/show.d.ts +11 -0
  59. package/dist/commands/measures/show.js +26 -0
  60. package/dist/commands/northstars/governance/configure.d.ts +1 -0
  61. package/dist/commands/northstars/governance/configure.js +8 -1
  62. package/dist/commands/proposals/discard.d.ts +5 -0
  63. package/dist/commands/proposals/discard.js +8 -0
  64. package/dist/commands/proposals/keep.d.ts +5 -0
  65. package/dist/commands/proposals/keep.js +8 -0
  66. package/dist/commands/proposals/list.d.ts +15 -0
  67. package/dist/commands/proposals/list.js +74 -0
  68. package/dist/commands/proposals/show.d.ts +12 -0
  69. package/dist/commands/proposals/show.js +27 -0
  70. package/dist/commands/proposals/snooze.d.ts +5 -0
  71. package/dist/commands/proposals/snooze.js +8 -0
  72. package/dist/commands/signals/monitors/create.d.ts +9 -0
  73. package/dist/commands/signals/monitors/create.js +29 -0
  74. package/dist/commands/signals/monitors/list.d.ts +8 -0
  75. package/dist/commands/signals/monitors/list.js +16 -0
  76. package/dist/commands/signals/monitors/pause.d.ts +11 -0
  77. package/dist/commands/signals/monitors/pause.js +17 -0
  78. package/dist/commands/signals/monitors/resume.d.ts +11 -0
  79. package/dist/commands/signals/monitors/resume.js +17 -0
  80. package/dist/commands/signals/monitors/revise.d.ts +12 -0
  81. package/dist/commands/signals/monitors/revise.js +25 -0
  82. package/dist/commands/signals/monitors/show.d.ts +11 -0
  83. package/dist/commands/signals/monitors/show.js +17 -0
  84. package/dist/commands/signals/monitors/test.d.ts +12 -0
  85. package/dist/commands/signals/monitors/test.js +25 -0
  86. package/dist/commands/signals/monitors/transitions.d.ts +11 -0
  87. package/dist/commands/signals/monitors/transitions.js +17 -0
  88. package/dist/commands/signals/monitors/validate.d.ts +9 -0
  89. package/dist/commands/signals/monitors/validate.js +24 -0
  90. package/dist/commands/signals/schemas/list.d.ts +8 -0
  91. package/dist/commands/signals/schemas/list.js +16 -0
  92. package/dist/commands/signals/schemas/show.d.ts +11 -0
  93. package/dist/commands/signals/schemas/show.js +17 -0
  94. package/dist/commands/signals/sources/list.d.ts +8 -0
  95. package/dist/commands/signals/sources/list.js +16 -0
  96. package/dist/commands/signals/sources/receipts.d.ts +11 -0
  97. package/dist/commands/signals/sources/receipts.js +17 -0
  98. package/dist/commands/signals/sources/show.d.ts +11 -0
  99. package/dist/commands/signals/sources/show.js +17 -0
  100. package/dist/commands/spaces/usage.d.ts +12 -0
  101. package/dist/commands/spaces/usage.js +28 -0
  102. package/dist/commands/spaces/workflows/run.js +3 -0
  103. package/dist/commands/spaces/workflows/runs.js +2 -1
  104. package/dist/commands/spaces/workflows/update.d.ts +6 -1
  105. package/dist/commands/spaces/workflows/update.js +34 -2
  106. package/dist/commands/tasks/cascade-status.d.ts +2 -0
  107. package/dist/commands/tasks/cascade-status.js +27 -1
  108. package/dist/commands/tasks/comments/add.js +29 -20
  109. package/dist/commands/tasks/complete.js +7 -4
  110. package/dist/commands/tasks/create.d.ts +4 -0
  111. package/dist/commands/tasks/create.js +16 -0
  112. package/dist/commands/tasks/deliverable/add.js +17 -7
  113. package/dist/commands/tasks/economics-compare.d.ts +15 -0
  114. package/dist/commands/tasks/economics-compare.js +60 -0
  115. package/dist/commands/tasks/events/append.js +6 -5
  116. package/dist/commands/tasks/expectations/assess.js +4 -4
  117. package/dist/commands/tasks/expectations/evaluate.d.ts +13 -0
  118. package/dist/commands/tasks/expectations/evaluate.js +43 -0
  119. package/dist/commands/tasks/list.d.ts +1 -0
  120. package/dist/commands/tasks/list.js +44 -1
  121. package/dist/commands/tasks/move.js +15 -11
  122. package/dist/commands/tasks/report.js +19 -11
  123. package/dist/commands/tasks/result/submit.js +5 -4
  124. package/dist/commands/tasks/resume/save.js +5 -4
  125. package/dist/commands/tasks/review.js +4 -4
  126. package/dist/commands/tasks/show.d.ts +5 -0
  127. package/dist/commands/tasks/show.js +17 -0
  128. package/dist/commands/tasks/update.d.ts +4 -0
  129. package/dist/commands/tasks/update.js +9 -0
  130. package/dist/commands/tasks/updates/add.js +5 -4
  131. package/dist/commands/tasks/workflow-findings.js +2 -2
  132. package/dist/commands/triggers/create.d.ts +2 -0
  133. package/dist/commands/triggers/create.js +15 -0
  134. package/dist/commands/workspaces/grants.d.ts +12 -0
  135. package/dist/commands/workspaces/grants.js +31 -0
  136. package/dist/commands/workspaces/stewardship.d.ts +13 -0
  137. package/dist/commands/workspaces/stewardship.js +34 -0
  138. package/dist/lib/agent-config.d.ts +0 -12
  139. package/dist/lib/agent-config.js +1 -27
  140. package/dist/lib/agent-mailbox.d.ts +35 -0
  141. package/dist/lib/agent-mailbox.js +55 -0
  142. package/dist/lib/commitment-endpoints.d.ts +2 -1
  143. package/dist/lib/commitment-endpoints.js +2 -5
  144. package/dist/lib/commitment-product.d.ts +3 -1
  145. package/dist/lib/commitment-product.js +10 -0
  146. package/dist/lib/commitment-terms.js +1 -1
  147. package/dist/lib/commitments.d.ts +17 -7
  148. package/dist/lib/commitments.js +173 -85
  149. package/dist/lib/compasses.d.ts +14 -0
  150. package/dist/lib/compasses.js +25 -11
  151. package/dist/lib/grants.d.ts +5 -1
  152. package/dist/lib/grants.js +9 -1
  153. package/dist/lib/hosted-machines.d.ts +7 -0
  154. package/dist/lib/mailbox-human-command.d.ts +11 -0
  155. package/dist/lib/mailbox-human-command.js +41 -0
  156. package/dist/lib/measures.d.ts +28 -2
  157. package/dist/lib/measures.js +38 -14
  158. package/dist/lib/message-intent.d.ts +6 -2
  159. package/dist/lib/message-intent.js +1 -0
  160. package/dist/lib/northstars.js +2 -1
  161. package/dist/lib/proposal-decision-command.d.ts +19 -0
  162. package/dist/lib/proposal-decision-command.js +64 -0
  163. package/dist/lib/proposals.d.ts +8 -0
  164. package/dist/lib/proposals.js +22 -0
  165. package/dist/lib/roles.d.ts +141 -6
  166. package/dist/lib/roles.js +8 -2
  167. package/dist/lib/session-task-write.d.ts +18 -0
  168. package/dist/lib/session-task-write.js +43 -0
  169. package/dist/lib/signals.d.ts +20 -0
  170. package/dist/lib/signals.js +24 -0
  171. package/dist/lib/space-usage.d.ts +3 -0
  172. package/dist/lib/space-usage.js +17 -0
  173. package/dist/lib/stewardship-output.d.ts +4 -0
  174. package/dist/lib/stewardship-output.js +82 -0
  175. package/dist/lib/task-asset-upload.d.ts +3 -1
  176. package/dist/lib/task-asset-upload.js +19 -22
  177. package/dist/lib/task-economics.d.ts +16 -0
  178. package/dist/lib/task-economics.js +22 -0
  179. package/dist/lib/triggers.d.ts +11 -3
  180. package/dist/lib/triggers.js +58 -1
  181. package/dist/lib/unsupported-managed-task-write.d.ts +2 -0
  182. package/dist/lib/unsupported-managed-task-write.js +12 -0
  183. package/dist/lib/usage-discovery.d.ts +10 -0
  184. package/dist/lib/usage-discovery.js +24 -3
  185. package/dist/lib/workflow-run-budget.d.ts +12 -0
  186. package/dist/lib/workflow-run-budget.js +37 -0
  187. package/dist/lib/workspaces.d.ts +48 -0
  188. package/dist/lib/workspaces.js +8 -0
  189. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.d.ts +4 -0
  190. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/cliHandoffWire.js +14 -1
  191. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.d.ts +9 -0
  192. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/githubPushRefusal.js +49 -0
  193. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.d.ts +4 -0
  194. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/cliHandoffWire.js +12 -0
  195. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.d.ts +9 -0
  196. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/githubPushRefusal.js +44 -0
  197. package/dist/node_modules/@skrr-ai/auth-core/package.json +10 -0
  198. package/dist/node_modules/@skrr-ai/data-provider/index.js +8017 -7086
  199. package/oclif.manifest.json +3872 -1167
  200. package/package.json +22 -1
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.sessionTaskWrite = sessionTaskWrite;
4
+ const api_fetch_1 = require("./api-fetch");
5
+ const credential_resolver_1 = require("./credential-resolver");
6
+ const session_task_endpoints_1 = require("./session-task-endpoints");
7
+ /**
8
+ * A canonical Task write must retain its authenticated session actor even if
9
+ * the daemon also grants the CLI a wide credential. The wide credential names
10
+ * its account, not the Agent that is executing this Task. Passing the trusted
11
+ * session credential explicitly makes apiFetch keep the server's bound actor.
12
+ * Human invocations continue through their ordinary Task endpoints.
13
+ */
14
+ async function sessionTaskWrite(operation, requestedTask, env = process.env) {
15
+ const credential = (0, credential_resolver_1.resolveTrustedAutonomousCredential)(env);
16
+ if (!credential)
17
+ return null;
18
+ const sessionId = env.OVERSKY_SESSION_ID?.trim();
19
+ if (!credential.token || !sessionId) {
20
+ throw Object.assign(new Error('Task writes require this runtime’s bound session credential.'), {
21
+ code: 'SESSION_TASK_CREDENTIAL_MISSING',
22
+ });
23
+ }
24
+ const prefix = `/api/agentic/sessions/${encodeURIComponent(sessionId)}`;
25
+ // A scoped endpoint derives its Task and ignores a caller's Task id. Check
26
+ // the requested coordinate first, so naming a sibling cannot quietly finish
27
+ // the bound Task instead. The server independently rechecks the live binding
28
+ // and checkout when committing the write.
29
+ const bound = await (0, api_fetch_1.apiFetch)(`${prefix}/task`, {
30
+ credential,
31
+ });
32
+ if (!bound.id || !(0, session_task_endpoints_1.requestMatchesBoundTask)(bound, requestedTask)) {
33
+ throw Object.assign(new Error(`This session is bound to task ${bound.identifier || bound.id || '?'} and cannot write the requested Task.`), {
34
+ code: 'SESSION_SCOPE_TASK_MISMATCH',
35
+ });
36
+ }
37
+ return {
38
+ path: `${prefix}/${session_task_endpoints_1.SESSION_TASK_PATH_SUFFIX[operation]}`,
39
+ credential,
40
+ taskId: bound.id,
41
+ task: bound,
42
+ };
43
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Secret-free Signal lifecycle (`/api/signal-monitors`, read-only sources and schemas).
3
+ * Source creation, credentials and schema review hand out one-display secrets and stay
4
+ * in the reviewed UI until a headless secret-display contract is approved.
5
+ */
6
+ export declare const signalsApi: {
7
+ listMonitors: () => Promise<unknown>;
8
+ showMonitor: (id: string) => Promise<unknown>;
9
+ validateMonitor: (body: unknown) => Promise<any>;
10
+ createMonitor: (body: unknown) => Promise<any>;
11
+ testMonitor: (id: string, body: unknown) => Promise<any>;
12
+ reviseMonitor: (id: string, body: unknown) => Promise<any>;
13
+ setMonitorStatus: (id: string, status: "active" | "paused") => Promise<any>;
14
+ monitorTransitions: (id: string) => Promise<unknown>;
15
+ listSources: () => Promise<unknown>;
16
+ showSource: (id: string) => Promise<unknown>;
17
+ sourceReceipts: (id: string) => Promise<unknown>;
18
+ listSchemas: () => Promise<unknown>;
19
+ showSchema: (id: string) => Promise<unknown>;
20
+ };
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signalsApi = void 0;
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ /**
6
+ * Secret-free Signal lifecycle (`/api/signal-monitors`, read-only sources and schemas).
7
+ * Source creation, credentials and schema review hand out one-display secrets and stay
8
+ * in the reviewed UI until a headless secret-display contract is approved.
9
+ */
10
+ 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)}`),
24
+ };
@@ -0,0 +1,3 @@
1
+ import { type SpendSummary } from '@skrr-ai/data-provider';
2
+ export declare const getSpaceUsage: (spaceId: string, window?: string) => Promise<SpendSummary>;
3
+ export declare function formatSpaceSpend(summary: SpendSummary): string;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getSpaceUsage = void 0;
4
+ exports.formatSpaceSpend = formatSpaceSpend;
5
+ const data_provider_1 = require("@skrr-ai/data-provider");
6
+ const getSpaceUsage = (spaceId, window = '30d') => data_provider_1.dataService.getSpaceUsage(spaceId, window);
7
+ exports.getSpaceUsage = getSpaceUsage;
8
+ /** A positive amount under one cent must not print as $0.00; a measured zero stays $0.00. */
9
+ const usd = (amount) => amount > 0 && amount < 0.01 ? '<$0.01' : `$${amount.toFixed(2)}`;
10
+ function formatSpaceSpend(summary) {
11
+ const attribution = `Attribution since: ${summary.coverage.attributionSince || 'not certified'}; not attributed: ${summary.coverage.notAttributed.join(', ')}`;
12
+ if (summary.runCount === 0)
13
+ return `No runs in window\n${attribution}`;
14
+ const cost = summary.costUsd === null ? 'Not fully attributed' : usd(summary.costUsd);
15
+ const known = summary.knownCostUsd === null ? 'unavailable' : usd(summary.knownCostUsd);
16
+ return `${cost}; known subtotal ${known}; ${summary.runCount} cost records; ${summary.byCostStatus.unavailable} unavailable, ${summary.byCostStatus.partial} partial\n${attribution}`;
17
+ }
@@ -0,0 +1,4 @@
1
+ import type { WorkspaceGrantReport, WorkspaceStewardshipReport } from './workspaces';
2
+ export declare function printStewardshipCoverage(report: WorkspaceGrantReport, log: (line: string) => void): void;
3
+ export declare function printWorkspaceGrants(report: WorkspaceGrantReport, log: (line: string) => void): void;
4
+ export declare function printWorkspaceStewardship(report: WorkspaceStewardshipReport, log: (line: string) => void): void;
@@ -0,0 +1,82 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printStewardshipCoverage = printStewardshipCoverage;
4
+ exports.printWorkspaceGrants = printWorkspaceGrants;
5
+ exports.printWorkspaceStewardship = printWorkspaceStewardship;
6
+ const format_1 = require("./format");
7
+ function printStewardshipCoverage(report, log) {
8
+ if (report.coverage.complete) {
9
+ log('Coverage: complete within the bounded sources.');
10
+ return;
11
+ }
12
+ log('Coverage: incomplete. Unreadable sources do not confirm absence.');
13
+ for (const source of report.coverage.sources) {
14
+ if (source.status === 'unavailable')
15
+ log(`${source.name}: unavailable`);
16
+ else if (source.truncated)
17
+ log(`${source.name}: truncated at 200 rows`);
18
+ }
19
+ }
20
+ function printWorkspaceGrants(report, log) {
21
+ printStewardshipCoverage(report, log);
22
+ printGrantRows(report, log);
23
+ }
24
+ function printGrantRows(report, log) {
25
+ if (report.grants === null) {
26
+ log('Grants: unavailable.');
27
+ return;
28
+ }
29
+ if (!report.grants.length) {
30
+ log('No workspace Grants observed.');
31
+ return;
32
+ }
33
+ (0, format_1.renderTable)(report.grants.map((row) => ({
34
+ id: row.id,
35
+ label: row.label,
36
+ owner: row.ownerUserId,
37
+ active: row.active ? 'yes' : 'no',
38
+ expires: row.expiresAt ?? '',
39
+ envKeys: row.credential?.envKeys.join(', ') ?? '',
40
+ referencedBy: row.referencedBy === null
41
+ ? 'unavailable'
42
+ : `${row.referencedBy.join(', ')}${row.referencesTruncated ? ' (truncated)' : ''}`,
43
+ })), [
44
+ { key: 'id', header: 'ID' },
45
+ { key: 'label', header: 'LABEL' },
46
+ { key: 'owner', header: 'OWNER' },
47
+ { key: 'active', header: 'ACTIVE' },
48
+ { key: 'expires', header: 'EXPIRES' },
49
+ { key: 'envKeys', header: 'ENV NAMES' },
50
+ { key: 'referencedBy', header: 'REFERENCED BY' },
51
+ ], log);
52
+ }
53
+ function printWorkspaceStewardship(report, log) {
54
+ printStewardshipCoverage(report, log);
55
+ log(`Membership: ${report.membership.status}${report.membership.role ? ` (${report.membership.role})` : ''}`);
56
+ for (const [name, rows] of [
57
+ ['Commitments', report.commitments],
58
+ ['Approver seats', report.approverSeats],
59
+ ]) {
60
+ log(`${name}: ${rows === null ? 'unavailable' : rows.length}`);
61
+ if (rows?.length)
62
+ (0, format_1.renderTable)(rows.map((row) => ({
63
+ id: row.id,
64
+ title: row.title ?? '',
65
+ status: row.status ?? '',
66
+ agentId: row.agentId ?? '',
67
+ })), [
68
+ { key: 'id', header: 'ID' },
69
+ { key: 'title', header: 'TITLE' },
70
+ { key: 'status', header: 'STATUS' },
71
+ { key: 'agentId', header: 'EXECUTOR' },
72
+ ], log);
73
+ }
74
+ log(`Authored Agents: ${report.agents === null ? 'unavailable' : report.agents.length}`);
75
+ if (report.agents?.length)
76
+ (0, format_1.renderTable)(report.agents.map((row) => ({ id: row.id, name: row.name ?? '' })), [
77
+ { key: 'id', header: 'ID' },
78
+ { key: 'name', header: 'NAME' },
79
+ ], log);
80
+ log('Grants:');
81
+ printGrantRows(report, log);
82
+ }
@@ -1,4 +1,4 @@
1
- import type { WorkResourceKind } from '@skrr-ai/data-provider';
1
+ import type { WorkResourceKind, TaskResourceWriteReceipt } from '@skrr-ai/data-provider';
2
2
  export type TaskAssetRole = 'input' | 'evidence' | 'deliverable';
3
3
  /**
4
4
  * What the bytes ARE in the work ledger, for an upload that is the archived
@@ -24,6 +24,8 @@ export interface TaskAssetLedgerResource {
24
24
  expectationIds?: string[];
25
25
  }
26
26
  export interface UploadedTaskAsset {
27
+ /** The canonical Task relationship and exact Resource version recorded by this upload. */
28
+ resourceReceipt?: TaskResourceWriteReceipt;
27
29
  id: string;
28
30
  /**
29
31
  * sha256 (hex) of the bytes the server STORED, as the server recorded them.
@@ -47,9 +47,8 @@ exports.postFileToStorage = postFileToStorage;
47
47
  * ≤25MB or image → buffered multipart (canonicalization needs the bytes)
48
48
  * larger non-image → intent → presigned POST to S3 → complete (OSK-9867)
49
49
  *
50
- * Session routing follows the CREDENTIAL, resolved here once per file so a
51
- * narrowed agent session lands on `task-attachments*` and a human (or a
52
- * delegated session holding a wide credential) lands on `/api/tasks/:id/*`.
50
+ * Agent uploads retain the authenticated session producer even beside a wide
51
+ * delegated credential. Human uploads use `/api/tasks/:id/*`.
53
52
  *
54
53
  * Retry safety is server-side: the sha256 IS the asset identity, so a second
55
54
  * upload of the same bytes returns the existing ready row instead of
@@ -73,7 +72,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
73
72
  const base_command_1 = require("../base-command");
74
73
  const file_mime_1 = require("./file-mime");
75
74
  const api_fetch_1 = require("./api-fetch");
76
- const session_task_endpoints_1 = require("./session-task-endpoints");
75
+ const session_task_write_1 = require("./session-task-write");
77
76
  function mb(bytes) {
78
77
  return `${Math.floor(bytes / (1024 * 1024))} MB`;
79
78
  }
@@ -89,14 +88,9 @@ function storedDigest(asset) {
89
88
  * refuses to act on any other task before a byte leaves the machine.
90
89
  */
91
90
  async function readTaskForUpload(taskId) {
92
- const readPath = await (0, session_task_endpoints_1.sessionScopedTaskPath)('read');
93
- if (readPath) {
94
- const bound = await (0, api_fetch_1.apiFetch)(readPath);
95
- if (!(0, session_task_endpoints_1.requestMatchesBoundTask)(bound, taskId)) {
96
- throw new Error(`This session is bound to task ${String(bound.identifier ?? bound.id ?? '?')} — a scoped credential cannot attach files to ${taskId}.`);
97
- }
98
- return bound;
99
- }
91
+ const target = await (0, session_task_write_1.sessionTaskWrite)('attachment', taskId);
92
+ if (target)
93
+ return target.task;
100
94
  return (await data_provider_1.dataService.getTask(taskId, { enrich: false }));
101
95
  }
102
96
  /**
@@ -124,14 +118,13 @@ async function uploadTaskAssetFile(opts) {
124
118
  throw new Error(`${filename} exceeds the direct-upload limit (${mb(data_provider_1.TASK_DIRECT_UPLOAD_MAX_BYTES)})`);
125
119
  }
126
120
  const direct = !isImage && size > data_provider_1.TASK_ATTACHMENT_MAX_BYTES;
127
- const sessionPath = await (0, session_task_endpoints_1.sessionScopedTaskPath)('attachment');
121
+ const sessionTarget = await (0, session_task_write_1.sessionTaskWrite)('attachment', taskId);
128
122
  let result;
129
123
  try {
130
- if (sessionPath) {
131
- await readTaskForUpload(taskId);
124
+ if (sessionTarget) {
132
125
  result = direct
133
- ? await sessionDirectUpload({ filePath, size, filename, mimeType, role, ledger })
134
- : await sessionBufferedUpload({ filePath, filename, mimeType, role, ledger });
126
+ ? await sessionDirectUpload({ filePath, size, filename, mimeType, role, ledger }, sessionTarget)
127
+ : await sessionBufferedUpload({ filePath, filename, mimeType, role, ledger }, sessionTarget);
135
128
  }
136
129
  else {
137
130
  result = direct
@@ -148,6 +141,7 @@ async function uploadTaskAssetFile(opts) {
148
141
  filename,
149
142
  mimeType,
150
143
  deduplicated: result.deduplicated === true,
144
+ ...(result.resourceReceipt ? { resourceReceipt: result.resourceReceipt } : {}),
151
145
  };
152
146
  }
153
147
  /**
@@ -228,9 +222,10 @@ async function genericBufferedUpload(p) {
228
222
  formData: fileFormData(p.filePath, p.filename, p.mimeType, p.role, p.ledger),
229
223
  });
230
224
  }
231
- async function sessionBufferedUpload(p) {
232
- return (0, api_fetch_1.apiFetch)((await (0, session_task_endpoints_1.sessionScopedTaskPath)('attachment')), {
225
+ async function sessionBufferedUpload(p, target) {
226
+ return (0, api_fetch_1.apiFetch)(target.path, {
233
227
  method: 'POST',
228
+ credential: target.credential,
234
229
  body: fileFormData(p.filePath, p.filename, p.mimeType, p.role, p.ledger),
235
230
  });
236
231
  }
@@ -266,9 +261,10 @@ async function genericDirectUpload(p) {
266
261
  attachmentId: intent.attachment.id,
267
262
  });
268
263
  }
269
- async function sessionDirectUpload(p) {
270
- const intent = await (0, api_fetch_1.apiFetch)((await (0, session_task_endpoints_1.sessionScopedTaskPath)('attachmentIntent')), {
264
+ async function sessionDirectUpload(p, target) {
265
+ const intent = await (0, api_fetch_1.apiFetch)(`${target.path}/intent`, {
271
266
  method: 'POST',
267
+ credential: target.credential,
272
268
  body: {
273
269
  filename: p.filename,
274
270
  mimeType: p.mimeType,
@@ -295,8 +291,9 @@ async function sessionDirectUpload(p) {
295
291
  filename: p.filename,
296
292
  mimeType: p.mimeType,
297
293
  });
298
- return (0, api_fetch_1.apiFetch)((await (0, session_task_endpoints_1.sessionScopedTaskPath)('attachmentComplete')), {
294
+ return (0, api_fetch_1.apiFetch)(`${target.path}/complete`, {
299
295
  method: 'POST',
296
+ credential: target.credential,
300
297
  body: { attachmentId: intent.attachment.id },
301
298
  });
302
299
  }
@@ -0,0 +1,16 @@
1
+ export declare function economicsFromFlags(flags: {
2
+ savings?: string;
3
+ per?: string;
4
+ effort?: string;
5
+ risk?: string;
6
+ }, existing?: unknown): {
7
+ savings?: {
8
+ amount: number;
9
+ currency: "USD";
10
+ per: "month" | "year" | "once";
11
+ } | undefined;
12
+ effortHours?: number | undefined;
13
+ risk?: "low" | "medium" | "high" | undefined;
14
+ confidence?: "low" | "medium" | "high" | undefined;
15
+ rationale?: string | undefined;
16
+ } | undefined;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.economicsFromFlags = economicsFromFlags;
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ function economicsFromFlags(flags, existing) {
6
+ if ([flags.savings, flags.per, flags.effort, flags.risk].every((value) => value === undefined))
7
+ return undefined;
8
+ if ((flags.savings === undefined) !== (flags.per === undefined))
9
+ throw new Error('--savings and --per must be supplied together (USD/month, year or once).');
10
+ for (const value of [flags.savings, flags.effort])
11
+ if (value !== undefined && value.trim() === '')
12
+ throw new Error('Economic amounts must be explicit nonnegative numbers.');
13
+ const content = existing && typeof existing === 'object' ? existing : {};
14
+ return data_provider_1.ProposalEconomicsInputSchema.parse({
15
+ ...content,
16
+ ...(flags.savings !== undefined
17
+ ? { savings: { amount: Number(flags.savings), currency: 'USD', per: flags.per } }
18
+ : {}),
19
+ ...(flags.effort !== undefined ? { effortHours: Number(flags.effort) } : {}),
20
+ ...(flags.risk !== undefined ? { risk: flags.risk } : {}),
21
+ });
22
+ }
@@ -1,4 +1,4 @@
1
- import { type EmbeddedActionRef, type TriggerPreflightReport } from '@skrr-ai/data-provider';
1
+ import { type EmbeddedActionRef, type TriggerPreflightReport, type TriggerSensorConfig } from '@skrr-ai/data-provider';
2
2
  export declare const TRIGGER_KINDS: readonly ["schedule", "once", "event", "stochastic", "self_scheduled", "on_demand", "form", "chat_message"];
3
3
  export declare const TRIGGER_SCOPE_KINDS: readonly ["agent", "group_chat_subscriber", "space_oracle", "space_subscriber", "key_result_watcher", "task_watcher", "goal_watcher", "entity_reminder"];
4
4
  /**
@@ -18,7 +18,7 @@ export declare const TRIGGER_SCOPE_KINDS: readonly ["agent", "group_chat_subscri
18
18
  * capability was live and unreachable. `watch_until` and `sensor_poll` had been
19
19
  * in that state for longer.
20
20
  */
21
- export declare const TRIGGER_EXECUTOR_MODES: readonly ["send_message", "run_only", "enqueue_group_chat_agent_turn", "enqueue_assistant_turn", "enqueue_space_assistant_turn", "proactive_orchestrator_check", "auto_dream_orchestrator", "task_schedule_fire", "goal_planner_tick", "morning_briefing", "builtin_action", "task_execute", "watch_until", "sensor_poll", "external_work_task", "notify_user", "space_cycle_rollover"];
21
+ export declare const TRIGGER_EXECUTOR_MODES: readonly ["send_message", "run_only", "enqueue_group_chat_agent_turn", "enqueue_assistant_turn", "enqueue_space_assistant_turn", "proactive_orchestrator_check", "auto_dream_orchestrator", "task_schedule_fire", "goal_planner_tick", "morning_briefing", "builtin_action", "task_execute", "watch_until", "sensor_poll", "external_work_task", "notify_user", "space_cycle_rollover", "workflow_signal"];
22
22
  export declare const TRIGGER_EVENT_FILTER_MODES: readonly ["equals", "contains", "in_set"];
23
23
  export declare const TRIGGER_CONCURRENCY_POLICIES: readonly ["coalesce_if_active", "allow_parallel"];
24
24
  export declare const TRIGGER_FORM_RESPONSE_MODES: readonly ["thank_you_page", "redirect_url", "run_complete"];
@@ -85,7 +85,9 @@ export interface TriggerDefinition {
85
85
  kind?: string;
86
86
  refs?: Record<string, unknown>;
87
87
  };
88
- execution?: Record<string, unknown>;
88
+ execution?: Record<string, unknown> & {
89
+ sensor?: TriggerSensorConfig;
90
+ };
89
91
  schedule?: Record<string, unknown>;
90
92
  once?: Record<string, unknown>;
91
93
  event?: Record<string, unknown>;
@@ -341,6 +343,12 @@ export declare function nextRunLabel(trigger: TriggerDefinition): string;
341
343
  * (OSK-7711).
342
344
  */
343
345
  export declare function armedLabel(trigger: TriggerDefinition): string;
346
+ /**
347
+ * A keyed sensor that is still ENABLED but degraded (an item has failed for
348
+ * dozens of polls) reads `yes` in ARMED, which is true and not the whole story.
349
+ * Marked here so the list says it, not only `triggers show`.
350
+ */
351
+ export declare function degradedMarker(trigger: TriggerDefinition): string;
344
352
  export declare function renderTriggerList(triggers: TriggerDefinition[], log: (line: string) => void): void;
345
353
  /**
346
354
  * The `duplicate_subscription` warning a create response may carry, as the
@@ -29,6 +29,7 @@ exports.fetchAgentScopedTrigger = fetchAgentScopedTrigger;
29
29
  exports.scopeLabel = scopeLabel;
30
30
  exports.nextRunLabel = nextRunLabel;
31
31
  exports.armedLabel = armedLabel;
32
+ exports.degradedMarker = degradedMarker;
32
33
  exports.renderTriggerList = renderTriggerList;
33
34
  exports.formatDuplicateSubscriptionWarning = formatDuplicateSubscriptionWarning;
34
35
  exports.armedStateLabel = armedStateLabel;
@@ -99,6 +100,9 @@ exports.TRIGGER_EXECUTOR_MODES = [
99
100
  'external_work_task',
100
101
  'notify_user',
101
102
  'space_cycle_rollover',
103
+ // Resolve a workflow step an agent parked with `workflow_await`, when this
104
+ // trigger's event arrives. Needs `--workflow-event-key`.
105
+ 'workflow_signal',
102
106
  ];
103
107
  exports.TRIGGER_EVENT_FILTER_MODES = ['equals', 'contains', 'in_set'];
104
108
  exports.TRIGGER_CONCURRENCY_POLICIES = ['coalesce_if_active', 'allow_parallel'];
@@ -748,6 +752,15 @@ function armedLabel(trigger) {
748
752
  }
749
753
  return 'yes';
750
754
  }
755
+ /**
756
+ * A keyed sensor that is still ENABLED but degraded (an item has failed for
757
+ * dozens of polls) reads `yes` in ARMED, which is true and not the whole story.
758
+ * Marked here so the list says it, not only `triggers show`.
759
+ */
760
+ function degradedMarker(trigger) {
761
+ const reason = trigger.execution?.sensor?.state?.degradedReason;
762
+ return reason ? ' (degraded)' : '';
763
+ }
751
764
  function renderTriggerList(triggers, log) {
752
765
  if (triggers.length === 0) {
753
766
  log('No triggers.');
@@ -756,7 +769,7 @@ function renderTriggerList(triggers, log) {
756
769
  (0, format_1.renderTable)(triggers.map((trigger) => ({
757
770
  id: trigger.id ?? '-',
758
771
  kind: trigger.kind ?? '-',
759
- enabled: armedLabel(trigger),
772
+ enabled: `${armedLabel(trigger)}${degradedMarker(trigger)}`,
760
773
  scope: scopeLabel(trigger),
761
774
  next: nextRunLabel(trigger),
762
775
  label: trigger.label ?? '',
@@ -768,6 +781,12 @@ function renderTriggerList(triggers, log) {
768
781
  { key: 'next', header: 'NEXT/LAST', maxWidth: 24 },
769
782
  { key: 'label', header: 'LABEL', maxWidth: 32 },
770
783
  ], log);
784
+ const degraded = triggers.filter((t) => degradedMarker(t));
785
+ if (degraded.length > 0) {
786
+ log('');
787
+ log(`${degraded.length} trigger(s) are enabled but degraded — ` +
788
+ 'see `Degraded:` in `triggers show <id>`.');
789
+ }
771
790
  const unsynced = triggers.filter((t) => armedLabel(t).startsWith('!'));
772
791
  if (unsynced.length > 0) {
773
792
  log('');
@@ -872,6 +891,44 @@ function renderTriggerSummary(trigger, log, options = {}) {
872
891
  log(`Label: ${trigger.label}`);
873
892
  if (trigger.execution?.mode)
874
893
  log(`Mode: ${String(trigger.execution.mode)}`);
894
+ const sensor = trigger.execution?.mode === 'sensor_poll' ? trigger.execution.sensor : undefined;
895
+ if (sensor?.measureSink) {
896
+ log(`Measure: ${sensor.measureSink.measureId} — platform fetched, unconfirmed`);
897
+ if (sensor.state?.lastSinkError)
898
+ log(`Sink: ${sensor.state.lastSinkError}`);
899
+ else if (sensor.state?.lastSinkAt)
900
+ log(`Sink: last reading ${sensor.state.lastSinkValue ?? '-'} at ${sensor.state.lastSinkAt}`);
901
+ else
902
+ log('Sink: awaiting a numeric reading');
903
+ }
904
+ // A keyed sensor can be quietly blind (items held back, unreadable, failing).
905
+ // Each count is printed, and zero is printed too: "0" and "absent" must read
906
+ // differently from a sensor that has never been counted.
907
+ if (sensor?.cursor?.mode === 'keyed' && sensor.state) {
908
+ const st = sensor.state;
909
+ const n = (v) => v ?? 0;
910
+ log(`Keyed: deferred ${n(st.deferredCount)}, unreadable ${n(st.unreadableCount)}, failed ${n(st.failedItemCount)}, quarantined ${n(st.quarantinedCount)}, duplicates ${n(st.duplicateCount)}, unsubjected ${n(st.unsubjectedCount)} (cumulative)`);
911
+ }
912
+ if (sensor?.cursor?.mode === 'keyed' && sensor.state) {
913
+ const st = sensor.state;
914
+ // A reseed publishes nothing by design, so it is the one change nothing
915
+ // else records: say when it happened, why, and how often.
916
+ if (st.reseededAt) {
917
+ const why = st.reseededReason
918
+ ? (data_provider_1.KEYED_RESEED_COPY[st.reseededReason] ?? st.reseededReason)
919
+ : 'reason not recorded';
920
+ log(`Reseeded: ${st.reseededAt} (${st.reseededReason ?? 'unknown'}: ${why}); ${st.reseedCount ?? 1} time(s); nothing was published for it`);
921
+ }
922
+ if (st.itemFailurePolls)
923
+ log(`Items: failing for ${st.itemFailurePolls} poll(s) in a row`);
924
+ }
925
+ // The sensor is degraded, not just carrying a line of text: say it first.
926
+ if (sensor?.state?.degradedReason)
927
+ log(`Degraded: ${sensor.state.degradedReason}`);
928
+ if (sensor?.state?.consecutiveErrors)
929
+ log(`Errors: ${sensor.state.consecutiveErrors} feed failure(s) in a row (the sensor disables itself if the feed keeps failing)`);
930
+ if (sensor?.state?.lastError)
931
+ log(`Problem: ${sensor.state.lastError}`);
875
932
  // An `external_work_task` writes into someone else's system, and which
876
933
  // contract governs that write — and whether it posts by itself — was readable
877
934
  // only by pulling `execution.effectPolicy` out of raw JSON. A trigger that can
@@ -0,0 +1,2 @@
1
+ /** Refuse before a composite command can partially mutate a Task as Human. */
2
+ export declare function refuseUnsupportedManagedTaskWrite(operation: string, supportedTool?: string, env?: Record<string, string | undefined>): void;
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.refuseUnsupportedManagedTaskWrite = refuseUnsupportedManagedTaskWrite;
4
+ const credential_resolver_1 = require("./credential-resolver");
5
+ /** Refuse before a composite command can partially mutate a Task as Human. */
6
+ function refuseUnsupportedManagedTaskWrite(operation, supportedTool, env = process.env) {
7
+ if (!(0, credential_resolver_1.resolveTrustedAutonomousCredential)(env))
8
+ return;
9
+ throw Object.assign(new Error(`${operation} have no bound-session CLI endpoint. ` +
10
+ (supportedTool ? `Use ${supportedTool} through this Agent’s authenticated tool runtime, or ask a person to make the change. ` : 'Ask a person to make this change. ') +
11
+ 'No Task was changed and no files were uploaded.'), { code: 'SESSION_TASK_WRITE_UNSUPPORTED' });
12
+ }
@@ -23,6 +23,14 @@ import type { CommandManifest } from './command-manifest';
23
23
  * only then whether the bound is still the right size.
24
24
  */
25
25
  export declare const USAGE_OVERVIEW_TOKEN_BUDGET = 240;
26
+ /**
27
+ * Topics listed under another domain instead of as their own overview line.
28
+ * Every command keeps its own id (`skrr proposals list`); only the discovery
29
+ * grouping changes, and `skrr usage <folded>` resolves to the host page, which
30
+ * names the folded topic in an `Includes:` line so it stays reachable.
31
+ * `proposals` is the unified proposal inbox, so it reads as part of `inbox`.
32
+ */
33
+ export declare const USAGE_DOMAIN_FOLDS: Readonly<Record<string, string>>;
26
34
  export declare const USAGE_DOMAIN_TOKEN_BUDGET = 500;
27
35
  export type UsageCommand = {
28
36
  command: string;
@@ -46,6 +54,8 @@ export type UsageReference = {
46
54
  object: 'sky_usage_reference';
47
55
  generatedFrom: 'oclif.manifest.json';
48
56
  domain: string;
57
+ /** Topics folded into this domain (see `USAGE_DOMAIN_FOLDS`). */
58
+ includes?: string[];
49
59
  tokenBudget: number;
50
60
  estimatedTokens: number;
51
61
  page: number;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.USAGE_DOMAIN_TOKEN_BUDGET = exports.USAGE_OVERVIEW_TOKEN_BUDGET = void 0;
3
+ exports.USAGE_DOMAIN_TOKEN_BUDGET = exports.USAGE_DOMAIN_FOLDS = exports.USAGE_OVERVIEW_TOKEN_BUDGET = void 0;
4
4
  exports.estimateUsageTokens = estimateUsageTokens;
5
5
  exports.usageDomains = usageDomains;
6
6
  exports.buildUsageOverview = buildUsageOverview;
@@ -34,6 +34,14 @@ const command_manifest_1 = require("./command-manifest");
34
34
  * only then whether the bound is still the right size.
35
35
  */
36
36
  exports.USAGE_OVERVIEW_TOKEN_BUDGET = 240;
37
+ /**
38
+ * Topics listed under another domain instead of as their own overview line.
39
+ * Every command keeps its own id (`skrr proposals list`); only the discovery
40
+ * grouping changes, and `skrr usage <folded>` resolves to the host page, which
41
+ * names the folded topic in an `Includes:` line so it stays reachable.
42
+ * `proposals` is the unified proposal inbox, so it reads as part of `inbox`.
43
+ */
44
+ exports.USAGE_DOMAIN_FOLDS = { proposals: 'inbox' };
37
45
  exports.USAGE_DOMAIN_TOKEN_BUDGET = 500;
38
46
  const GENERIC_FLAGS = new Set(['bare', 'help', 'json', 'token', 'workspace']);
39
47
  /**
@@ -74,7 +82,8 @@ function buildUsageOverview(manifest, bin = 'skrr') {
74
82
  }
75
83
  /** Return case-insensitive manifest domain names without inventing aliases. */
76
84
  function resolveUsageDomain(manifest, value) {
77
- const needle = value.trim().toLowerCase();
85
+ const trimmed = value.trim().toLowerCase();
86
+ const needle = exports.USAGE_DOMAIN_FOLDS[trimmed] ?? trimmed;
78
87
  return usageDomains(manifest).find(({ domain }) => domain.toLowerCase() === needle)?.domain;
79
88
  }
80
89
  /**
@@ -95,10 +104,12 @@ function buildUsageReference(manifest, domainInput, options = {}) {
95
104
  if (!Number.isInteger(requested) || index < 0 || index >= pages.length)
96
105
  return undefined;
97
106
  const data = pages[index];
107
+ const includes = foldedTopics(manifest, domain);
98
108
  const draft = {
99
109
  object: 'sky_usage_reference',
100
110
  generatedFrom: 'oclif.manifest.json',
101
111
  domain,
112
+ ...(includes.length > 0 ? { includes } : {}),
102
113
  tokenBudget: exports.USAGE_DOMAIN_TOKEN_BUDGET,
103
114
  estimatedTokens: 0,
104
115
  page: requested,
@@ -122,6 +133,8 @@ function renderUsageReference(reference, bin = 'skrr') {
122
133
  `${reference.domain} commands — page ${reference.page}/${reference.pages} ` +
123
134
  `(${reference.commandCount} total; manifest-derived).`,
124
135
  ];
136
+ if (reference.includes?.length)
137
+ lines.push(`Includes: ${reference.includes.join(', ')}.`);
125
138
  for (const command of reference.data) {
126
139
  const flags = command.keyFlags.length > 0 ? ` Flags: ${command.keyFlags.join(', ')}.` : '';
127
140
  lines.push(`${bin} ${command.command} — ${command.description}.${flags} Example: ${command.example}`);
@@ -167,13 +180,21 @@ function auditUsageBudgets(manifest, bin = 'skrr') {
167
180
  function groupEntries(manifest) {
168
181
  const grouped = new Map();
169
182
  for (const entry of (0, command_manifest_1.visibleCommandEntries)(manifest)) {
170
- const domain = entry.id.split(':')[0];
183
+ const topic = entry.id.split(':')[0];
184
+ const domain = exports.USAGE_DOMAIN_FOLDS[topic] ?? topic;
171
185
  const existing = grouped.get(domain) ?? [];
172
186
  existing.push(entry);
173
187
  grouped.set(domain, existing);
174
188
  }
175
189
  return grouped;
176
190
  }
191
+ function foldedTopics(manifest, domain) {
192
+ const present = new Set((0, command_manifest_1.visibleCommandEntries)(manifest).map((entry) => entry.id.split(':')[0]));
193
+ return Object.entries(exports.USAGE_DOMAIN_FOLDS)
194
+ .filter(([topic, host]) => host === domain && present.has(topic))
195
+ .map(([topic]) => topic)
196
+ .sort();
197
+ }
177
198
  function usageCommand(entry, bin) {
178
199
  const flags = Object.entries(entry.flags)
179
200
  .filter(([, value]) => !value.hidden)
@@ -0,0 +1,12 @@
1
+ type RunLike = Record<string, unknown>;
2
+ /**
3
+ * Human lines for a run's duration budget and for a run the budget stopped.
4
+ *
5
+ * The server derives `budget` per read and stamps `error.code =
6
+ * WORKFLOW_RUN_DEADLINE` on a run it cancelled; this only words them. A run with
7
+ * neither prints nothing: no budget is not "0 minutes left".
8
+ */
9
+ export declare function describeRunBudget(run: RunLike): string[];
10
+ /** The status cell, naming a time-limit stop where the plain status would hide it. */
11
+ export declare function runStatusLabel(run: RunLike): string;
12
+ export {};