@skrr-ai/cli 0.1.14 → 0.1.15

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 (90) hide show
  1. package/dist/base-command.d.ts +14 -0
  2. package/dist/base-command.js +60 -1
  3. package/dist/commands/agents/create.d.ts +1 -0
  4. package/dist/commands/agents/create.js +6 -1
  5. package/dist/commands/agents/instructions/add.d.ts +21 -0
  6. package/dist/commands/agents/instructions/add.js +115 -0
  7. package/dist/commands/agents/instructions/disable.d.ts +13 -0
  8. package/dist/commands/agents/instructions/disable.js +50 -0
  9. package/dist/commands/agents/instructions/edit.d.ts +16 -0
  10. package/dist/commands/agents/instructions/edit.js +79 -0
  11. package/dist/commands/agents/instructions/enable.d.ts +13 -0
  12. package/dist/commands/agents/instructions/enable.js +50 -0
  13. package/dist/commands/agents/instructions/move.d.ts +15 -0
  14. package/dist/commands/agents/instructions/move.js +61 -0
  15. package/dist/commands/agents/instructions/preview.d.ts +13 -0
  16. package/dist/commands/agents/instructions/preview.js +59 -0
  17. package/dist/commands/agents/instructions/remove.d.ts +13 -0
  18. package/dist/commands/agents/instructions/remove.js +49 -0
  19. package/dist/commands/agents/instructions/set.d.ts +17 -0
  20. package/dist/commands/agents/instructions/set.js +79 -0
  21. package/dist/commands/agents/instructions/show.d.ts +15 -0
  22. package/dist/commands/agents/instructions/show.js +76 -0
  23. package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
  24. package/dist/commands/agents/instructions/upgrade.js +77 -0
  25. package/dist/commands/agents/show.js +5 -0
  26. package/dist/commands/agents/triggers/policy.js +8 -2
  27. package/dist/commands/agents/update.d.ts +1 -0
  28. package/dist/commands/agents/update.js +8 -2
  29. package/dist/commands/daemon/index.js +2 -0
  30. package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
  31. package/dist/commands/daemon/recover-offline-queue.js +24 -0
  32. package/dist/commands/daemon/start.js +29 -1
  33. package/dist/commands/instruction-blocks/create.d.ts +19 -0
  34. package/dist/commands/instruction-blocks/create.js +94 -0
  35. package/dist/commands/instruction-blocks/delete.d.ts +13 -0
  36. package/dist/commands/instruction-blocks/delete.js +35 -0
  37. package/dist/commands/instruction-blocks/list.d.ts +13 -0
  38. package/dist/commands/instruction-blocks/list.js +71 -0
  39. package/dist/commands/instruction-blocks/show.d.ts +12 -0
  40. package/dist/commands/instruction-blocks/show.js +35 -0
  41. package/dist/commands/instruction-blocks/update.d.ts +23 -0
  42. package/dist/commands/instruction-blocks/update.js +94 -0
  43. package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
  44. package/dist/commands/spaces/auto-executor/preview.js +62 -3
  45. package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
  46. package/dist/commands/spaces/auto-executor/update.js +78 -7
  47. package/dist/commands/spaces/capacity.js +14 -2
  48. package/dist/commands/spaces/update.d.ts +4 -0
  49. package/dist/commands/spaces/update.js +49 -2
  50. package/dist/commands/tasks/complete.d.ts +12 -5
  51. package/dist/commands/tasks/complete.js +50 -28
  52. package/dist/commands/tasks/continue-ready.js +9 -1
  53. package/dist/commands/tasks/create.d.ts +1 -0
  54. package/dist/commands/tasks/create.js +25 -1
  55. package/dist/commands/tasks/deliverable/add.d.ts +23 -0
  56. package/dist/commands/tasks/deliverable/add.js +70 -0
  57. package/dist/commands/tasks/events/append.d.ts +1 -1
  58. package/dist/commands/tasks/events/append.js +4 -4
  59. package/dist/commands/tasks/expectations.d.ts +12 -0
  60. package/dist/commands/tasks/expectations.js +92 -0
  61. package/dist/commands/tasks/judge.d.ts +29 -0
  62. package/dist/commands/tasks/judge.js +68 -0
  63. package/dist/commands/tasks/report.d.ts +1 -1
  64. package/dist/commands/tasks/report.js +3 -3
  65. package/dist/commands/tasks/update.d.ts +1 -0
  66. package/dist/commands/tasks/update.js +34 -4
  67. package/dist/commands/tasks/waive.d.ts +25 -0
  68. package/dist/commands/tasks/waive.js +58 -0
  69. package/dist/lib/agent-config.js +5 -0
  70. package/dist/lib/agent-instructions.d.ts +15 -0
  71. package/dist/lib/agent-instructions.js +55 -0
  72. package/dist/lib/command-hygiene.d.ts +6 -1
  73. package/dist/lib/command-hygiene.js +49 -29
  74. package/dist/lib/exec-runtime-binary.d.ts +13 -0
  75. package/dist/lib/exec-runtime-binary.js +45 -0
  76. package/dist/lib/git-task.d.ts +37 -0
  77. package/dist/lib/git-task.js +151 -0
  78. package/dist/lib/instruction-blocks.d.ts +29 -0
  79. package/dist/lib/instruction-blocks.js +159 -0
  80. package/dist/lib/runtime-start-verification.d.ts +49 -0
  81. package/dist/lib/runtime-start-verification.js +108 -0
  82. package/dist/lib/task-execution.d.ts +18 -2
  83. package/dist/lib/task-execution.js +19 -14
  84. package/dist/lib/task-transcript.js +1 -1
  85. package/dist/lib/tasks.d.ts +50 -0
  86. package/dist/lib/tasks.js +66 -1
  87. package/dist/node_modules/@skrr-ai/data-provider/index.js +2027 -1904
  88. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  89. package/oclif.manifest.json +13706 -11858
  90. package/package.json +4 -1
