@skrr-ai/cli 0.1.14 → 0.1.17

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 (107) hide show
  1. package/README.md +3 -0
  2. package/dist/base-command.d.ts +14 -0
  3. package/dist/base-command.js +60 -1
  4. package/dist/commands/agents/create.d.ts +1 -0
  5. package/dist/commands/agents/create.js +6 -1
  6. package/dist/commands/agents/instructions/add.d.ts +21 -0
  7. package/dist/commands/agents/instructions/add.js +115 -0
  8. package/dist/commands/agents/instructions/disable.d.ts +13 -0
  9. package/dist/commands/agents/instructions/disable.js +50 -0
  10. package/dist/commands/agents/instructions/edit.d.ts +16 -0
  11. package/dist/commands/agents/instructions/edit.js +79 -0
  12. package/dist/commands/agents/instructions/enable.d.ts +13 -0
  13. package/dist/commands/agents/instructions/enable.js +50 -0
  14. package/dist/commands/agents/instructions/move.d.ts +15 -0
  15. package/dist/commands/agents/instructions/move.js +61 -0
  16. package/dist/commands/agents/instructions/preview.d.ts +13 -0
  17. package/dist/commands/agents/instructions/preview.js +59 -0
  18. package/dist/commands/agents/instructions/remove.d.ts +13 -0
  19. package/dist/commands/agents/instructions/remove.js +49 -0
  20. package/dist/commands/agents/instructions/set.d.ts +17 -0
  21. package/dist/commands/agents/instructions/set.js +79 -0
  22. package/dist/commands/agents/instructions/show.d.ts +15 -0
  23. package/dist/commands/agents/instructions/show.js +76 -0
  24. package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
  25. package/dist/commands/agents/instructions/upgrade.js +77 -0
  26. package/dist/commands/agents/show.js +5 -0
  27. package/dist/commands/agents/triggers/policy.js +8 -2
  28. package/dist/commands/agents/update.d.ts +1 -0
  29. package/dist/commands/agents/update.js +8 -2
  30. package/dist/commands/commitments/autonomy.js +9 -1
  31. package/dist/commands/commitments/create.d.ts +8 -5
  32. package/dist/commands/commitments/create.js +38 -8
  33. package/dist/commands/commitments/doctor.d.ts +19 -0
  34. package/dist/commands/commitments/doctor.js +72 -0
  35. package/dist/commands/commitments/preflight.d.ts +3 -1
  36. package/dist/commands/commitments/preflight.js +25 -3
  37. package/dist/commands/commitments/reports.js +15 -2
  38. package/dist/commands/commitments/update.d.ts +9 -2
  39. package/dist/commands/commitments/update.js +36 -2
  40. package/dist/commands/daemon/index.js +2 -0
  41. package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
  42. package/dist/commands/daemon/recover-offline-queue.js +24 -0
  43. package/dist/commands/daemon/start.js +29 -1
  44. package/dist/commands/instruction-blocks/create.d.ts +19 -0
  45. package/dist/commands/instruction-blocks/create.js +94 -0
  46. package/dist/commands/instruction-blocks/delete.d.ts +13 -0
  47. package/dist/commands/instruction-blocks/delete.js +35 -0
  48. package/dist/commands/instruction-blocks/list.d.ts +13 -0
  49. package/dist/commands/instruction-blocks/list.js +71 -0
  50. package/dist/commands/instruction-blocks/show.d.ts +12 -0
  51. package/dist/commands/instruction-blocks/show.js +35 -0
  52. package/dist/commands/instruction-blocks/update.d.ts +23 -0
  53. package/dist/commands/instruction-blocks/update.js +94 -0
  54. package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
  55. package/dist/commands/spaces/auto-executor/preview.js +62 -3
  56. package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
  57. package/dist/commands/spaces/auto-executor/update.js +78 -7
  58. package/dist/commands/spaces/capacity.js +14 -2
  59. package/dist/commands/spaces/update.d.ts +4 -0
  60. package/dist/commands/spaces/update.js +49 -2
  61. package/dist/commands/tasks/complete.d.ts +12 -5
  62. package/dist/commands/tasks/complete.js +107 -28
  63. package/dist/commands/tasks/continue-ready.js +9 -1
  64. package/dist/commands/tasks/create.d.ts +1 -0
  65. package/dist/commands/tasks/create.js +27 -2
  66. package/dist/commands/tasks/deliverable/add.d.ts +23 -0
  67. package/dist/commands/tasks/deliverable/add.js +70 -0
  68. package/dist/commands/tasks/deliverable/confirm.d.ts +35 -0
  69. package/dist/commands/tasks/deliverable/confirm.js +77 -0
  70. package/dist/commands/tasks/events/append.d.ts +1 -1
  71. package/dist/commands/tasks/events/append.js +4 -4
  72. package/dist/commands/tasks/expectations.d.ts +12 -0
  73. package/dist/commands/tasks/expectations.js +111 -0
  74. package/dist/commands/tasks/judge.d.ts +29 -0
  75. package/dist/commands/tasks/judge.js +68 -0
  76. package/dist/commands/tasks/report.d.ts +1 -1
  77. package/dist/commands/tasks/report.js +27 -3
  78. package/dist/commands/tasks/trust.d.ts +35 -0
  79. package/dist/commands/tasks/trust.js +75 -0
  80. package/dist/commands/tasks/update.d.ts +1 -0
  81. package/dist/commands/tasks/update.js +34 -4
  82. package/dist/commands/tasks/waive.d.ts +25 -0
  83. package/dist/commands/tasks/waive.js +58 -0
  84. package/dist/lib/agent-config.js +5 -0
  85. package/dist/lib/agent-instructions.d.ts +15 -0
  86. package/dist/lib/agent-instructions.js +55 -0
  87. package/dist/lib/command-hygiene.d.ts +6 -1
  88. package/dist/lib/command-hygiene.js +49 -29
  89. package/dist/lib/commitments.d.ts +28 -3
  90. package/dist/lib/commitments.js +20 -11
  91. package/dist/lib/exec-runtime-binary.d.ts +13 -0
  92. package/dist/lib/exec-runtime-binary.js +45 -0
  93. package/dist/lib/git-task.d.ts +37 -0
  94. package/dist/lib/git-task.js +151 -0
  95. package/dist/lib/instruction-blocks.d.ts +29 -0
  96. package/dist/lib/instruction-blocks.js +159 -0
  97. package/dist/lib/runtime-start-verification.d.ts +49 -0
  98. package/dist/lib/runtime-start-verification.js +108 -0
  99. package/dist/lib/task-execution.d.ts +18 -2
  100. package/dist/lib/task-execution.js +19 -14
  101. package/dist/lib/task-transcript.js +1 -1
  102. package/dist/lib/tasks.d.ts +50 -0
  103. package/dist/lib/tasks.js +93 -2
  104. package/dist/node_modules/@skrr-ai/data-provider/index.js +2393 -2197
  105. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  106. package/oclif.manifest.json +17725 -15563
  107. package/package.json +4 -1
