@skrr-ai/cli 0.1.13 → 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.
- package/dist/base-command.d.ts +12 -36
- package/dist/base-command.js +96 -11
- package/dist/commands/agents/create.d.ts +1 -0
- package/dist/commands/agents/create.js +6 -1
- package/dist/commands/agents/instructions/add.d.ts +21 -0
- package/dist/commands/agents/instructions/add.js +115 -0
- package/dist/commands/agents/instructions/disable.d.ts +13 -0
- package/dist/commands/agents/instructions/disable.js +50 -0
- package/dist/commands/agents/instructions/edit.d.ts +16 -0
- package/dist/commands/agents/instructions/edit.js +79 -0
- package/dist/commands/agents/instructions/enable.d.ts +13 -0
- package/dist/commands/agents/instructions/enable.js +50 -0
- package/dist/commands/agents/instructions/move.d.ts +15 -0
- package/dist/commands/agents/instructions/move.js +61 -0
- package/dist/commands/agents/instructions/preview.d.ts +13 -0
- package/dist/commands/agents/instructions/preview.js +59 -0
- package/dist/commands/agents/instructions/remove.d.ts +13 -0
- package/dist/commands/agents/instructions/remove.js +49 -0
- package/dist/commands/agents/instructions/set.d.ts +17 -0
- package/dist/commands/agents/instructions/set.js +79 -0
- package/dist/commands/agents/instructions/show.d.ts +15 -0
- package/dist/commands/agents/instructions/show.js +76 -0
- package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
- package/dist/commands/agents/instructions/upgrade.js +77 -0
- package/dist/commands/agents/list.d.ts +10 -0
- package/dist/commands/agents/list.js +16 -1
- package/dist/commands/agents/show.js +5 -0
- package/dist/commands/agents/triggers/policy.js +8 -2
- package/dist/commands/agents/update.d.ts +1 -0
- package/dist/commands/agents/update.js +8 -2
- package/dist/commands/browser.js +5 -5
- package/dist/commands/commitments/create.d.ts +2 -0
- package/dist/commands/commitments/create.js +30 -13
- package/dist/commands/commitments/update.d.ts +2 -0
- package/dist/commands/commitments/update.js +24 -4
- package/dist/commands/daemon/byok.js +5 -5
- package/dist/commands/daemon/index.js +5 -3
- package/dist/commands/daemon/install.js +8 -5
- package/dist/commands/daemon/login.js +5 -5
- package/dist/commands/daemon/logs.js +5 -5
- package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
- package/dist/commands/daemon/recover-offline-queue.js +24 -0
- package/dist/commands/daemon/restart.js +5 -5
- package/dist/commands/daemon/start.js +34 -6
- package/dist/commands/daemon/status.js +5 -5
- package/dist/commands/daemon/stop.js +5 -5
- package/dist/commands/daemon/uninstall.js +5 -5
- package/dist/commands/daemon/unlock.js +5 -5
- package/dist/commands/daemon/usage.js +6 -6
- package/dist/commands/instruction-blocks/create.d.ts +19 -0
- package/dist/commands/instruction-blocks/create.js +94 -0
- package/dist/commands/instruction-blocks/delete.d.ts +13 -0
- package/dist/commands/instruction-blocks/delete.js +35 -0
- package/dist/commands/instruction-blocks/list.d.ts +13 -0
- package/dist/commands/instruction-blocks/list.js +71 -0
- package/dist/commands/instruction-blocks/show.d.ts +12 -0
- package/dist/commands/instruction-blocks/show.js +35 -0
- package/dist/commands/instruction-blocks/update.d.ts +23 -0
- package/dist/commands/instruction-blocks/update.js +94 -0
- package/dist/commands/login.js +7 -2
- package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
- package/dist/commands/spaces/auto-executor/preview.js +62 -3
- package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
- package/dist/commands/spaces/auto-executor/update.js +78 -7
- package/dist/commands/spaces/capacity.js +14 -2
- package/dist/commands/spaces/list.d.ts +13 -0
- package/dist/commands/spaces/list.js +20 -1
- package/dist/commands/spaces/update.d.ts +4 -0
- package/dist/commands/spaces/update.js +49 -2
- package/dist/commands/tasks/complete.d.ts +12 -5
- package/dist/commands/tasks/complete.js +50 -28
- package/dist/commands/tasks/continue-ready.js +9 -1
- package/dist/commands/tasks/create.d.ts +1 -0
- package/dist/commands/tasks/create.js +25 -1
- package/dist/commands/tasks/deliverable/add.d.ts +23 -0
- package/dist/commands/tasks/deliverable/add.js +70 -0
- package/dist/commands/tasks/events/append.d.ts +1 -1
- package/dist/commands/tasks/events/append.js +4 -4
- package/dist/commands/tasks/expectations.d.ts +12 -0
- package/dist/commands/tasks/expectations.js +92 -0
- package/dist/commands/tasks/judge.d.ts +29 -0
- package/dist/commands/tasks/judge.js +68 -0
- package/dist/commands/tasks/list.d.ts +11 -0
- package/dist/commands/tasks/list.js +82 -11
- package/dist/commands/tasks/report.d.ts +1 -1
- package/dist/commands/tasks/report.js +3 -3
- package/dist/commands/tasks/update.d.ts +1 -0
- package/dist/commands/tasks/update.js +34 -4
- package/dist/commands/tasks/waive.d.ts +25 -0
- package/dist/commands/tasks/waive.js +58 -0
- package/dist/commands/wiki/cat.js +2 -1
- package/dist/commands/wiki/mv.js +5 -1
- package/dist/commands/wiki/write.js +5 -1
- package/dist/lib/agent-config.js +5 -0
- package/dist/lib/agent-instructions.d.ts +15 -0
- package/dist/lib/agent-instructions.js +55 -0
- package/dist/lib/api-fetch.d.ts +2 -1
- package/dist/lib/api-fetch.js +9 -2
- package/dist/lib/command-hygiene.d.ts +6 -1
- package/dist/lib/command-hygiene.js +49 -29
- package/dist/lib/commitments.d.ts +44 -0
- package/dist/lib/commitments.js +123 -0
- package/dist/lib/daemon-binding.d.ts +3 -4
- package/dist/lib/daemon-binding.js +11 -10
- package/dist/lib/daemon-handoff-opt-out.d.ts +8 -0
- package/dist/lib/daemon-handoff-opt-out.js +17 -0
- package/dist/lib/daemon-installer.d.ts +1 -1
- package/dist/lib/daemon-installer.js +2 -2
- package/dist/lib/daemon-setup.js +13 -7
- package/dist/lib/daemonHandoff.d.ts +1 -1
- package/dist/lib/daemonHandoff.js +20 -7
- package/dist/lib/delegated-cli.d.ts +0 -9
- package/dist/lib/delegated-cli.js +149 -6
- package/dist/lib/{exec-oversky.d.ts → exec-runtime-binary.d.ts} +28 -4
- package/dist/lib/{exec-oversky.js → exec-runtime-binary.js} +138 -37
- package/dist/lib/git-task.d.ts +37 -0
- package/dist/lib/git-task.js +151 -0
- package/dist/lib/instruction-blocks.d.ts +29 -0
- package/dist/lib/instruction-blocks.js +159 -0
- package/dist/lib/node-adapter.js +6 -0
- package/dist/lib/runtime-start-verification.d.ts +49 -0
- package/dist/lib/runtime-start-verification.js +108 -0
- package/dist/lib/sky-code.d.ts +1 -1
- package/dist/lib/sky-code.js +1 -1
- package/dist/lib/task-execution.d.ts +18 -2
- package/dist/lib/task-execution.js +19 -14
- package/dist/lib/task-transcript.js +1 -1
- package/dist/lib/tasks.d.ts +50 -0
- package/dist/lib/tasks.js +66 -1
- package/dist/lib/usage-discovery.js +1 -1
- package/dist/lib/web-url.d.ts +16 -0
- package/dist/lib/web-url.js +25 -0
- package/dist/node_modules/@skrr-ai/data-provider/index.js +4075 -3905
- package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
- package/oclif.manifest.json +12364 -10488
- package/package.json +4 -1
|
@@ -5,11 +5,21 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../../base-command");
|
|
6
6
|
const prompt_1 = require("../../../lib/prompt");
|
|
7
7
|
const space_output_1 = require("../../../lib/space-output");
|
|
8
|
+
/**
|
|
9
|
+
* `0 = unlimited` is the persisted convention for both task limits. A flag
|
|
10
|
+
* description that omits it leaves the most useful setting undiscoverable —
|
|
11
|
+
* a user who wants "no cap" has no reason to guess that zero means it.
|
|
12
|
+
*/
|
|
13
|
+
const UNLIMITED_HINT = '0 = unlimited';
|
|
8
14
|
class SpacesAutoExecutorUpdate extends base_command_1.BaseCommand {
|
|
9
15
|
static description = 'Update a space auto-executor configuration';
|
|
10
16
|
static examples = [
|
|
11
17
|
'<%= config.bin %> spaces auto-executor update <space-id> --max-active-tasks 6',
|
|
12
18
|
'<%= config.bin %> spaces auto-executor update <space-id> --enabled --interval-ms 600000',
|
|
19
|
+
'<%= config.bin %> spaces auto-executor update <space-id> --mode cadenced',
|
|
20
|
+
'<%= config.bin %> spaces auto-executor update <space-id> --daily-time 09:00 --daily-time 17:00 --timezone America/Vancouver',
|
|
21
|
+
'<%= config.bin %> spaces auto-executor update <space-id> --cron "0 */2 * * *"',
|
|
22
|
+
'<%= config.bin %> spaces auto-executor update <space-id> --max-starts-per-run 0 # unlimited',
|
|
13
23
|
'<%= config.bin %> spaces auto-executor update <space-id> --no-enabled --json',
|
|
14
24
|
];
|
|
15
25
|
static args = {
|
|
@@ -21,9 +31,28 @@ class SpacesAutoExecutorUpdate extends base_command_1.BaseCommand {
|
|
|
21
31
|
description: 'Path to a JSON file (or "-" for stdin) to use as the patch body',
|
|
22
32
|
}),
|
|
23
33
|
enabled: core_1.Flags.boolean({ description: 'Enable or disable auto-execution', allowNo: true }),
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
mode: core_1.Flags.string({
|
|
35
|
+
description: 'continuous = a finished task promptly wakes the Leader to refill capacity; ' +
|
|
36
|
+
'cadenced = only run on the schedule, leaving a review window between batches',
|
|
37
|
+
options: [...data_provider_1.SPACE_AUTO_EXECUTION_MODES],
|
|
38
|
+
}),
|
|
39
|
+
'interval-ms': core_1.Flags.integer({
|
|
40
|
+
description: `Run every N milliseconds (min ${data_provider_1.SPACE_AUTO_EXECUTION_LIMITS.intervalMs.min}, max ${data_provider_1.SPACE_AUTO_EXECUTION_LIMITS.intervalMs.max})`,
|
|
41
|
+
}),
|
|
42
|
+
cron: core_1.Flags.string({ description: 'Run on a cron expression, e.g. "0 */2 * * *"' }),
|
|
43
|
+
'daily-time': core_1.Flags.string({
|
|
44
|
+
description: 'Run daily at HH:MM (24h). Repeatable for several times a day.',
|
|
45
|
+
multiple: true,
|
|
46
|
+
}),
|
|
47
|
+
timezone: core_1.Flags.string({
|
|
48
|
+
description: 'IANA timezone for --cron / --daily-time (default UTC)',
|
|
49
|
+
}),
|
|
50
|
+
'max-starts-per-run': core_1.Flags.integer({
|
|
51
|
+
description: `Max tasks to start per auto run (${UNLIMITED_HINT}, max ${data_provider_1.SPACE_AUTO_EXECUTION_LIMITS.maxStartsPerRun.max})`,
|
|
52
|
+
}),
|
|
53
|
+
'max-active-tasks': core_1.Flags.integer({
|
|
54
|
+
description: `Max concurrently active task executions in this space (${UNLIMITED_HINT}, max ${data_provider_1.SPACE_AUTO_EXECUTION_LIMITS.maxActiveTasks.max})`,
|
|
55
|
+
}),
|
|
27
56
|
};
|
|
28
57
|
async run() {
|
|
29
58
|
this.requireAuth();
|
|
@@ -35,9 +64,37 @@ class SpacesAutoExecutorUpdate extends base_command_1.BaseCommand {
|
|
|
35
64
|
catch (err) {
|
|
36
65
|
this.error(err.message, { exit: 1 });
|
|
37
66
|
}
|
|
67
|
+
// The API accepts exactly one recurrence shape. Rejecting here — by name,
|
|
68
|
+
// with the alternatives listed — beats a 400 that echoes the Zod refine.
|
|
69
|
+
const scheduleFlags = [
|
|
70
|
+
flags.cron !== undefined ? '--cron' : null,
|
|
71
|
+
flags['daily-time']?.length ? '--daily-time' : null,
|
|
72
|
+
flags['interval-ms'] !== undefined ? '--interval-ms' : null,
|
|
73
|
+
].filter(Boolean);
|
|
74
|
+
if (scheduleFlags.length > 1) {
|
|
75
|
+
this.error(`Pick one schedule shape: ${scheduleFlags.join(', ')} cannot be combined.`, {
|
|
76
|
+
exit: 1,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const timezone = flags.timezone ? { timezone: flags.timezone } : {};
|
|
80
|
+
let schedule;
|
|
81
|
+
if (flags.cron !== undefined) {
|
|
82
|
+
schedule = { cronExpression: flags.cron, ...timezone };
|
|
83
|
+
}
|
|
84
|
+
else if (flags['daily-time']?.length) {
|
|
85
|
+
schedule = { times: flags['daily-time'], ...timezone };
|
|
86
|
+
}
|
|
87
|
+
if (flags.timezone && !schedule) {
|
|
88
|
+
this.error('--timezone applies to --cron or --daily-time; pass one of them too.', {
|
|
89
|
+
exit: 1,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
38
92
|
const payload = (0, prompt_1.mergeDefined)(body, {
|
|
39
93
|
enabled: flags.enabled,
|
|
40
|
-
|
|
94
|
+
mode: flags.mode,
|
|
95
|
+
// `intervalMs` and `schedule` are mutually exclusive at the API.
|
|
96
|
+
intervalMs: schedule ? undefined : flags['interval-ms'],
|
|
97
|
+
schedule,
|
|
41
98
|
maxStartsPerRun: flags['max-starts-per-run'],
|
|
42
99
|
maxActiveTasks: flags['max-active-tasks'],
|
|
43
100
|
});
|
|
@@ -59,11 +116,25 @@ class SpacesAutoExecutorUpdate extends base_command_1.BaseCommand {
|
|
|
59
116
|
return;
|
|
60
117
|
}
|
|
61
118
|
const space = response?.space ?? {};
|
|
119
|
+
// A stored `0` means unlimited. Printing the raw number told the user the
|
|
120
|
+
// opposite of what the setting does.
|
|
121
|
+
const limit = (value) => typeof value === 'number' ? (0, data_provider_1.formatLimit)(value === 0 ? null : value) : '-';
|
|
62
122
|
this.log(`Updated auto-executor for ${space.id ?? args.id}`);
|
|
63
123
|
this.log(`Enabled: ${space.autoExecuteEnabled ? 'yes' : 'no'}`);
|
|
64
|
-
this.log(`
|
|
65
|
-
this.log(`
|
|
66
|
-
this.log(`Max
|
|
124
|
+
this.log(`Mode: ${space.autoExecuteMode ?? '-'}`);
|
|
125
|
+
this.log(`Interval: ${formatIntervalMs(space.autoExecuteIntervalMs)}`);
|
|
126
|
+
this.log(`Max starts/run: ${limit(space.autoExecuteMaxStartsPerRun)}`);
|
|
127
|
+
this.log(`Max active tasks: ${limit(space.autoExecuteMaxActiveTasks)}`);
|
|
67
128
|
}
|
|
68
129
|
}
|
|
69
130
|
exports.default = SpacesAutoExecutorUpdate;
|
|
131
|
+
/** Milliseconds are how the field is stored, not how anyone reads a cadence. */
|
|
132
|
+
function formatIntervalMs(value) {
|
|
133
|
+
if (typeof value !== 'number' || !Number.isFinite(value))
|
|
134
|
+
return '-';
|
|
135
|
+
const minutes = value / 60_000;
|
|
136
|
+
if (minutes < 60)
|
|
137
|
+
return `${minutes % 1 === 0 ? minutes : minutes.toFixed(1)}m (${value}ms)`;
|
|
138
|
+
const hours = minutes / 60;
|
|
139
|
+
return `${hours % 1 === 0 ? hours : hours.toFixed(1)}h (${value}ms)`;
|
|
140
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
4
5
|
const base_command_1 = require("../../base-command");
|
|
5
6
|
const format_1 = require("../../lib/format");
|
|
6
7
|
const task_execution_1 = require("../../lib/task-execution");
|
|
@@ -33,11 +34,22 @@ class SpacesCapacity extends base_command_1.BaseCommand {
|
|
|
33
34
|
return;
|
|
34
35
|
}
|
|
35
36
|
const space = capacity.space;
|
|
37
|
+
// `maxActiveTasks` / `availableSlots` are null when the space has no cap.
|
|
38
|
+
// Interpolating them directly is what produced `0/null active` (OSK-7087);
|
|
39
|
+
// the shared formatters in @skrr-ai/data-provider are the one place that
|
|
40
|
+
// decides how an absent ceiling reads, for the web and the CLI alike.
|
|
36
41
|
this.log(`Space: ${space.title ?? args.id}`);
|
|
37
42
|
this.log(`ID: ${space.id ?? args.id}`);
|
|
38
43
|
this.log(`Auto exec: ${space.autoExecuteEnabled ? 'enabled' : 'disabled'}`);
|
|
39
|
-
this.log(`Capacity: ${capacity.activeCount
|
|
40
|
-
|
|
44
|
+
this.log(`Capacity: ${(0, data_provider_1.formatRunningCapacity)(capacity.activeCount, capacity.maxActiveTasks)} active` +
|
|
45
|
+
(capacity.maxActiveTasks === null ? ' (no concurrency cap)' : ''));
|
|
46
|
+
this.log(`Available: ${(0, data_provider_1.formatLimit)(capacity.availableSlots)}`);
|
|
47
|
+
if (capacity.budget) {
|
|
48
|
+
const { consumedStarts, maxStarts, remaining, unlimited } = capacity.budget;
|
|
49
|
+
this.log(unlimited
|
|
50
|
+
? `Starts: ${consumedStarts} used this round (no per-run budget)`
|
|
51
|
+
: `Starts: ${consumedStarts}/${maxStarts} used this round, ${remaining} left`);
|
|
52
|
+
}
|
|
41
53
|
this.log(`Sampled: ${capacity.sampledTaskCount} tasks`);
|
|
42
54
|
if (capacity.activeTasks.length > 0) {
|
|
43
55
|
this.log('');
|
|
@@ -17,6 +17,19 @@ import { BaseCommand } from '../../base-command';
|
|
|
17
17
|
* convention `describeResourceNotFound` follows in `base-command`.
|
|
18
18
|
*/
|
|
19
19
|
export declare function noSpacesMatchedMessage(q: string, bin?: string): string;
|
|
20
|
+
/**
|
|
21
|
+
* What the unfiltered zero-row case is allowed to say.
|
|
22
|
+
*
|
|
23
|
+
* A bare `No spaces.` on a fresh account is technically true and operationally
|
|
24
|
+
* useless: it names no next step, so the user reading `GETTING_STARTED.md`
|
|
25
|
+
* step 3 hits three of these in a row and concludes the product is inert.
|
|
26
|
+
* `commitments/list.ts` learned this the hard way and left the reasoning in a
|
|
27
|
+
* comment; the fix is to follow the same pattern here — one line naming the
|
|
28
|
+
* command that fixes it.
|
|
29
|
+
*
|
|
30
|
+
* Pure and exported so the wording is testable without an oclif runtime.
|
|
31
|
+
*/
|
|
32
|
+
export declare function noSpacesEmptyMessage(bin?: string): string;
|
|
20
33
|
export default class SpacesList extends BaseCommand {
|
|
21
34
|
static description: string;
|
|
22
35
|
static examples: string[];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.noSpacesMatchedMessage = noSpacesMatchedMessage;
|
|
4
|
+
exports.noSpacesEmptyMessage = noSpacesEmptyMessage;
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
6
7
|
const base_command_1 = require("../../base-command");
|
|
@@ -29,6 +30,22 @@ function noSpacesMatchedMessage(q, bin = 'skrr') {
|
|
|
29
30
|
'not a tokenized search, so word order matters.\n' +
|
|
30
31
|
` → retry with one distinctive word, or list them all with \`${bin} spaces list\`.`);
|
|
31
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* What the unfiltered zero-row case is allowed to say.
|
|
35
|
+
*
|
|
36
|
+
* A bare `No spaces.` on a fresh account is technically true and operationally
|
|
37
|
+
* useless: it names no next step, so the user reading `GETTING_STARTED.md`
|
|
38
|
+
* step 3 hits three of these in a row and concludes the product is inert.
|
|
39
|
+
* `commitments/list.ts` learned this the hard way and left the reasoning in a
|
|
40
|
+
* comment; the fix is to follow the same pattern here — one line naming the
|
|
41
|
+
* command that fixes it.
|
|
42
|
+
*
|
|
43
|
+
* Pure and exported so the wording is testable without an oclif runtime.
|
|
44
|
+
*/
|
|
45
|
+
function noSpacesEmptyMessage(bin = 'skrr') {
|
|
46
|
+
return ('No spaces yet.\n' +
|
|
47
|
+
` → Create your first one: \`${bin} spaces create --title "My workspace"\``);
|
|
48
|
+
}
|
|
32
49
|
class SpacesList extends base_command_1.BaseCommand {
|
|
33
50
|
static description = 'List spaces';
|
|
34
51
|
static examples = [
|
|
@@ -95,7 +112,9 @@ class SpacesList extends base_command_1.BaseCommand {
|
|
|
95
112
|
const r = response;
|
|
96
113
|
const items = r?.data ?? [];
|
|
97
114
|
if (items.length === 0) {
|
|
98
|
-
this.log(flags.q
|
|
115
|
+
this.log(flags.q
|
|
116
|
+
? noSpacesMatchedMessage(flags.q, this.config.bin)
|
|
117
|
+
: noSpacesEmptyMessage(this.config.bin));
|
|
99
118
|
return;
|
|
100
119
|
}
|
|
101
120
|
const rows = items.map((s) => ({
|
|
@@ -20,6 +20,10 @@ export default class SpacesUpdate extends BaseCommand {
|
|
|
20
20
|
'estimate-migration': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
21
21
|
'default-estimate': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
22
22
|
'use-default-estimate': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
23
|
+
'default-expect': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
|
+
'delivery-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
25
|
+
'integration-branch': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
26
|
+
'auto-merge-tier': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
23
27
|
'worktree-isolation': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
24
28
|
};
|
|
25
29
|
run(): Promise<void>;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const core_1 = require("@oclif/core");
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const tasks_1 = require("../../lib/tasks");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const web_url_1 = require("../../lib/web-url");
|
|
7
8
|
const prompt_1 = require("../../lib/prompt");
|
|
@@ -70,6 +71,29 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
70
71
|
allowNo: true,
|
|
71
72
|
description: 'Whether rollups treat an unestimated task as --default-estimate',
|
|
72
73
|
}),
|
|
74
|
+
'default-expect': core_1.Flags.string({
|
|
75
|
+
multiple: true,
|
|
76
|
+
description: tasks_1.EXPECT_FLAG_HELP +
|
|
77
|
+
' This is the space DEFAULT, and it is where the contract actually gets set: nobody ' +
|
|
78
|
+
'declares one per task, but a space default is configured once and then holds. A ' +
|
|
79
|
+
'per-task --expect wins. Merged into deliveryPolicy without clobbering its other fields.',
|
|
80
|
+
}),
|
|
81
|
+
'delivery-mode': core_1.Flags.string({
|
|
82
|
+
options: ['none', 'branch', 'pr', 'pr_automerge', 'direct'],
|
|
83
|
+
description: "How this space's coding tasks integrate their work. 'pr' opens a pull request; " +
|
|
84
|
+
"'direct' pushes to the integration branch. Note that auto-merging modes are gated " +
|
|
85
|
+
'by --auto-merge-tier: naming one here does not by itself authorise landing code.',
|
|
86
|
+
}),
|
|
87
|
+
'integration-branch': core_1.Flags.string({
|
|
88
|
+
description: "The branch this space's coding tasks integrate into, e.g. main or dev. Also the " +
|
|
89
|
+
'default target for a code expectation that names none.',
|
|
90
|
+
}),
|
|
91
|
+
'auto-merge-tier': core_1.Flags.string({
|
|
92
|
+
options: ['off', 'checks_only', 'trusted'],
|
|
93
|
+
description: 'How far a task may go without a person. "off" (default) downgrades every auto-merging ' +
|
|
94
|
+
'mode to a human-approved PR; "checks_only" honours pr_automerge; "trusted" also honours ' +
|
|
95
|
+
"direct. This is the space owner's sanction and it outranks any per-task override.",
|
|
96
|
+
}),
|
|
73
97
|
'worktree-isolation': core_1.Flags.string({
|
|
74
98
|
options: ['shared', 'ephemeral'],
|
|
75
99
|
description: "Space default for how this space's autonomous task runs use git worktrees. " +
|
|
@@ -105,8 +129,24 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
105
129
|
// which the API replaces wholesale on update. Merge into the policy the user
|
|
106
130
|
// supplied via --from-json, else the space's current policy, so setting just
|
|
107
131
|
// the isolation default never clobbers mode / integrationBranch / trust tier.
|
|
132
|
+
// Parse before any write: a bad spec must fail here, not reach the server
|
|
133
|
+
// as a silently-dropped field. A space default nobody is enforcing is worse
|
|
134
|
+
// than none, because every task under it then shows a contract that isn't real.
|
|
135
|
+
let defaultExpectations;
|
|
136
|
+
if (flags['default-expect'] !== undefined && flags['default-expect'].length > 0) {
|
|
137
|
+
const parsed = (0, tasks_1.parseExpectations)(flags['default-expect']);
|
|
138
|
+
if (!parsed.ok) {
|
|
139
|
+
this.error(parsed.error, { exit: 1 });
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
defaultExpectations = parsed.value;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
108
145
|
const isolation = flags['worktree-isolation'];
|
|
109
|
-
|
|
146
|
+
const deliveryMode = flags['delivery-mode'];
|
|
147
|
+
const integrationBranch = flags['integration-branch'];
|
|
148
|
+
const autoMergeTier = flags['auto-merge-tier'];
|
|
149
|
+
if (isolation || defaultExpectations || deliveryMode || integrationBranch || autoMergeTier) {
|
|
110
150
|
let basePolicy = {};
|
|
111
151
|
if (payload.deliveryPolicy && typeof payload.deliveryPolicy === 'object') {
|
|
112
152
|
basePolicy = { ...payload.deliveryPolicy };
|
|
@@ -123,7 +163,14 @@ class SpacesUpdate extends base_command_1.BaseCommand {
|
|
|
123
163
|
// Fall through: send a policy carrying only the isolation default.
|
|
124
164
|
}
|
|
125
165
|
}
|
|
126
|
-
payload.deliveryPolicy = {
|
|
166
|
+
payload.deliveryPolicy = {
|
|
167
|
+
...basePolicy,
|
|
168
|
+
...(isolation ? { executionIsolation: isolation } : {}),
|
|
169
|
+
...(deliveryMode ? { mode: deliveryMode } : {}),
|
|
170
|
+
...(integrationBranch ? { integrationBranch } : {}),
|
|
171
|
+
...(autoMergeTier ? { autoMergeTrustTier: autoMergeTier } : {}),
|
|
172
|
+
...(defaultExpectations ? { defaultExpectations } : {}),
|
|
173
|
+
};
|
|
127
174
|
}
|
|
128
175
|
// Goal links are NOT part of the PATCH body — the server rejects a PATCH
|
|
129
176
|
// /api/spaces/:id carrying `goalIds` with `space_relations_atomic_only` and
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { dataService } from '@skrr-ai/data-provider';
|
|
2
2
|
import type { TaskReport } from '@skrr-ai/data-provider';
|
|
3
3
|
import { BaseCommand } from '../../base-command';
|
|
4
|
+
import { type TaskDeliveryReceipt } from '../../lib/git-task';
|
|
4
5
|
type CompletionStatus = 'completed' | 'failed';
|
|
5
6
|
type TaskCommentResult = Awaited<ReturnType<typeof dataService.addTaskComment>>;
|
|
6
7
|
type MovedTaskResult = Awaited<ReturnType<typeof dataService.moveTask>>;
|
|
@@ -9,15 +10,18 @@ export interface CompleteEndpointResponse {
|
|
|
9
10
|
task: MovedTaskResult | null;
|
|
10
11
|
comment: TaskCommentResult | null;
|
|
11
12
|
commentFailed?: boolean;
|
|
13
|
+
deliveryHeld?: boolean;
|
|
14
|
+
deliveryReason?: string;
|
|
12
15
|
}
|
|
13
16
|
export interface CompleteTransport {
|
|
14
17
|
/** POST /api/tasks/:taskId/complete — atomic move + TASK_OUTPUT comment. */
|
|
15
18
|
postComplete: (taskId: string, payload: {
|
|
16
19
|
status: CompletionStatus;
|
|
17
20
|
summary: string;
|
|
18
|
-
|
|
21
|
+
deliverables: string;
|
|
19
22
|
verdict: string;
|
|
20
23
|
report?: TaskReport;
|
|
24
|
+
deliveryReceipt?: TaskDeliveryReceipt;
|
|
21
25
|
}) => Promise<CompleteEndpointResponse>;
|
|
22
26
|
/** Legacy step 1 — POST /api/tasks/:taskId/comments. */
|
|
23
27
|
addComment: (taskId: string, body: string) => Promise<TaskCommentResult>;
|
|
@@ -31,6 +35,8 @@ export interface DurableCompletionResult {
|
|
|
31
35
|
movedTask: MovedTaskResult | null;
|
|
32
36
|
moved: boolean;
|
|
33
37
|
commentFailed: boolean;
|
|
38
|
+
deliveryHeld?: boolean;
|
|
39
|
+
deliveryReason?: string;
|
|
34
40
|
/** true when the atomic /complete endpoint handled the finalization. */
|
|
35
41
|
usedAtomicEndpoint: boolean;
|
|
36
42
|
}
|
|
@@ -60,7 +66,7 @@ export declare function resolveCompletionTarget(taskRef: string, lookup: (taskRe
|
|
|
60
66
|
* The repo contract calls this "the only terminal closeout", and a task has one
|
|
61
67
|
* canonical outcome — but a second run used to succeed silently, post another
|
|
62
68
|
* TASK_OUTPUT block, and leave `tasks output` rendering the throwaway one as
|
|
63
|
-
* authoritative. The real summary and its
|
|
69
|
+
* authoritative. The real summary and its deliverables link were still on the task
|
|
64
70
|
* but no longer canonical, which is the worst of the available failures: no
|
|
65
71
|
* error, no warning, exit 0, and a record that now says something different.
|
|
66
72
|
*
|
|
@@ -120,9 +126,10 @@ export declare function completeTaskDurably(opts: {
|
|
|
120
126
|
status: CompletionStatus;
|
|
121
127
|
body: string;
|
|
122
128
|
summary: string;
|
|
123
|
-
|
|
129
|
+
deliverables: string;
|
|
124
130
|
verdict: string;
|
|
125
131
|
report?: TaskReport;
|
|
132
|
+
deliveryReceipt?: TaskDeliveryReceipt;
|
|
126
133
|
noMove: boolean;
|
|
127
134
|
transport: CompleteTransport;
|
|
128
135
|
}): Promise<DurableCompletionResult>;
|
|
@@ -139,8 +146,8 @@ export default class TasksComplete extends BaseCommand {
|
|
|
139
146
|
amend: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
140
147
|
status: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
141
148
|
summary: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
142
|
-
|
|
143
|
-
|
|
149
|
+
deliverables: import("@oclif/core/lib/interfaces").OptionFlag<string[], import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
150
|
+
deliverable: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
144
151
|
verdict: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
145
152
|
outcome: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
146
153
|
finding: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
@@ -21,6 +21,7 @@ const task_instruction_offer_1 = require("../../lib/task-instruction-offer");
|
|
|
21
21
|
const task_transcript_1 = require("../../lib/task-transcript");
|
|
22
22
|
const web_url_1 = require("../../lib/web-url");
|
|
23
23
|
const tasks_1 = require("../../lib/tasks");
|
|
24
|
+
const git_task_1 = require("../../lib/git-task");
|
|
24
25
|
/**
|
|
25
26
|
* Mutation endpoints require the task's canonical UUID even though the CLI
|
|
26
27
|
* accepts human-readable refs and titles. Resolve the display reference before
|
|
@@ -55,7 +56,7 @@ async function resolveCompletionTarget(taskRef, lookup) {
|
|
|
55
56
|
* The repo contract calls this "the only terminal closeout", and a task has one
|
|
56
57
|
* canonical outcome — but a second run used to succeed silently, post another
|
|
57
58
|
* TASK_OUTPUT block, and leave `tasks output` rendering the throwaway one as
|
|
58
|
-
* authoritative. The real summary and its
|
|
59
|
+
* authoritative. The real summary and its deliverables link were still on the task
|
|
59
60
|
* but no longer canonical, which is the worst of the available failures: no
|
|
60
61
|
* error, no warning, exit 0, and a record that now says something different.
|
|
61
62
|
*
|
|
@@ -128,7 +129,7 @@ exports.TaskCompletionMismatchError = TaskCompletionMismatchError;
|
|
|
128
129
|
* error handling.
|
|
129
130
|
*/
|
|
130
131
|
async function completeTaskDurably(opts) {
|
|
131
|
-
const { taskId, status, body, summary,
|
|
132
|
+
const { taskId, status, body, summary, deliverables, verdict, report, deliveryReceipt, noMove, transport, } = opts;
|
|
132
133
|
if (noMove) {
|
|
133
134
|
const comment = await transport.addComment(taskId, body);
|
|
134
135
|
return {
|
|
@@ -143,9 +144,10 @@ async function completeTaskDurably(opts) {
|
|
|
143
144
|
const response = await transport.postComplete(taskId, {
|
|
144
145
|
status,
|
|
145
146
|
summary,
|
|
146
|
-
|
|
147
|
+
deliverables,
|
|
147
148
|
verdict,
|
|
148
149
|
...(report ? { report } : {}),
|
|
150
|
+
...(deliveryReceipt ? { deliveryReceipt } : {}),
|
|
149
151
|
});
|
|
150
152
|
// Reconcile before reporting. On the session-scoped path the server owns
|
|
151
153
|
// the task selection, so a divergence here means the task we were asked to
|
|
@@ -160,6 +162,8 @@ async function completeTaskDurably(opts) {
|
|
|
160
162
|
movedTask: response.task ?? null,
|
|
161
163
|
moved: true,
|
|
162
164
|
commentFailed: response.commentFailed === true,
|
|
165
|
+
...(response.deliveryHeld ? { deliveryHeld: true } : {}),
|
|
166
|
+
...(response.deliveryReason ? { deliveryReason: response.deliveryReason } : {}),
|
|
163
167
|
usedAtomicEndpoint: true,
|
|
164
168
|
};
|
|
165
169
|
}
|
|
@@ -174,7 +178,13 @@ async function completeTaskDurably(opts) {
|
|
|
174
178
|
// behavior (comment first, then move).
|
|
175
179
|
const comment = await transport.addComment(taskId, body);
|
|
176
180
|
const movedTask = await transport.moveTask(taskId, status === 'completed' ? 'done' : 'failed');
|
|
177
|
-
return {
|
|
181
|
+
return {
|
|
182
|
+
comment,
|
|
183
|
+
movedTask,
|
|
184
|
+
moved: true,
|
|
185
|
+
commentFailed: false,
|
|
186
|
+
usedAtomicEndpoint: false,
|
|
187
|
+
};
|
|
178
188
|
}
|
|
179
189
|
class TasksComplete extends base_command_1.BaseCommand {
|
|
180
190
|
static description = 'Post a canonical TASK_OUTPUT comment and finish a task';
|
|
@@ -199,7 +209,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
199
209
|
description: 'Task ID or identifier; overrides daemon runtime context',
|
|
200
210
|
}),
|
|
201
211
|
'from-json': core_1.Flags.string({
|
|
202
|
-
description: 'Path to JSON with taskId, status, summary,
|
|
212
|
+
description: 'Path to JSON with taskId, status, summary, deliverables, and verdict; "-" for stdin',
|
|
203
213
|
}),
|
|
204
214
|
amend: core_1.Flags.boolean({
|
|
205
215
|
description: 'Supersede the canonical outcome of an already-completed task. Without it, a differing report on a closed task is refused',
|
|
@@ -210,13 +220,13 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
210
220
|
default: 'completed',
|
|
211
221
|
}),
|
|
212
222
|
summary: core_1.Flags.string({ description: 'Completion summary' }),
|
|
213
|
-
|
|
214
|
-
description: '
|
|
215
|
-
`The whole TASK_OUTPUT block (status + summary +
|
|
223
|
+
deliverables: core_1.Flags.string({
|
|
224
|
+
description: 'Deliverables produced, or "none". Repeatable, and interchangeable with --deliverable. ' +
|
|
225
|
+
`The whole TASK_OUTPUT block (status + summary + deliverables + verdict) must fit in ${data_provider_1.TASK_OUTPUT_BODY_MAX} ` +
|
|
216
226
|
'characters — link to commits, PRs, or file paths rather than pasting content.',
|
|
217
|
-
// Repeatable, even though `--
|
|
218
|
-
// one letter apart and both read as "the
|
|
219
|
-
// one gets typed — and oclif answered that with "Flag --
|
|
227
|
+
// Repeatable, even though `--deliverable` already is. The two spellings sit
|
|
228
|
+
// one letter apart and both read as "the deliverables go here", so the wrong
|
|
229
|
+
// one gets typed — and oclif answered that with "Flag --deliverables can only
|
|
220
230
|
// be specified once", which names the flag you used and not the sibling
|
|
221
231
|
// that does what you wanted. Dogfooded (OSK-266) by losing a completion
|
|
222
232
|
// call to exactly that, then re-running with the two references pre-joined
|
|
@@ -224,9 +234,9 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
224
234
|
multiple: true,
|
|
225
235
|
default: ['none'],
|
|
226
236
|
}),
|
|
227
|
-
|
|
237
|
+
deliverable: core_1.Flags.string({
|
|
228
238
|
description: 'A single artifact reference (repeatable); joined with commas. Prefer this ' +
|
|
229
|
-
'over pre-joining into --
|
|
239
|
+
'over pre-joining into --deliverables.',
|
|
230
240
|
multiple: true,
|
|
231
241
|
}),
|
|
232
242
|
verdict: core_1.Flags.string({ description: 'Final verdict', default: 'approved' }),
|
|
@@ -271,13 +281,13 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
271
281
|
summary: flags.summary,
|
|
272
282
|
// Both spellings, pooled, then normalized to the one-line form the server
|
|
273
283
|
// stores. Pooling rather than letting one win: someone who passes both has
|
|
274
|
-
// named
|
|
284
|
+
// named deliverables in each, and silently dropping half of them is worse
|
|
275
285
|
// than either flag being unavailable.
|
|
276
|
-
|
|
277
|
-
...(flags.
|
|
286
|
+
deliverables: (0, data_provider_1.normalizeTaskOutputDeliverables)([
|
|
287
|
+
...(flags.deliverable ?? []),
|
|
278
288
|
// The literal default is dropped when it is only the default; a user
|
|
279
|
-
// who typed `--
|
|
280
|
-
...(flags.
|
|
289
|
+
// who typed `--deliverables none` alongside a real one meant the real one.
|
|
290
|
+
...(flags.deliverables ?? []).filter((value) => value !== 'none'),
|
|
281
291
|
]) || 'none',
|
|
282
292
|
verdict: flags.verdict,
|
|
283
293
|
});
|
|
@@ -312,14 +322,14 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
312
322
|
}
|
|
313
323
|
let status;
|
|
314
324
|
let summary;
|
|
315
|
-
let
|
|
325
|
+
let deliverables;
|
|
316
326
|
let verdict;
|
|
317
327
|
let report;
|
|
318
328
|
try {
|
|
319
329
|
status = normalizeStatus(input.status);
|
|
320
330
|
summary = requireOutputField(input.summary, 'summary');
|
|
321
|
-
|
|
322
|
-
optionalOutputField((0, data_provider_1.
|
|
331
|
+
deliverables =
|
|
332
|
+
optionalOutputField((0, data_provider_1.normalizeTaskOutputDeliverables)(input.deliverables)) || 'none';
|
|
323
333
|
verdict =
|
|
324
334
|
optionalOutputField(input.verdict) || (status === 'completed' ? 'approved' : 'failed');
|
|
325
335
|
const jsonReport = input.report && typeof input.report === 'object'
|
|
@@ -360,7 +370,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
360
370
|
// budget the server enforces. Discovering an undocumented cap by HTTP 400
|
|
361
371
|
// after a run has already finished is the failure being fixed; a caller must
|
|
362
372
|
// be able to see the limit without provoking it.
|
|
363
|
-
const budget = (0, data_provider_1.measureTaskOutputBudget)({ status, summary,
|
|
373
|
+
const budget = (0, data_provider_1.measureTaskOutputBudget)({ status, summary, deliverables, verdict });
|
|
364
374
|
if (!budget.fits) {
|
|
365
375
|
this.failWithCliError({
|
|
366
376
|
message: (0, data_provider_1.describeTaskOutputOverflow)(budget),
|
|
@@ -375,14 +385,21 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
375
385
|
},
|
|
376
386
|
});
|
|
377
387
|
}
|
|
378
|
-
const body = formatTaskOutput({ status, summary,
|
|
388
|
+
const body = formatTaskOutput({ status, summary, deliverables, verdict });
|
|
389
|
+
// Artifact text such as a commit SHA is a delivery claim. The local probe
|
|
390
|
+
// both verifies remote-default ancestry and anchors a commit that has no
|
|
391
|
+
// local branch before the completion request can release this run.
|
|
392
|
+
const deliveryReceipt = status === 'completed' && !flags['no-move']
|
|
393
|
+
? ((0, git_task_1.inspectTaskDeliveryArtifacts)({ deliverables, taskRef: taskIdentity.id }) ?? undefined)
|
|
394
|
+
: undefined;
|
|
379
395
|
// Computed once, OUTSIDE the try, so the original attempt and the queued
|
|
380
396
|
// replay carry the same key. Must match the payload the outbox stores.
|
|
381
397
|
const completionPayload = {
|
|
382
398
|
summary,
|
|
383
|
-
|
|
399
|
+
deliverables,
|
|
384
400
|
verdict,
|
|
385
401
|
...(report ? { report } : {}),
|
|
402
|
+
...(deliveryReceipt ? { deliveryReceipt } : {}),
|
|
386
403
|
noMove: !!flags['no-move'],
|
|
387
404
|
};
|
|
388
405
|
const completionIdempotencyKey = (0, outbox_1.idempotencyKeyFor)('task-complete', taskIdentity.id, JSON.stringify(completionPayload));
|
|
@@ -456,7 +473,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
456
473
|
if (decision === 'refuse') {
|
|
457
474
|
this.failWithCliError({
|
|
458
475
|
message: `Task ${completionTaskId} is already ${priorStatus}, and this report differs from its canonical ` +
|
|
459
|
-
`outcome — completing again would silently replace it (
|
|
476
|
+
`outcome — completing again would silently replace it (deliverables included).\n` +
|
|
460
477
|
` → re-run with \`--amend\` to supersede it deliberately, or read what is there now with ` +
|
|
461
478
|
`\`${this.config.bin} tasks output ${completionTaskId}\`.`,
|
|
462
479
|
code: 'TASK_ALREADY_COMPLETED',
|
|
@@ -473,9 +490,10 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
473
490
|
status,
|
|
474
491
|
body,
|
|
475
492
|
summary,
|
|
476
|
-
|
|
493
|
+
deliverables,
|
|
477
494
|
verdict,
|
|
478
495
|
report,
|
|
496
|
+
deliveryReceipt,
|
|
479
497
|
noMove: !!flags['no-move'],
|
|
480
498
|
transport: {
|
|
481
499
|
postComplete: (taskId, payload) => (0, api_fetch_1.apiFetch)(
|
|
@@ -496,7 +514,7 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
496
514
|
});
|
|
497
515
|
}
|
|
498
516
|
catch (err) {
|
|
499
|
-
// The completion summary/
|
|
517
|
+
// The completion summary/deliverables/verdict are authored content — the
|
|
500
518
|
// record of what the agent actually did. Losing that to a 503 costs the
|
|
501
519
|
// reasoning, not just a retry, so an OUTAGE queues it durably (OSK-2806)
|
|
502
520
|
// while every 4xx still fails immediately through handleApiError.
|
|
@@ -531,11 +549,12 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
531
549
|
taskOutput: {
|
|
532
550
|
status,
|
|
533
551
|
summary,
|
|
534
|
-
|
|
552
|
+
deliverables,
|
|
535
553
|
verdict,
|
|
536
554
|
...(report ? { report } : {}),
|
|
537
555
|
body,
|
|
538
556
|
},
|
|
557
|
+
...(result.deliveryHeld ? { deliveryHeld: true, deliveryReason: result.deliveryReason } : {}),
|
|
539
558
|
};
|
|
540
559
|
if (flags.json) {
|
|
541
560
|
const url = movedTask
|
|
@@ -558,6 +577,9 @@ class TasksComplete extends base_command_1.BaseCommand {
|
|
|
558
577
|
? `Task ${completedTaskId} was already ${newStatus}; the outcome was amended.`
|
|
559
578
|
: `Moved task ${completedTaskId} to ${newStatus}`);
|
|
560
579
|
}
|
|
580
|
+
if (result.deliveryHeld) {
|
|
581
|
+
this.warn('Delivery is not verified on the default branch; the task was held at in_review instead of done.');
|
|
582
|
+
}
|
|
561
583
|
if (report) {
|
|
562
584
|
const url = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, movedTask ?? { id: completedTaskId });
|
|
563
585
|
if (url)
|
|
@@ -70,7 +70,15 @@ class TasksContinueReady extends base_command_1.BaseCommand {
|
|
|
70
70
|
this.handleApiError(err);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
// `availableSlots` is null for an UNLIMITED space, which is the default.
|
|
74
|
+
// `Math.min(n, null)` is 0, so the previous arithmetic silently computed a
|
|
75
|
+
// start limit of zero and reported every ready task as "skipped for
|
|
76
|
+
// capacity" on exactly the spaces that had no capacity limit at all. The
|
|
77
|
+
// platform's own helper warns about this: callers must branch on null
|
|
78
|
+
// rather than do arithmetic on it (SpaceTaskConcurrency.availableSlotsFor).
|
|
79
|
+
const freeSlots = capacity && capacity.availableSlots !== null
|
|
80
|
+
? capacity.availableSlots
|
|
81
|
+
: Number.POSITIVE_INFINITY;
|
|
74
82
|
const startLimit = Math.max(0, Math.min(flags['max-starts'], freeSlots));
|
|
75
83
|
const selected = ready.slice(0, startLimit);
|
|
76
84
|
const skippedForCapacity = ready.slice(startLimit);
|
|
@@ -53,6 +53,7 @@ export default class TasksCreate extends BaseCommand {
|
|
|
53
53
|
'assignee-user': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
54
54
|
'dri-agent': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
55
55
|
'dri-user': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
56
|
+
expect: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
56
57
|
'execution-mode': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
57
58
|
'start-date': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
58
59
|
'due-date': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|