@@ -10,6 +10,7 @@ exports.isCurrentRuntimeBinary = isCurrentRuntimeBinary;
10
10
  exports.normalizeRuntimeGlobalArgs = normalizeRuntimeGlobalArgs;
11
11
  exports.notInstalledMessage = notInstalledMessage;
12
12
  exports.execRuntimeBinary = execRuntimeBinary;
13
+ exports.execRuntimeBinaryCapture = execRuntimeBinaryCapture;
13
14
  exports.runtimeBinarySupportsSubcommand = runtimeBinarySupportsSubcommand;
14
15
  /**
15
16
  * exec-runtime-binary.ts — locate and forward to the local runtime binary.
@@ -242,6 +243,50 @@ function execRuntimeBinary(args) {
242
243
  });
243
244
  });
244
245
  }
246
+ /**
247
+ * Invoke the installed runtime without inheriting its streams. The regular
248
+ * lifecycle command still owns the terminal; this is only for the wrapper's
249
+ * post-condition probes, where output is evidence rather than user-facing
250
+ * command output.
251
+ */
252
+ function execRuntimeBinaryCapture(args) {
253
+ return new Promise((resolve, reject) => {
254
+ const bin = findRuntimeBinary();
255
+ if (!bin) {
256
+ reject(new Error('runtime-binary-not-installed'));
257
+ return;
258
+ }
259
+ const isWinShim = process.platform === 'win32' && /\.(cmd|bat)$/i.test(bin);
260
+ const child = (0, node_child_process_1.spawn)(bin, normalizeRuntimeGlobalArgs(args), {
261
+ stdio: ['ignore', 'pipe', 'pipe'],
262
+ ...(isWinShim ? { shell: true } : {}),
263
+ });
264
+ let stdout = '';
265
+ let stderr = '';
266
+ // A status response is tiny. Keep a broken runtime from making its wrapper
267
+ // retain an unbounded stream merely to decide whether `start` worked.
268
+ const append = (current, chunk) => {
269
+ const limit = 256 * 1024;
270
+ if (current.length >= limit)
271
+ return current;
272
+ return (current + chunk.toString()).slice(0, limit);
273
+ };
274
+ child.stdout?.on('data', (chunk) => {
275
+ stdout = append(stdout, chunk);
276
+ });
277
+ child.stderr?.on('data', (chunk) => {
278
+ stderr = append(stderr, chunk);
279
+ });
280
+ child.on('error', reject);
281
+ child.on('exit', (code, signal) => {
282
+ resolve({
283
+ code: signal ? 128 + signalNumber(signal) : (code ?? 0),
284
+ stdout,
285
+ stderr,
286
+ });
287
+ });
288
+ });
289
+ }
245
290
  /**
246
291
  * Check an installed daemon's top-level help before forwarding a command that
247
292
  * was added after older daemon releases shipped. A false result is useful to
@@ -2,6 +2,42 @@ export type GitTaskBranch = {
2
2
  branch: string;
3
3
  taskRef: string;
4
4
  };
5
+ export type TaskDeliveryReceipt = {
6
+ kind: 'commit';
7
+ state: 'branch_ready' | 'integrated';
8
+ commitShas: string[];
9
+ headSha: string;
10
+ branch?: string;
11
+ integrationBranch?: string;
12
+ mergeCommit?: string;
13
+ } | {
14
+ kind: 'pull_request';
15
+ state: 'pr_open' | 'integrated';
16
+ prUrl: string;
17
+ branch?: string;
18
+ headSha?: string;
19
+ integrationBranch?: string;
20
+ mergeCommit?: string;
21
+ };
22
+ type CommandResult = {
23
+ status: number | null;
24
+ stdout: string;
25
+ stderr: string;
26
+ };
27
+ type CommandRunner = (command: string, args: string[], cwd: string) => CommandResult;
28
+ export declare function commitArtifacts(deliverables: string): string[];
29
+ /**
30
+ * Build a receipt for commit/PR deliverables before `tasks complete` writes
31
+ * anything. Commit reachability is checked against a freshly fetched remote
32
+ * default branch. A locally dangling commit is first anchored under refs/heads
33
+ * so even a held completion cannot leave it exposed to `git gc`.
34
+ */
35
+ export declare function inspectTaskDeliveryArtifacts(opts: {
36
+ deliverables: string;
37
+ taskRef: string;
38
+ cwd?: string;
39
+ runner?: CommandRunner;
40
+ }): TaskDeliveryReceipt | null;
5
41
  export declare class GitTaskError extends Error {
6
42
  readonly code: 'GIT_REPOSITORY_REQUIRED' | 'TASK_BRANCH_CONTEXT_MISSING' | 'GIT_BRANCH_SWITCH_FAILED';
7
43
  constructor(message: string, code: 'GIT_REPOSITORY_REQUIRED' | 'TASK_BRANCH_CONTEXT_MISSING' | 'GIT_BRANCH_SWITCH_FAILED');
@@ -23,3 +59,4 @@ export declare function checkoutTaskBranch(taskRef: string, cwd?: string): {
23
59
  branch: string;
24
60
  created: boolean;
25
61
  };
62
+ export {};
@@ -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
  /**