@@ -34,11 +34,162 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.GitTaskError = void 0;
37
+ exports.commitArtifacts = commitArtifacts;
38
+ exports.inspectTaskDeliveryArtifacts = inspectTaskDeliveryArtifacts;
37
39
  exports.currentGitBranch = currentGitBranch;
38
40
  exports.currentGitTaskBranch = currentGitTaskBranch;
39
41
  exports.checkoutTaskBranch = checkoutTaskBranch;
40
42
  const node_child_process_1 = require("node:child_process");
41
43
  const taskReferences = __importStar(require("@skrr-ai/data-provider"));
44
+ const COMMIT_ARTIFACT_RE = /(?:^|[\s,;:(/])(?:commit\s+)?([0-9a-f]{7,40})(?=$|[\s,;:)?#])/gi;
45
+ const GITHUB_PR_ARTIFACT_RE = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+/i;
46
+ function runCommand(command, args, cwd) {
47
+ const result = (0, node_child_process_1.spawnSync)(command, args, {
48
+ cwd,
49
+ encoding: 'utf8',
50
+ stdio: ['ignore', 'pipe', 'pipe'],
51
+ timeout: 15_000,
52
+ });
53
+ return {
54
+ status: result.status,
55
+ stdout: typeof result.stdout === 'string' ? result.stdout.trim() : '',
56
+ stderr: typeof result.stderr === 'string' ? result.stderr.trim() : '',
57
+ };
58
+ }
59
+ function commitArtifacts(deliverables) {
60
+ const commits = [];
61
+ for (const match of deliverables.matchAll(COMMIT_ARTIFACT_RE)) {
62
+ const sha = match[1].toLowerCase();
63
+ if (!commits.includes(sha))
64
+ commits.push(sha);
65
+ }
66
+ return commits;
67
+ }
68
+ function safeTaskBranchRef(taskRef, sha) {
69
+ const match = taskRef.match(/[A-Za-z][A-Za-z0-9]{1,31}-\d{1,12}/);
70
+ const label = match?.[0].toUpperCase() || 'recovery';
71
+ return `oversky/task/${label}/recovery-${sha.slice(0, 12)}`;
72
+ }
73
+ function resolveRemoteDefault(cwd, remote, runner) {
74
+ const live = runner('git', ['ls-remote', '--symref', remote, 'HEAD'], cwd);
75
+ if (live.status === 0) {
76
+ const match = live.stdout.match(/^ref:\s+refs\/heads\/(\S+)\s+HEAD$/m);
77
+ if (match)
78
+ return match[1];
79
+ }
80
+ const symbolic = runner('git', ['symbolic-ref', '--quiet', '--short', `refs/remotes/${remote}/HEAD`], cwd);
81
+ if (symbolic.status === 0 && symbolic.stdout.startsWith(`${remote}/`)) {
82
+ return symbolic.stdout.slice(remote.length + 1);
83
+ }
84
+ for (const fallback of ['main', 'master']) {
85
+ if (runner('git', ['show-ref', '--verify', '--quiet', `refs/remotes/${remote}/${fallback}`], cwd)
86
+ .status === 0) {
87
+ return fallback;
88
+ }
89
+ }
90
+ return null;
91
+ }
92
+ /**
93
+ * Build a receipt for commit/PR deliverables before `tasks complete` writes
94
+ * anything. Commit reachability is checked against a freshly fetched remote
95
+ * default branch. A locally dangling commit is first anchored under refs/heads
96
+ * so even a held completion cannot leave it exposed to `git gc`.
97
+ */
98
+ function inspectTaskDeliveryArtifacts(opts) {
99
+ const cwd = opts.cwd ?? process.cwd();
100
+ const runner = opts.runner ?? runCommand;
101
+ const commits = commitArtifacts(opts.deliverables);
102
+ const verifiedCommits = [];
103
+ let containingBranch;
104
+ for (const claimed of commits) {
105
+ const resolved = runner('git', ['rev-parse', '--verify', `${claimed}^{commit}`], cwd);
106
+ if (resolved.status !== 0 || !/^[0-9a-f]{40}$/i.test(resolved.stdout))
107
+ continue;
108
+ const sha = resolved.stdout.toLowerCase();
109
+ verifiedCommits.push(sha);
110
+ const branches = runner('git', ['for-each-ref', '--format=%(refname:short)', '--contains', sha, 'refs/heads'], cwd);
111
+ const firstBranch = branches.stdout.split('\n').find(Boolean);
112
+ if (firstBranch) {
113
+ containingBranch ||= firstBranch;
114
+ continue;
115
+ }
116
+ const recoveryBranch = safeTaskBranchRef(opts.taskRef, sha);
117
+ const existing = runner('git', ['show-ref', '--verify', '--quiet', `refs/heads/${recoveryBranch}`], cwd);
118
+ if (existing.status !== 0) {
119
+ const anchored = runner('git', ['update-ref', `refs/heads/${recoveryBranch}`, sha], cwd);
120
+ if (anchored.status !== 0) {
121
+ throw new Error(`Commit ${sha} is not reachable from a named local branch, and ${recoveryBranch} ` +
122
+ 'could not be created. Completion was refused so the commit is not reported as safe.');
123
+ }
124
+ containingBranch ||= recoveryBranch;
125
+ }
126
+ else {
127
+ containingBranch ||= recoveryBranch;
128
+ }
129
+ }
130
+ const prUrl = opts.deliverables.match(GITHUB_PR_ARTIFACT_RE)?.[0];
131
+ if (prUrl) {
132
+ const viewed = runner('gh', [
133
+ 'pr',
134
+ 'view',
135
+ prUrl,
136
+ '--json',
137
+ 'mergedAt,headRefName,headRefOid,baseRefName,mergeCommit,url',
138
+ ], cwd);
139
+ if (viewed.status === 0) {
140
+ try {
141
+ const pr = JSON.parse(viewed.stdout);
142
+ const headSha = typeof pr.headRefOid === 'string' ? pr.headRefOid : undefined;
143
+ const mergeCommit = pr.mergeCommit && typeof pr.mergeCommit === 'object'
144
+ ? pr.mergeCommit.oid
145
+ : undefined;
146
+ return {
147
+ kind: 'pull_request',
148
+ state: pr.mergedAt ? 'integrated' : 'pr_open',
149
+ prUrl,
150
+ ...(typeof pr.headRefName === 'string' ? { branch: pr.headRefName } : {}),
151
+ ...(headSha && /^[0-9a-f]{7,40}$/i.test(headSha) ? { headSha } : {}),
152
+ ...(typeof pr.baseRefName === 'string' ? { integrationBranch: pr.baseRefName } : {}),
153
+ ...(typeof mergeCommit === 'string' && /^[0-9a-f]{7,40}$/i.test(mergeCommit)
154
+ ? { mergeCommit }
155
+ : {}),
156
+ };
157
+ }
158
+ catch {
159
+ // Invalid/unavailable gh evidence is not proof of integration.
160
+ }
161
+ }
162
+ return { kind: 'pull_request', state: 'pr_open', prUrl };
163
+ }
164
+ if (!commits.length || verifiedCommits.length !== commits.length)
165
+ return null;
166
+ const remotes = runner('git', ['remote'], cwd).stdout.split('\n').filter(Boolean);
167
+ const remote = remotes.includes('origin') ? 'origin' : remotes[0];
168
+ const integrationBranch = remote ? resolveRemoteDefault(cwd, remote, runner) : null;
169
+ let integrated = false;
170
+ if (remote && integrationBranch) {
171
+ const fetched = runner('git', [
172
+ 'fetch',
173
+ '--quiet',
174
+ '--no-tags',
175
+ remote,
176
+ `+refs/heads/${integrationBranch}:refs/remotes/${remote}/${integrationBranch}`,
177
+ ], cwd);
178
+ integrated =
179
+ fetched.status === 0 &&
180
+ verifiedCommits.every((sha) => runner('git', ['merge-base', '--is-ancestor', sha, `refs/remotes/${remote}/${integrationBranch}`], cwd).status === 0);
181
+ }
182
+ const headSha = verifiedCommits[verifiedCommits.length - 1];
183
+ return {
184
+ kind: 'commit',
185
+ state: integrated ? 'integrated' : 'branch_ready',
186
+ commitShas: verifiedCommits,
187
+ headSha,
188
+ ...(containingBranch ? { branch: containingBranch } : {}),
189
+ ...(integrationBranch ? { integrationBranch } : {}),
190
+ ...(integrated ? { mergeCommit: headSha } : {}),
191
+ };
192
+ }
42
193
  class GitTaskError extends Error {
43
194
  code;
44
195
  constructor(message, code) {
@@ -0,0 +1,29 @@
1
+ import type { AgentPromptBlockEntry, AgentPromptComposition, InstructionBlock, InstructionBlockCatalogResponse } from '@skrr-ai/data-provider';
2
+ /**
3
+ * Helpers shared by the reusable Block Library commands and the per-Agent
4
+ * composition commands. Keep this deliberately about data shape, not HTTP:
5
+ * the server remains the authority that resolves versions, bindings and
6
+ * rendered text before an Agent is saved.
7
+ */
8
+ export declare function parseJsonFlag<T>(value: string | undefined, flag: string): T | undefined;
9
+ export declare function parseStringArrayFlag(value: string | undefined, flag: string): string[] | undefined;
10
+ /** Parse repeated `--binding key=value` flags without ever guessing a key. */
11
+ export declare function parseBindings(values: string[] | undefined): Record<string, string> | undefined;
12
+ /**
13
+ * Read the structured source when it exists; otherwise preserve the legacy
14
+ * prompt byte-for-byte as one inline entry before changing anything. This is
15
+ * the same compatibility move as the server-side resolver, so `add` never
16
+ * drops an Agent's existing instructions merely because it predates Blocks.
17
+ */
18
+ export declare function compositionForAgent(agent: {
19
+ promptComposition?: unknown;
20
+ instructions?: unknown;
21
+ }): AgentPromptComposition;
22
+ export declare function nextInstanceId(kind?: 'inline' | 'library'): string;
23
+ export declare function entryByInstance(composition: AgentPromptComposition, instanceId: string): AgentPromptBlockEntry;
24
+ export declare function replaceEntry(composition: AgentPromptComposition, instanceId: string, replacement: AgentPromptBlockEntry): AgentPromptComposition;
25
+ export declare function removeEntry(composition: AgentPromptComposition, instanceId: string): AgentPromptComposition;
26
+ /** Move one instance immediately before or after a different existing instance. */
27
+ export declare function moveEntry(composition: AgentPromptComposition, instanceId: string, anchorId: string, placement: 'before' | 'after'): AgentPromptComposition;
28
+ export declare function findBlock(catalog: InstructionBlockCatalogResponse, reference: string): InstructionBlock | undefined;
29
+ export declare function renderInstructionBlockDetail(block: InstructionBlock): string[];
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseJsonFlag = parseJsonFlag;
4
+ exports.parseStringArrayFlag = parseStringArrayFlag;
5
+ exports.parseBindings = parseBindings;
6
+ exports.compositionForAgent = compositionForAgent;
7
+ exports.nextInstanceId = nextInstanceId;
8
+ exports.entryByInstance = entryByInstance;
9
+ exports.replaceEntry = replaceEntry;
10
+ exports.removeEntry = removeEntry;
11
+ exports.moveEntry = moveEntry;
12
+ exports.findBlock = findBlock;
13
+ exports.renderInstructionBlockDetail = renderInstructionBlockDetail;
14
+ const node_crypto_1 = require("node:crypto");
15
+ /**
16
+ * Helpers shared by the reusable Block Library commands and the per-Agent
17
+ * composition commands. Keep this deliberately about data shape, not HTTP:
18
+ * the server remains the authority that resolves versions, bindings and
19
+ * rendered text before an Agent is saved.
20
+ */
21
+ function parseJsonFlag(value, flag) {
22
+ if (value === undefined)
23
+ return undefined;
24
+ try {
25
+ return JSON.parse(value);
26
+ }
27
+ catch {
28
+ throw new Error(`${flag} must be valid JSON`);
29
+ }
30
+ }
31
+ function parseStringArrayFlag(value, flag) {
32
+ const parsed = parseJsonFlag(value, flag);
33
+ if (parsed === undefined)
34
+ return undefined;
35
+ if (!Array.isArray(parsed) || parsed.some((entry) => typeof entry !== 'string')) {
36
+ throw new Error(`${flag} must be a JSON array of strings`);
37
+ }
38
+ return parsed;
39
+ }
40
+ /** Parse repeated `--binding key=value` flags without ever guessing a key. */
41
+ function parseBindings(values) {
42
+ if (!values?.length)
43
+ return undefined;
44
+ const bindings = {};
45
+ for (const value of values) {
46
+ const separator = value.indexOf('=');
47
+ if (separator < 1) {
48
+ throw new Error(`Invalid binding "${value}". Use --binding key=value.`);
49
+ }
50
+ const key = value.slice(0, separator).trim();
51
+ if (!/^[a-z][a-z0-9_]{0,63}$/.test(key)) {
52
+ throw new Error(`Invalid binding key "${key}".`);
53
+ }
54
+ if (Object.prototype.hasOwnProperty.call(bindings, key)) {
55
+ throw new Error(`Binding "${key}" was supplied more than once.`);
56
+ }
57
+ bindings[key] = value.slice(separator + 1).trim();
58
+ }
59
+ return bindings;
60
+ }
61
+ /**
62
+ * Read the structured source when it exists; otherwise preserve the legacy
63
+ * prompt byte-for-byte as one inline entry before changing anything. This is
64
+ * the same compatibility move as the server-side resolver, so `add` never
65
+ * drops an Agent's existing instructions merely because it predates Blocks.
66
+ */
67
+ function compositionForAgent(agent) {
68
+ const composition = agent.promptComposition;
69
+ if (composition &&
70
+ typeof composition === 'object' &&
71
+ !Array.isArray(composition) &&
72
+ composition.schemaVersion === 1 &&
73
+ Array.isArray(composition.entries)) {
74
+ return composition;
75
+ }
76
+ const instructions = agent.instructions;
77
+ if (typeof instructions === 'string' && instructions.length) {
78
+ return {
79
+ schemaVersion: 1,
80
+ entries: [
81
+ {
82
+ instanceId: nextInstanceId('inline'),
83
+ enabled: true,
84
+ source: { kind: 'inline', title: 'Core instructions', body: instructions },
85
+ },
86
+ ],
87
+ };
88
+ }
89
+ return { schemaVersion: 1, entries: [] };
90
+ }
91
+ function nextInstanceId(kind = 'inline') {
92
+ return `${kind}_${(0, node_crypto_1.randomUUID)()}`;
93
+ }
94
+ function entryByInstance(composition, instanceId) {
95
+ const entry = composition.entries.find((candidate) => candidate.instanceId === instanceId);
96
+ if (!entry) {
97
+ throw new Error(`No instruction block instance "${instanceId}". Run \`skrr agents instructions show\` to list this Agent's instances.`);
98
+ }
99
+ return entry;
100
+ }
101
+ function replaceEntry(composition, instanceId, replacement) {
102
+ entryByInstance(composition, instanceId);
103
+ return {
104
+ schemaVersion: 1,
105
+ entries: composition.entries.map((entry) => entry.instanceId === instanceId ? replacement : entry),
106
+ };
107
+ }
108
+ function removeEntry(composition, instanceId) {
109
+ entryByInstance(composition, instanceId);
110
+ return {
111
+ schemaVersion: 1,
112
+ entries: composition.entries.filter((entry) => entry.instanceId !== instanceId),
113
+ };
114
+ }
115
+ /** Move one instance immediately before or after a different existing instance. */
116
+ function moveEntry(composition, instanceId, anchorId, placement) {
117
+ if (instanceId === anchorId)
118
+ throw new Error('A block cannot be moved relative to itself.');
119
+ const entry = entryByInstance(composition, instanceId);
120
+ entryByInstance(composition, anchorId);
121
+ const remaining = composition.entries.filter((candidate) => candidate.instanceId !== instanceId);
122
+ const anchor = remaining.findIndex((candidate) => candidate.instanceId === anchorId);
123
+ const insertAt = placement === 'before' ? anchor : anchor + 1;
124
+ return {
125
+ schemaVersion: 1,
126
+ entries: [...remaining.slice(0, insertAt), entry, ...remaining.slice(insertAt)],
127
+ };
128
+ }
129
+ function findBlock(catalog, reference) {
130
+ return [...catalog.official, ...catalog.library].find((block) => block.id === reference);
131
+ }
132
+ function renderInstructionBlockDetail(block) {
133
+ const current = block.versions.find((version) => version.version === block.currentVersion);
134
+ const lines = [
135
+ `${block.name} (${block.id})`,
136
+ `Source: ${block.source}`,
137
+ `Visibility: ${block.visibility}`,
138
+ `Status: ${block.status}`,
139
+ `Version: v${block.currentVersion}`,
140
+ ];
141
+ if (block.summary)
142
+ lines.push(`Summary: ${block.summary}`);
143
+ if (block.tags.length)
144
+ lines.push(`Tags: ${block.tags.join(', ')}`);
145
+ if (current) {
146
+ if (current.requirements.length) {
147
+ lines.push(`Needs: ${current.requirements
148
+ .map((requirement) => `${requirement.kind}:${requirement.key}${requirement.access ? ` (${requirement.access})` : ''}`)
149
+ .join(', ')} — declarative only; this does not grant access`);
150
+ }
151
+ if (current.parameterSchema.length) {
152
+ lines.push(`Parameters: ${current.parameterSchema
153
+ .map((parameter) => `${parameter.key}${parameter.required ? ' (required)' : ''}`)
154
+ .join(', ')}`);
155
+ }
156
+ lines.push('', 'Instructions:', ...current.bodyTemplate.split('\n').map((line) => ` ${line}`));
157
+ }
158
+ return lines;
159
+ }
@@ -0,0 +1,49 @@
1
+ /** Matches the runtime's EX_CONFIG result for a permanent local configuration failure. */
2
+ export declare const DAEMON_START_REAUTH_EXIT = 78;
3
+ /** Matches the runtime's EX_TEMPFAIL result when a service cannot remain running. */
4
+ export declare const DAEMON_START_UNVERIFIED_EXIT = 75;
5
+ export type RuntimeStartStatus = {
6
+ profile: string;
7
+ serverUrl: string | null;
8
+ cwd: string | null;
9
+ reauth: {
10
+ needed: boolean;
11
+ reason: string | null;
12
+ };
13
+ running: boolean;
14
+ runtimeStatus: string | null;
15
+ };
16
+ export type RuntimeStartVerification = {
17
+ status: 'running';
18
+ } | {
19
+ status: 'reauth_required';
20
+ profile: string;
21
+ reason: string | null;
22
+ recoveryCommand: string;
23
+ } | {
24
+ status: 'not_running';
25
+ profile: string;
26
+ runtimeStatus: string | null;
27
+ } | {
28
+ status: 'unreadable_status';
29
+ profile: string;
30
+ };
31
+ /**
32
+ * The status probe must observe the same profile as the start request. A
33
+ * runtime's `--profile` flag is global, so it is deliberately returned after
34
+ * `status` here and normalized by execRuntimeBinary before spawning.
35
+ */
36
+ export declare function statusArgsForStart(argv: readonly string[]): string[];
37
+ export declare function parseRuntimeStartStatus(value: unknown, fallbackProfile: string): RuntimeStartStatus | null;
38
+ /**
39
+ * `skrr daemon login` is the public wrapper for daemon-scope login. Explicit
40
+ * server, profile, and cwd make this remedy target the stopped daemon rather
41
+ * than whichever CLI profile or working directory happens to be current.
42
+ */
43
+ export declare function daemonLoginRecoveryCommand(status: RuntimeStartStatus): string;
44
+ /**
45
+ * A runtime binary may predate a CLI update. Treat its successful process exit
46
+ * as an unverified supervisor request until `status --json` proves it either
47
+ * stayed alive or named the permanent re-auth latch it wrote.
48
+ */
49
+ export declare function verifyRuntimeStart(statusOutput: string, statusExitCode: number, argv: readonly string[]): RuntimeStartVerification;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DAEMON_START_UNVERIFIED_EXIT = exports.DAEMON_START_REAUTH_EXIT = void 0;
4
+ exports.statusArgsForStart = statusArgsForStart;
5
+ exports.parseRuntimeStartStatus = parseRuntimeStartStatus;
6
+ exports.daemonLoginRecoveryCommand = daemonLoginRecoveryCommand;
7
+ exports.verifyRuntimeStart = verifyRuntimeStart;
8
+ const keychain_1 = require("./keychain");
9
+ /** Matches the runtime's EX_CONFIG result for a permanent local configuration failure. */
10
+ exports.DAEMON_START_REAUTH_EXIT = 78;
11
+ /** Matches the runtime's EX_TEMPFAIL result when a service cannot remain running. */
12
+ exports.DAEMON_START_UNVERIFIED_EXIT = 75;
13
+ function asRecord(value) {
14
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
15
+ ? value
16
+ : null;
17
+ }
18
+ function asString(value) {
19
+ return typeof value === 'string' && value.length > 0 ? value : null;
20
+ }
21
+ function profileFromArgv(argv) {
22
+ return ((0, keychain_1.resolveProfileFromArgv)(argv) ??
23
+ process.env.OVERSKY_PROFILE ??
24
+ (0, keychain_1.getActiveProfile)() ??
25
+ keychain_1.DEFAULT_PROFILE);
26
+ }
27
+ function quoteShellArg(value) {
28
+ return /^[A-Za-z0-9_./:@+=-]+$/.test(value) ? value : `'${value.replaceAll("'", "'\\''")}'`;
29
+ }
30
+ /**
31
+ * The status probe must observe the same profile as the start request. A
32
+ * runtime's `--profile` flag is global, so it is deliberately returned after
33
+ * `status` here and normalized by execRuntimeBinary before spawning.
34
+ */
35
+ function statusArgsForStart(argv) {
36
+ const requested = (0, keychain_1.resolveProfileFromArgv)(argv);
37
+ return requested ? ['status', '--json', '--profile', requested] : ['status', '--json'];
38
+ }
39
+ function parseRuntimeStartStatus(value, fallbackProfile) {
40
+ const root = asRecord(value);
41
+ if (!root)
42
+ return null;
43
+ const auth = asRecord(root.auth);
44
+ const reauth = asRecord(auth?.reauth);
45
+ const profile = asRecord(root.profile);
46
+ const liveDaemon = asRecord(root.liveDaemon);
47
+ const service = asRecord(root.service);
48
+ const runtime = asRecord(service?.runtime);
49
+ return {
50
+ profile: asString(profile?.name) ?? fallbackProfile,
51
+ serverUrl: asString(root.serverUrl),
52
+ cwd: asString(root.cwd),
53
+ reauth: {
54
+ needed: reauth?.needed === true,
55
+ reason: asString(reauth?.reason),
56
+ },
57
+ running: liveDaemon?.detected === true || runtime?.status === 'running',
58
+ runtimeStatus: asString(runtime?.status),
59
+ };
60
+ }
61
+ /**
62
+ * `skrr daemon login` is the public wrapper for daemon-scope login. Explicit
63
+ * server, profile, and cwd make this remedy target the stopped daemon rather
64
+ * than whichever CLI profile or working directory happens to be current.
65
+ */
66
+ function daemonLoginRecoveryCommand(status) {
67
+ const args = ['skrr', 'daemon', 'login', '--profile', quoteShellArg(status.profile)];
68
+ if (status.serverUrl)
69
+ args.push('--server', quoteShellArg(status.serverUrl));
70
+ if (status.cwd)
71
+ args.push('--cwd', quoteShellArg(status.cwd));
72
+ return args.join(' ');
73
+ }
74
+ /**
75
+ * A runtime binary may predate a CLI update. Treat its successful process exit
76
+ * as an unverified supervisor request until `status --json` proves it either
77
+ * stayed alive or named the permanent re-auth latch it wrote.
78
+ */
79
+ function verifyRuntimeStart(statusOutput, statusExitCode, argv) {
80
+ const fallbackProfile = profileFromArgv(argv);
81
+ if (statusExitCode !== 0)
82
+ return { status: 'unreadable_status', profile: fallbackProfile };
83
+ let parsed;
84
+ try {
85
+ parsed = JSON.parse(statusOutput);
86
+ }
87
+ catch {
88
+ return { status: 'unreadable_status', profile: fallbackProfile };
89
+ }
90
+ const status = parseRuntimeStartStatus(parsed, fallbackProfile);
91
+ if (!status)
92
+ return { status: 'unreadable_status', profile: fallbackProfile };
93
+ if (status.reauth.needed) {
94
+ return {
95
+ status: 'reauth_required',
96
+ profile: status.profile,
97
+ reason: status.reauth.reason,
98
+ recoveryCommand: daemonLoginRecoveryCommand(status),
99
+ };
100
+ }
101
+ if (status.running)
102
+ return { status: 'running' };
103
+ return {
104
+ status: 'not_running',
105
+ profile: status.profile,
106
+ runtimeStatus: status.runtimeStatus,
107
+ };
108
+ }
@@ -46,12 +46,28 @@ export type TaskSnapshot = {
46
46
  export type SpaceCapacity = {
47
47
  object: 'space_task_capacity';
48
48
  space: TaskRecord;
49
- maxActiveTasks: number;
49
+ /**
50
+ * `null` means UNLIMITED, matching the orchestration payload. Declaring
51
+ * these `number` was not merely a type lie — it broke the CLI build, and
52
+ * before that it reached users as `Capacity: 0/null active`, because
53
+ * `autoExecuteMaxActiveTasks` defaults to `0` (= unlimited) and so the
54
+ * unlimited case is the COMMON path, not an edge case (OSK-7087).
55
+ */
56
+ maxActiveTasks: number | null;
50
57
  activeCount: number;
51
- availableSlots: number;
58
+ availableSlots: number | null;
59
+ /** Live start-budget consumption, or `null` when no round is open. */
60
+ budget: SpaceCapacityBudget | null;
52
61
  activeTasks: TaskRecord[];
53
62
  sampledTaskCount: number;
54
63
  };
64
+ export type SpaceCapacityBudget = {
65
+ generation: number;
66
+ maxStarts: number;
67
+ consumedStarts: number;
68
+ remaining: number | null;
69
+ unlimited: boolean;
70
+ };
55
71
  export declare function statusOf(task: TaskRecord | null | undefined): string;
56
72
  export declare function isTerminalTask(task: TaskRecord): boolean;
57
73
  /**
@@ -79,10 +79,15 @@ function satisfiesWaitTarget(task, until) {
79
79
  return until === 'terminal' ? exports.TERMINAL_STATUSES.has(status) : status === until;
80
80
  }
81
81
  function isActiveTask(task) {
82
- const status = statusOf(task);
83
- return (status === 'in_progress' ||
84
- isNonEmptyString(task.checkoutRunId) ||
85
- isNonEmptyString(task.sessionId));
82
+ if (isTerminalTask(task))
83
+ return false;
84
+ const reasonCode = task.actionability?.reasonCode;
85
+ if (typeof reasonCode === 'string')
86
+ return reasonCode === 'execution_running';
87
+ // Compatibility for older servers that do not attach actionability. A
88
+ // workflow status or leftover session is not compute ownership; only the
89
+ // checkout is a conservative fallback.
90
+ return isNonEmptyString(task.checkoutRunId);
86
91
  }
87
92
  function isRecoveredExecutionTask(task) {
88
93
  if (!isStartableTask(task))
@@ -178,19 +183,25 @@ async function fetchSpaceTasks(spaceId, limit = 500) {
178
183
  return items;
179
184
  }
180
185
  async function getSpaceCapacity(spaceId, limit = 1000) {
181
- const [space, tasks] = await Promise.all([
186
+ const [space, tasks, preview] = await Promise.all([
182
187
  data_provider_1.dataService.getSpace(spaceId),
183
188
  fetchSpaceTasks(spaceId, limit),
189
+ data_provider_1.dataService.previewSpaceAutoExecutor(spaceId),
184
190
  ]);
185
191
  const activeTasks = tasks.filter(isActiveTask);
186
- const maxActiveTasks = normalizePositiveInt(space.autoExecuteMaxActiveTasks, 3);
187
- const activeCount = activeTasks.length;
192
+ const canonicalCapacity = preview?.plan?.capacity;
193
+ if (!canonicalCapacity) {
194
+ throw new Error('Space auto-executor preview did not return canonical capacity');
195
+ }
196
+ const maxActiveTasks = canonicalCapacity.maxActiveTasks;
197
+ const activeCount = canonicalCapacity.activeCount;
188
198
  return {
189
199
  object: 'space_task_capacity',
190
200
  space,
191
201
  maxActiveTasks,
192
202
  activeCount,
193
- availableSlots: Math.max(0, maxActiveTasks - activeCount),
203
+ availableSlots: canonicalCapacity.availableSlots,
204
+ budget: canonicalCapacity.budget ?? null,
194
205
  activeTasks,
195
206
  sampledTaskCount: tasks.length,
196
207
  };
@@ -314,9 +325,3 @@ function stringValue(value) {
314
325
  function isNonEmptyString(value) {
315
326
  return typeof value === 'string' && value.trim().length > 0;
316
327
  }
317
- function normalizePositiveInt(value, fallback) {
318
- const n = Number(value);
319
- if (!Number.isFinite(n) || n <= 0)
320
- return fallback;
321
- return Math.floor(n);
322
- }
@@ -53,7 +53,7 @@ function parseTaskOutputBlock(body) {
53
53
  return {
54
54
  status: field('status'),
55
55
  summary: field('summary'),
56
- artifacts: field('artifacts'),
56
+ artifacts: field('deliverables') || field('artifacts'),
57
57
  verdict: field('verdict'),
58
58
  };
59
59
  }
@@ -1,3 +1,4 @@
1
+ import type { ParsedDeliverableCoordinate, ParsedExpectationSpec } from '@skrr-ai/data-provider';
1
2
  export declare const TASK_PRIORITIES: readonly ["low", "medium", "high", "critical"];
2
3
  /**
3
4
  * The kinds `tasks events append --kind` will accept.
@@ -50,6 +51,14 @@ export type TaskCreateFlagValues = {
50
51
  'assignee-agent'?: string[];
51
52
  'assignee-user'?: string[];
52
53
  'execution-mode'?: string;
54
+ expectations?: Array<{
55
+ shape?: string;
56
+ kind?: string;
57
+ format?: string;
58
+ target?: string;
59
+ description?: string;
60
+ presetKey?: string;
61
+ }>;
53
62
  'start-date'?: string;
54
63
  'due-date'?: string;
55
64
  estimate?: number;
@@ -62,6 +71,47 @@ export type TaskCreateFlagValues = {
62
71
  * true ⇒ 'ephemeral', false ⇒ 'shared', undefined ⇒ undefined (inherit/leave).
63
72
  */
64
73
  export declare function executionIsolationFromWorktreeFlag(worktree: boolean | undefined): 'shared' | 'ephemeral' | undefined;
74
+ /**
75
+ * Parse an expectation spec.
76
+ *
77
+ * DELEGATES to `@skrr-ai/data-provider`, which owns the grammar and is read by
78
+ * the API and the web client too. The copy this replaces validated correctly
79
+ * and then described a syntax it refused — its messages named three different
80
+ * flags (`--requires`, `--output`, `--accept`) and used `git_ref:origin/main`
81
+ * as the example, a value the same function rejects. For an agent the refusal
82
+ * IS the documentation: it hits the error and corrects, so an error naming a
83
+ * rejected example costs a whole turn and can loop.
84
+ */
85
+ export declare function parseExpectation(spec: string): {
86
+ ok: true;
87
+ value: ParsedExpectationSpec;
88
+ } | {
89
+ ok: false;
90
+ error: string;
91
+ };
92
+ /** Parse several, failing on the first that cannot be read. */
93
+ export declare function parseExpectations(specs: string[] | undefined): {
94
+ ok: true;
95
+ value: ParsedExpectationSpec[];
96
+ } | {
97
+ ok: false;
98
+ error: string;
99
+ };
100
+ export declare const EXPECT_FLAG_HELP: string;
101
+ /**
102
+ * Parse a deliverable coordinate. Also delegated — `:` and `@` and now `/` all
103
+ * mean different things, and a second implementation of that grammar is how the
104
+ * declaring side and the recording side stop agreeing.
105
+ */
106
+ export declare function parseDeliverableCoordinate(spec: string): {
107
+ ok: true;
108
+ value: ParsedDeliverableCoordinate;
109
+ } | {
110
+ ok: false;
111
+ error: string;
112
+ };
113
+ /** Lifecycle values a caller may assert, for flag validation. */
114
+ export declare const DELIVERABLE_LIFECYCLE_VALUES: readonly ["local", "in_review", "durable", "blocked", "superseded"];
65
115
  export declare function buildTaskCreatePayload(body: TaskRecord, flags?: TaskCreateFlagValues): TaskRecord;
66
116
  /**
67
117
  * The Agent this CLI process is running AS, if any.