@skrr-ai/cli 0.1.101 → 0.1.102
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/commands/commitments/control/apply.d.ts +15 -0
- package/dist/commands/commitments/control/apply.js +36 -0
- package/dist/commands/commitments/control/preview.d.ts +17 -0
- package/dist/commands/commitments/control/preview.js +43 -0
- package/dist/commands/commitments/control/show.d.ts +12 -0
- package/dist/commands/commitments/control/show.js +29 -0
- package/dist/commands/commitments/create.js +5 -2
- package/dist/commands/commitments/observation/probe.d.ts +15 -0
- package/dist/commands/commitments/observation/probe.js +46 -0
- package/dist/commands/commitments/observation/summary.d.ts +9 -0
- package/dist/commands/commitments/observation/summary.js +31 -0
- package/dist/commands/commitments/readiness/show.d.ts +12 -0
- package/dist/commands/commitments/readiness/show.js +29 -0
- package/dist/commands/commitments/readiness/verify.d.ts +13 -0
- package/dist/commands/commitments/readiness/verify.js +33 -0
- package/dist/commands/commitments/update.js +11 -9
- package/dist/commands/commitments/work-plan.d.ts +11 -0
- package/dist/commands/commitments/work-plan.js +36 -0
- package/dist/commands/compass/effective-policy.d.ts +12 -0
- package/dist/commands/compass/effective-policy.js +34 -0
- package/dist/commands/compass/update.d.ts +12 -0
- package/dist/commands/compass/update.js +27 -0
- package/dist/commands/machines/dedicated/exec.js +3 -1
- package/dist/commands/proposals/discard.d.ts +4 -0
- package/dist/commands/proposals/discard.js +3 -0
- package/dist/commands/proposals/keep.d.ts +4 -0
- package/dist/commands/proposals/keep.js +3 -0
- package/dist/commands/proposals/snooze.d.ts +4 -0
- package/dist/commands/proposals/snooze.js +3 -0
- package/dist/commands/spaces/workflows/cancel-node.d.ts +16 -0
- package/dist/commands/spaces/workflows/cancel-node.js +44 -0
- package/dist/commands/spaces/workflows/cancel.d.ts +40 -0
- package/dist/commands/spaces/workflows/cancel.js +109 -3
- package/dist/commands/spaces/workflows/run.d.ts +9 -0
- package/dist/commands/spaces/workflows/run.js +38 -0
- package/dist/commands/tasks/schedule.js +1 -1
- package/dist/commands/triggers/capabilities.js +7 -0
- package/dist/commands/triggers/create.js +4 -1
- package/dist/lib/commitments.d.ts +19 -0
- package/dist/lib/commitments.js +36 -3
- package/dist/lib/compasses.d.ts +3 -0
- package/dist/lib/compasses.js +1 -0
- package/dist/lib/responsibility-controls.d.ts +11 -0
- package/dist/lib/responsibility-controls.js +119 -0
- package/dist/lib/stewardship-output.js +13 -2
- package/dist/lib/workflow-node-control.d.ts +10 -0
- package/dist/lib/workflow-node-control.js +76 -0
- package/dist/lib/workspaces.d.ts +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6369 -6355
- package/oclif.manifest.json +1033 -155
- package/package.json +11 -2
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ProposalDecisionCommand } from '../../lib/proposal-decision-command';
|
|
2
2
|
export default class ProposalsDiscard extends ProposalDecisionCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
4
8
|
static decision: "discard";
|
|
5
9
|
}
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const proposal_decision_command_1 = require("../../lib/proposal-decision-command");
|
|
4
4
|
class ProposalsDiscard extends proposal_decision_command_1.ProposalDecisionCommand {
|
|
5
5
|
static description = 'Discard one displayed proposal through its domain authority';
|
|
6
|
+
// Own, not inherited: oclif spreads the command class into the parser input, and a static a
|
|
7
|
+
// subclass only inherits is dropped, which left these commands with no declared KIND/ID (OSK-15346).
|
|
8
|
+
static args = proposal_decision_command_1.ProposalDecisionCommand.args;
|
|
6
9
|
static decision = 'discard';
|
|
7
10
|
}
|
|
8
11
|
exports.default = ProposalsDiscard;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ProposalDecisionCommand } from '../../lib/proposal-decision-command';
|
|
2
2
|
export default class ProposalsKeep extends ProposalDecisionCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
4
8
|
static decision: "keep";
|
|
5
9
|
}
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const proposal_decision_command_1 = require("../../lib/proposal-decision-command");
|
|
4
4
|
class ProposalsKeep extends proposal_decision_command_1.ProposalDecisionCommand {
|
|
5
5
|
static description = 'Keep one displayed proposal through its domain authority';
|
|
6
|
+
// Own, not inherited: oclif spreads the command class into the parser input, and a static a
|
|
7
|
+
// subclass only inherits is dropped, which left these commands with no declared KIND/ID (OSK-15346).
|
|
8
|
+
static args = proposal_decision_command_1.ProposalDecisionCommand.args;
|
|
6
9
|
static decision = 'keep';
|
|
7
10
|
}
|
|
8
11
|
exports.default = ProposalsKeep;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ProposalDecisionCommand } from '../../lib/proposal-decision-command';
|
|
2
2
|
export default class ProposalsSnooze extends ProposalDecisionCommand {
|
|
3
3
|
static description: string;
|
|
4
|
+
static args: {
|
|
5
|
+
kind: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
};
|
|
4
8
|
static decision: "snooze";
|
|
5
9
|
}
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
const proposal_decision_command_1 = require("../../lib/proposal-decision-command");
|
|
4
4
|
class ProposalsSnooze extends proposal_decision_command_1.ProposalDecisionCommand {
|
|
5
5
|
static description = 'Snooze one displayed proposal through its domain authority';
|
|
6
|
+
// Own, not inherited: oclif spreads the command class into the parser input, and a static a
|
|
7
|
+
// subclass only inherits is dropped, which left these commands with no declared KIND/ID (OSK-15346).
|
|
8
|
+
static args = proposal_decision_command_1.ProposalDecisionCommand.args;
|
|
6
9
|
static decision = 'snooze';
|
|
7
10
|
}
|
|
8
11
|
exports.default = ProposalsSnooze;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
export default class SpacesWorkflowsCancelNode extends BaseCommand {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
spaceId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
runId: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
8
|
+
nodeKey: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
|
|
9
|
+
};
|
|
10
|
+
static flags: {
|
|
11
|
+
'from-json': import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
12
|
+
reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
13
|
+
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
};
|
|
15
|
+
run(): Promise<void>;
|
|
16
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
const prompt_1 = require("../../../lib/prompt");
|
|
7
|
+
const workflow_node_control_1 = require("../../../lib/workflow-node-control");
|
|
8
|
+
class SpacesWorkflowsCancelNode extends base_command_1.BaseCommand {
|
|
9
|
+
static description = 'Request cancellation of one exact workflow node attempt and show the returned execution state';
|
|
10
|
+
static examples = [
|
|
11
|
+
'<%= config.bin %> spaces workflows run <space> <run> --json > reviewed-run.json',
|
|
12
|
+
'<%= config.bin %> spaces workflows cancel-node <space> <run> <node> --from-json reviewed-run.json',
|
|
13
|
+
];
|
|
14
|
+
static args = {
|
|
15
|
+
spaceId: core_1.Args.string({ description: 'Owning Space ID', required: true, ignoreStdin: true }),
|
|
16
|
+
runId: core_1.Args.string({ description: 'Workflow run ID', required: true, ignoreStdin: true }),
|
|
17
|
+
nodeKey: core_1.Args.string({ description: 'Node key to stop', required: true, ignoreStdin: true }),
|
|
18
|
+
};
|
|
19
|
+
static flags = {
|
|
20
|
+
'from-json': core_1.Flags.string({ description: 'Saved canonical workflow run snapshot, or - for stdin', required: true }),
|
|
21
|
+
reason: core_1.Flags.string({ description: 'Cancellation reason (at most 500 characters)' }),
|
|
22
|
+
json: core_1.Flags.boolean({ description: 'Output the updated workflow run as JSON' }),
|
|
23
|
+
};
|
|
24
|
+
async run() {
|
|
25
|
+
this.requireAuth();
|
|
26
|
+
const { args, flags } = await this.parse(SpacesWorkflowsCancelNode);
|
|
27
|
+
try {
|
|
28
|
+
const body = (0, workflow_node_control_1.nodeCancellationFromSnapshot)(args.spaceId, args.runId, args.nodeKey, await (0, prompt_1.readJsonBody)(flags['from-json']), flags.reason);
|
|
29
|
+
const run = await data_provider_1.dataService.cancelSpaceWorkflowNode(args.spaceId, args.runId, args.nodeKey, body);
|
|
30
|
+
if (flags.json) {
|
|
31
|
+
this.log(JSON.stringify(run, null, 2));
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const node = run.nodes?.find((item) => item.nodeKey === args.nodeKey);
|
|
35
|
+
this.log(`Cancellation for ${args.nodeKey}: ${node?.cancellation?.state ?? 'unknown'}.`);
|
|
36
|
+
this.log(`Returned node state: ${node?.status ?? 'unknown'}.`);
|
|
37
|
+
this.log(`Workflow run ${run.id}: ${run.status}.`);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
this.handleApiError(error);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.default = SpacesWorkflowsCancelNode;
|
|
@@ -1,4 +1,43 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* Child workflow runs a cancelled run was waiting on. A cancelled node keeps its `waitingFor`,
|
|
4
|
+
* and every node kind that starts a child run (`workflow_call` = subworkflow, `loop` iteration,
|
|
5
|
+
* `task_plan`) records that run as `waitingFor.workflowRunId`. Reading the id rather than the
|
|
6
|
+
* kind means a new child-starting node kind is not silently missed.
|
|
7
|
+
*/
|
|
8
|
+
export declare function childWorkflowRunIds(run: {
|
|
9
|
+
id?: string;
|
|
10
|
+
nodes?: Array<{
|
|
11
|
+
waitingFor?: unknown;
|
|
12
|
+
}>;
|
|
13
|
+
}): string[];
|
|
14
|
+
/**
|
|
15
|
+
* Lines for a cancel that has not finished converging. `cancellation.state: settled` with
|
|
16
|
+
* nothing pending is the only converged answer; anything else must not read as "stopped".
|
|
17
|
+
*/
|
|
18
|
+
export declare function describeCancellationProgress(spaceId: string, run: {
|
|
19
|
+
id: string;
|
|
20
|
+
cancellation?: {
|
|
21
|
+
state?: string;
|
|
22
|
+
descendantsPending?: boolean;
|
|
23
|
+
executorsPending?: number;
|
|
24
|
+
};
|
|
25
|
+
}): string[];
|
|
26
|
+
/**
|
|
27
|
+
* The lines that tell a person which child runs a `preserve_children` cancel left running.
|
|
28
|
+
* "Cancelled" next to a child that is still waiting forever is the part nobody could see
|
|
29
|
+
* (OSK-15331), so say it and say how to stop them. Re-cancelling the parent with
|
|
30
|
+
* `--scope subtree` widens the stop to every descendant (the server persists the wider
|
|
31
|
+
* decision before touching any descendant). An unreadable child is reported as unreadable,
|
|
32
|
+
* never as stopped.
|
|
33
|
+
*/
|
|
34
|
+
export declare function describePreservedChildren(spaceId: string, parentRunId: string, children: Array<{
|
|
35
|
+
id: string;
|
|
36
|
+
status?: string;
|
|
37
|
+
unreadable?: boolean;
|
|
38
|
+
}>, { reviewedScope }?: {
|
|
39
|
+
reviewedScope?: boolean;
|
|
40
|
+
}): string[];
|
|
2
41
|
export default class SpacesWorkflowsCancel extends BaseCommand {
|
|
3
42
|
static description: string;
|
|
4
43
|
static args: {
|
|
@@ -7,6 +46,7 @@ export default class SpacesWorkflowsCancel extends BaseCommand {
|
|
|
7
46
|
};
|
|
8
47
|
static flags: {
|
|
9
48
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
49
|
+
scope: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
|
|
10
50
|
yes: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
11
51
|
};
|
|
12
52
|
run(): Promise<void>;
|
|
@@ -1,10 +1,84 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.childWorkflowRunIds = childWorkflowRunIds;
|
|
4
|
+
exports.describeCancellationProgress = describeCancellationProgress;
|
|
5
|
+
exports.describePreservedChildren = describePreservedChildren;
|
|
3
6
|
const core_1 = require("@oclif/core");
|
|
4
7
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
8
|
const base_command_1 = require("../../../base-command");
|
|
6
9
|
const prompt_1 = require("../../../lib/prompt");
|
|
7
10
|
const space_output_1 = require("../../../lib/space-output");
|
|
11
|
+
const CANCELLATION_SCOPES = [
|
|
12
|
+
'preserve_children',
|
|
13
|
+
'subtree',
|
|
14
|
+
];
|
|
15
|
+
/**
|
|
16
|
+
* Child workflow runs a cancelled run was waiting on. A cancelled node keeps its `waitingFor`,
|
|
17
|
+
* and every node kind that starts a child run (`workflow_call` = subworkflow, `loop` iteration,
|
|
18
|
+
* `task_plan`) records that run as `waitingFor.workflowRunId`. Reading the id rather than the
|
|
19
|
+
* kind means a new child-starting node kind is not silently missed.
|
|
20
|
+
*/
|
|
21
|
+
function childWorkflowRunIds(run) {
|
|
22
|
+
const ids = new Set();
|
|
23
|
+
for (const node of run.nodes ?? []) {
|
|
24
|
+
const waitingFor = node.waitingFor;
|
|
25
|
+
const childId = waitingFor?.workflowRunId;
|
|
26
|
+
if (typeof childId === 'string' && childId && childId !== run.id)
|
|
27
|
+
ids.add(childId);
|
|
28
|
+
}
|
|
29
|
+
return [...ids];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Lines for a cancel that has not finished converging. `cancellation.state: settled` with
|
|
33
|
+
* nothing pending is the only converged answer; anything else must not read as "stopped".
|
|
34
|
+
*/
|
|
35
|
+
function describeCancellationProgress(spaceId, run) {
|
|
36
|
+
const c = run.cancellation;
|
|
37
|
+
if (!c)
|
|
38
|
+
return [];
|
|
39
|
+
const pending = c.state !== 'settled' || c.descendantsPending || Number(c.executorsPending) > 0;
|
|
40
|
+
if (!pending)
|
|
41
|
+
return [];
|
|
42
|
+
const detail = `state ${c.state ?? 'unknown'}, descendant runs pending: ${c.descendantsPending ? 'yes' : 'no'}, executors pending: ${Number(c.executorsPending) || 0}`;
|
|
43
|
+
// `failed` means the cleanup gave up: reading again never converges it, only a reconcile retries.
|
|
44
|
+
if (c.state === 'failed') {
|
|
45
|
+
return [
|
|
46
|
+
`Cancellation cleanup gave up (${detail}).`,
|
|
47
|
+
` Retry it with: skrr spaces workflows reconcile ${spaceId} ${run.id}`,
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
return [
|
|
51
|
+
`Cancellation is still converging (${detail}).`,
|
|
52
|
+
` Re-read it with: skrr spaces workflows run ${spaceId} ${run.id}`,
|
|
53
|
+
];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The lines that tell a person which child runs a `preserve_children` cancel left running.
|
|
57
|
+
* "Cancelled" next to a child that is still waiting forever is the part nobody could see
|
|
58
|
+
* (OSK-15331), so say it and say how to stop them. Re-cancelling the parent with
|
|
59
|
+
* `--scope subtree` widens the stop to every descendant (the server persists the wider
|
|
60
|
+
* decision before touching any descendant). An unreadable child is reported as unreadable,
|
|
61
|
+
* never as stopped.
|
|
62
|
+
*/
|
|
63
|
+
function describePreservedChildren(spaceId, parentRunId, children, { reviewedScope = false } = {}) {
|
|
64
|
+
const live = children.filter((child) => child.unreadable || (0, data_provider_1.isActiveWorkflowRunStatus)(child.status));
|
|
65
|
+
if (live.length === 0)
|
|
66
|
+
return [];
|
|
67
|
+
const lines = [
|
|
68
|
+
`Left running (scope preserve_children): ${live.length} child workflow run${live.length === 1 ? '' : 's'} started by this run.`,
|
|
69
|
+
];
|
|
70
|
+
for (const child of live) {
|
|
71
|
+
lines.push(` ${child.id} (${child.unreadable ? 'status unreadable' : child.status})`);
|
|
72
|
+
}
|
|
73
|
+
if (reviewedScope) {
|
|
74
|
+
// A stop under a reviewed scope is frozen to that review, so cancelling the parent again
|
|
75
|
+
// does not widen it; each child is stopped on its own.
|
|
76
|
+
lines.push(`This run was stopped under a reviewed scope, so cancelling it again will not widen the stop.`, `Stop a child with: skrr spaces workflows cancel ${spaceId} <child-run-id> --scope subtree --yes`);
|
|
77
|
+
return lines;
|
|
78
|
+
}
|
|
79
|
+
lines.push(`Stop them now: skrr spaces workflows cancel ${spaceId} ${parentRunId} --scope subtree --yes`, ` (widens this cancel to every descendant run), or stop one: skrr spaces workflows cancel ${spaceId} <child-run-id> --scope subtree --yes`);
|
|
80
|
+
return lines;
|
|
81
|
+
}
|
|
8
82
|
class SpacesWorkflowsCancel extends base_command_1.BaseCommand {
|
|
9
83
|
static description = 'Cancel a workflow run and stop further graph coordination';
|
|
10
84
|
static args = {
|
|
@@ -12,15 +86,25 @@ class SpacesWorkflowsCancel extends base_command_1.BaseCommand {
|
|
|
12
86
|
runId: core_1.Args.string({ description: 'Workflow run ID', required: true, ignoreStdin: true }),
|
|
13
87
|
};
|
|
14
88
|
static flags = {
|
|
15
|
-
json: core_1.Flags.boolean({
|
|
89
|
+
json: core_1.Flags.boolean({
|
|
90
|
+
description: 'Output the cancelled run as JSON (cancellation.scope and the waitingFor.workflowRunId of the cancelled nodes show which child runs were left running)',
|
|
91
|
+
}),
|
|
92
|
+
scope: core_1.Flags.string({
|
|
93
|
+
description: 'What happens to child workflow runs this run started: preserve_children (default) leaves them running, subtree stops every descendant run too',
|
|
94
|
+
options: [...CANCELLATION_SCOPES],
|
|
95
|
+
}),
|
|
16
96
|
yes: core_1.Flags.boolean({ char: 'y', description: 'Skip confirmation prompt' }),
|
|
17
97
|
};
|
|
18
98
|
async run() {
|
|
19
99
|
this.requireAuth();
|
|
20
100
|
const { args, flags } = await this.parse(SpacesWorkflowsCancel);
|
|
101
|
+
const scope = flags.scope;
|
|
21
102
|
let confirmed;
|
|
22
103
|
try {
|
|
23
|
-
|
|
104
|
+
const scopeNote = scope === 'subtree'
|
|
105
|
+
? ' Child workflow runs it started are cancelled too.'
|
|
106
|
+
: ' Child workflow runs it started keep running (--scope subtree stops them too).';
|
|
107
|
+
confirmed = await (0, prompt_1.confirmDestructive)(`Cancel workflow run ${args.runId}? This stops workflow coordination; already-dispatched task execution may continue.${scopeNote}`, flags.yes);
|
|
24
108
|
}
|
|
25
109
|
catch (error) {
|
|
26
110
|
this.error(error.message, {
|
|
@@ -34,7 +118,7 @@ class SpacesWorkflowsCancel extends base_command_1.BaseCommand {
|
|
|
34
118
|
}
|
|
35
119
|
let run;
|
|
36
120
|
try {
|
|
37
|
-
run = await data_provider_1.dataService.cancelSpaceWorkflowRun(args.spaceId, args.runId);
|
|
121
|
+
run = await data_provider_1.dataService.cancelSpaceWorkflowRun(args.spaceId, args.runId, scope ? { scope } : {});
|
|
38
122
|
}
|
|
39
123
|
catch (error) {
|
|
40
124
|
this.handleApiError(error);
|
|
@@ -45,6 +129,28 @@ class SpacesWorkflowsCancel extends base_command_1.BaseCommand {
|
|
|
45
129
|
return;
|
|
46
130
|
}
|
|
47
131
|
this.log(`Cancelled workflow run ${run.id} (${run.status}).`);
|
|
132
|
+
for (const line of describeCancellationProgress(args.spaceId, run))
|
|
133
|
+
this.log(line);
|
|
134
|
+
if ((run.cancellation?.scope ?? run.cancellationScope ?? scope) === 'subtree')
|
|
135
|
+
return;
|
|
136
|
+
const childIds = childWorkflowRunIds(run);
|
|
137
|
+
if (childIds.length === 0)
|
|
138
|
+
return;
|
|
139
|
+
const children = await Promise.all(childIds.map(async (id) => {
|
|
140
|
+
try {
|
|
141
|
+
const child = await data_provider_1.dataService.getSpaceWorkflowRun(args.spaceId, id);
|
|
142
|
+
return { id, status: child.status };
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return { id, unreadable: true };
|
|
146
|
+
}
|
|
147
|
+
}));
|
|
148
|
+
const reviewedScope = Boolean(run.cancellationExpectedScope);
|
|
149
|
+
for (const line of describePreservedChildren(args.spaceId, run.id, children, {
|
|
150
|
+
reviewedScope,
|
|
151
|
+
})) {
|
|
152
|
+
this.log(line);
|
|
153
|
+
}
|
|
48
154
|
}
|
|
49
155
|
}
|
|
50
156
|
exports.default = SpacesWorkflowsCancel;
|
|
@@ -16,6 +16,15 @@ export declare function describeNodeAttention(node: {
|
|
|
16
16
|
message?: string;
|
|
17
17
|
} | null;
|
|
18
18
|
}): string;
|
|
19
|
+
export declare function describeLoopItemsNote(node: {
|
|
20
|
+
nodeKey?: unknown;
|
|
21
|
+
nodeType?: unknown;
|
|
22
|
+
output?: {
|
|
23
|
+
itemsStatus?: unknown;
|
|
24
|
+
itemsKey?: unknown;
|
|
25
|
+
itemsFoundType?: unknown;
|
|
26
|
+
} | null;
|
|
27
|
+
}): string;
|
|
19
28
|
export default class SpacesWorkflowsRun extends BaseCommand {
|
|
20
29
|
static description: string;
|
|
21
30
|
static args: {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.describeNodeAttention = describeNodeAttention;
|
|
4
|
+
exports.describeLoopItemsNote = describeLoopItemsNote;
|
|
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");
|
|
@@ -26,6 +27,38 @@ function describeNodeAttention(node) {
|
|
|
26
27
|
return `${reason}: ${message}`;
|
|
27
28
|
return reason || message || 'needs attention';
|
|
28
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* A note for a loop that succeeded having iterated nothing because its
|
|
32
|
+
* collection never arrived, or '' when there is nothing to say (OSK-15324).
|
|
33
|
+
*
|
|
34
|
+
* A zero-iteration loop is `succeeded`, so the plain table reads as healthy
|
|
35
|
+
* whether the list was legitimately empty or the value the author meant never
|
|
36
|
+
* reached the loop. The server records which on the node output; say it. An
|
|
37
|
+
* empty list is the legitimate case and stays silent.
|
|
38
|
+
*/
|
|
39
|
+
/** One printable line: control characters (an ESC sequence included) become spaces. */
|
|
40
|
+
function oneLine(value) {
|
|
41
|
+
// eslint-disable-next-line no-control-regex
|
|
42
|
+
return (0, format_1.collapseWhitespace)(value.replace(/[\u0000-\u001f\u007f-\u009f]/g, ' '));
|
|
43
|
+
}
|
|
44
|
+
function describeLoopItemsNote(node) {
|
|
45
|
+
if (node.nodeType !== 'loop')
|
|
46
|
+
return '';
|
|
47
|
+
const output = node.output;
|
|
48
|
+
// `itemsKey` is author-chosen free text, unlike a node key, so it is printed the way the
|
|
49
|
+
// table prints a cell: one line, no control characters.
|
|
50
|
+
const key = oneLine(typeof output?.itemsKey === 'string' && output.itemsKey ? output.itemsKey : 'items');
|
|
51
|
+
const name = oneLine(String(node.nodeKey ?? 'loop'));
|
|
52
|
+
if (output?.itemsStatus === 'missing') {
|
|
53
|
+
return `${name}: ran 0 iterations because its input "${key}" was missing or null`;
|
|
54
|
+
}
|
|
55
|
+
if (output?.itemsStatus === 'not_array') {
|
|
56
|
+
const found = typeof output.itemsFoundType === 'string' ? output.itemsFoundType : 'a value';
|
|
57
|
+
const article = /^[aeiou]/i.test(found) ? 'an' : 'a';
|
|
58
|
+
return `${name}: ran 0 iterations because its input "${key}" was ${article} ${oneLine(found)}, not a list`;
|
|
59
|
+
}
|
|
60
|
+
return '';
|
|
61
|
+
}
|
|
29
62
|
class SpacesWorkflowsRun extends base_command_1.BaseCommand {
|
|
30
63
|
static description = 'Inspect a workflow run and each durable node state';
|
|
31
64
|
static args = {
|
|
@@ -84,6 +117,11 @@ class SpacesWorkflowsRun extends base_command_1.BaseCommand {
|
|
|
84
117
|
? [{ key: 'attention', header: 'ATTENTION' }]
|
|
85
118
|
: []),
|
|
86
119
|
], (line) => this.log(line));
|
|
120
|
+
for (const node of nodes) {
|
|
121
|
+
const note = describeLoopItemsNote(node);
|
|
122
|
+
if (note)
|
|
123
|
+
this.log(`Note: ${note}`);
|
|
124
|
+
}
|
|
87
125
|
}
|
|
88
126
|
}
|
|
89
127
|
}
|
|
@@ -28,7 +28,7 @@ class TasksSchedule extends base_command_1.BaseCommand {
|
|
|
28
28
|
dependsOn: ['cron'],
|
|
29
29
|
}),
|
|
30
30
|
'max-runs': core_1.Flags.integer({
|
|
31
|
-
description: 'Cap total
|
|
31
|
+
description: 'Cap total occurrences (recurring only): each scheduled fire counts, and so does an occurrence you complete by hand before the scheduler touches it',
|
|
32
32
|
dependsOn: ['cron'],
|
|
33
33
|
}),
|
|
34
34
|
'end-date': core_1.Flags.string({
|
|
@@ -37,6 +37,13 @@ class TriggersCapabilities extends base_command_1.BaseCommand {
|
|
|
37
37
|
? capabilities.templateVariables.map((v) => typeof v === 'object' && v !== null ? String(v.name) : String(v))
|
|
38
38
|
: capabilities.templateVariableNames;
|
|
39
39
|
this.log(`Template vars: ${Array.isArray(vars) ? vars.join(', ') : '-'}`);
|
|
40
|
+
// Some scopes cannot create a trigger until something else is set up, and the refusal
|
|
41
|
+
// otherwise arrives only after submitting.
|
|
42
|
+
const needs = capabilities.prerequisites;
|
|
43
|
+
if (Array.isArray(needs)) {
|
|
44
|
+
for (const need of needs)
|
|
45
|
+
this.log(`Needs: ${String(need)}`);
|
|
46
|
+
}
|
|
40
47
|
// Beside the modes, because `external_work_task` is a mode that cannot be
|
|
41
48
|
// authored without one of these.
|
|
42
49
|
const contracts = capabilities.effectContracts;
|
|
@@ -20,7 +20,10 @@ class TriggersCreate extends base_command_1.BaseCommand {
|
|
|
20
20
|
'from-json': core_1.Flags.string({
|
|
21
21
|
description: 'Path to a JSON file (or "-" for stdin) to use as the request body',
|
|
22
22
|
}),
|
|
23
|
-
scope: core_1.Flags.string({
|
|
23
|
+
scope: core_1.Flags.string({
|
|
24
|
+
description: 'Trigger scope kind. A scope can have prerequisites (a space_oracle Space needs a Leader agent): `triggers capabilities <scope>` lists them under "Needs"',
|
|
25
|
+
options: [...triggers_1.TRIGGER_SCOPE_KINDS],
|
|
26
|
+
}),
|
|
24
27
|
ref: core_1.Flags.string({
|
|
25
28
|
description: 'Scope ref as key=value (repeatable), for example --ref agentId=abc',
|
|
26
29
|
multiple: true,
|
|
@@ -247,6 +247,23 @@ export declare const commitmentApi: {
|
|
|
247
247
|
};
|
|
248
248
|
}>;
|
|
249
249
|
get: (id: string) => Promise<CommitmentStatusResponse>;
|
|
250
|
+
controlPreview: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<Record<string, unknown>>;
|
|
251
|
+
applyControl: (id: string, body: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
252
|
+
controlReceipt: (id: string, controlId: string) => Promise<Record<string, unknown>>;
|
|
253
|
+
workPlan: (id: string) => Promise<Record<string, unknown>>;
|
|
254
|
+
observationSummary: (query?: {
|
|
255
|
+
agentId?: string;
|
|
256
|
+
}) => Promise<Record<string, unknown>>;
|
|
257
|
+
draftObservationProbe: (body: Record<string, unknown>) => Promise<Record<string, unknown>>;
|
|
258
|
+
observationProbe: (id: string, body?: {
|
|
259
|
+
expectedVersion?: number;
|
|
260
|
+
timeoutMs?: number;
|
|
261
|
+
}) => Promise<Record<string, unknown>>;
|
|
262
|
+
verifyReadiness: (id: string, body: {
|
|
263
|
+
expectedVersion: number;
|
|
264
|
+
timeoutMs?: number;
|
|
265
|
+
}) => Promise<Record<string, unknown>>;
|
|
266
|
+
readinessReceipt: (id: string, receiptId: string) => Promise<Record<string, unknown>>;
|
|
250
267
|
create: (body: Record<string, unknown>) => Promise<Commitment>;
|
|
251
268
|
update: (id: string, body: Record<string, unknown>) => Promise<Commitment>;
|
|
252
269
|
draftPreflight: (body: Record<string, unknown>) => Promise<CommitmentPreflightReport>;
|
|
@@ -629,6 +646,8 @@ export type WatchFlagInputs = {
|
|
|
629
646
|
spaceIds?: string[];
|
|
630
647
|
monitorIds?: string[];
|
|
631
648
|
};
|
|
649
|
+
/** Keep the canonical one-binding-per-source contract without discarding conflicting intent. */
|
|
650
|
+
export declare function normalizeWatchedSourceDeclarations<T>(declarations: T[]): T[];
|
|
632
651
|
export declare function buildWatchedSources(inputs: WatchFlagInputs): Array<Record<string, unknown>> | undefined;
|
|
633
652
|
/** The evidence-health shape carried on a check's `inputs`. */
|
|
634
653
|
export type EvidenceHealthView = {
|
package/dist/lib/commitments.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.parseCommitmentConcurrencyLimit = parseCommitmentConcurrencyLimit;
|
|
|
9
9
|
exports.commitmentConcurrencyEnvelopePatch = commitmentConcurrencyEnvelopePatch;
|
|
10
10
|
exports.describeCommitmentConcurrency = describeCommitmentConcurrency;
|
|
11
11
|
exports.parseIntervalToMs = parseIntervalToMs;
|
|
12
|
+
exports.normalizeWatchedSourceDeclarations = normalizeWatchedSourceDeclarations;
|
|
12
13
|
exports.buildWatchedSources = buildWatchedSources;
|
|
13
14
|
exports.describeEvidenceHealth = describeEvidenceHealth;
|
|
14
15
|
exports.describeIntervalMs = describeIntervalMs;
|
|
@@ -33,6 +34,7 @@ exports.renderCommitmentCases = renderCommitmentCases;
|
|
|
33
34
|
exports.renderCommitmentCaseDetail = renderCommitmentCaseDetail;
|
|
34
35
|
exports.renderCommitmentRemediationProposals = renderCommitmentRemediationProposals;
|
|
35
36
|
exports.renderCommitmentRemediationProposal = renderCommitmentRemediationProposal;
|
|
37
|
+
const node_util_1 = require("node:util");
|
|
36
38
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
37
39
|
/**
|
|
38
40
|
* commitments.ts — request-based client for the Commitments subsystem
|
|
@@ -138,6 +140,15 @@ exports.commitmentApi = {
|
|
|
138
140
|
// GET /:id returns `{ commitment, latestCheck }` — NOT a bare contract.
|
|
139
141
|
// Unwrap before rendering, or every field reads as undefined.
|
|
140
142
|
get: (id) => data_provider_2.request.get(base(id)),
|
|
143
|
+
controlPreview: (id, query) => data_provider_2.request.get((0, triggers_1.withQuery)(`${base(id)}/control-preview`, query)),
|
|
144
|
+
applyControl: (id, body) => data_provider_2.request.post(`${base(id)}/control`, body),
|
|
145
|
+
controlReceipt: (id, controlId) => data_provider_2.request.get(`${base(id)}/control/${encodeURIComponent(controlId)}`),
|
|
146
|
+
workPlan: (id) => data_provider_2.request.get(`${base(id)}/work-plan`),
|
|
147
|
+
observationSummary: (query = {}) => data_provider_2.request.get((0, triggers_1.withQuery)('/api/commitments/observation-summary', query)),
|
|
148
|
+
draftObservationProbe: (body) => data_provider_2.request.post('/api/commitments/observation-probe', body),
|
|
149
|
+
observationProbe: (id, body = {}) => data_provider_2.request.post(`${base(id)}/observation-probe`, body),
|
|
150
|
+
verifyReadiness: (id, body) => data_provider_2.request.post(`${base(id)}/readiness-receipt`, body),
|
|
151
|
+
readinessReceipt: (id, receiptId) => data_provider_2.request.get(`${base(id)}/readiness-receipt/${encodeURIComponent(receiptId)}`),
|
|
141
152
|
create: (body) => data_provider_2.request.post('/api/commitments', body),
|
|
142
153
|
update: (id, body) => data_provider_2.request.patch(base(id), body),
|
|
143
154
|
draftPreflight: (body) => data_provider_2.request.post('/api/commitments/preflight', body),
|
|
@@ -496,11 +507,33 @@ function parseIntervalToMs(input) {
|
|
|
496
507
|
}
|
|
497
508
|
return ms;
|
|
498
509
|
}
|
|
510
|
+
/** Keep the canonical one-binding-per-source contract without discarding conflicting intent. */
|
|
511
|
+
function normalizeWatchedSourceDeclarations(declarations) {
|
|
512
|
+
const bySource = new Map();
|
|
513
|
+
const normalized = [];
|
|
514
|
+
for (const binding of declarations) {
|
|
515
|
+
if (!binding || typeof binding !== 'object' ||
|
|
516
|
+
!('source' in binding) || typeof binding.source !== 'string') {
|
|
517
|
+
normalized.push(binding); // The API owns validation of the remaining DTO.
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
520
|
+
if (bySource.has(binding.source)) {
|
|
521
|
+
if (!(0, node_util_1.isDeepStrictEqual)(bySource.get(binding.source), binding)) {
|
|
522
|
+
throw new Error(`Watched source "${binding.source}" has conflicting declarations. ` +
|
|
523
|
+
'Choose one complete binding per source.');
|
|
524
|
+
}
|
|
525
|
+
continue;
|
|
526
|
+
}
|
|
527
|
+
bySource.set(binding.source, binding);
|
|
528
|
+
normalized.push(binding);
|
|
529
|
+
}
|
|
530
|
+
return normalized;
|
|
531
|
+
}
|
|
499
532
|
function buildWatchedSources(inputs) {
|
|
500
533
|
const watched = (inputs.watch ?? []).filter(Boolean);
|
|
501
534
|
const metricKeys = (inputs.metricKey ?? []).filter(Boolean);
|
|
502
|
-
const spaceIds = (inputs.spaceIds ?? []).filter(Boolean);
|
|
503
|
-
const monitorIds = (inputs.monitorIds ?? []).filter(Boolean);
|
|
535
|
+
const spaceIds = [...new Set((inputs.spaceIds ?? []).filter(Boolean))];
|
|
536
|
+
const monitorIds = [...new Set((inputs.monitorIds ?? []).filter(Boolean))];
|
|
504
537
|
const githubEvents = (inputs.githubEvents ?? []).filter(Boolean);
|
|
505
538
|
const githubRepository = String(inputs.githubRepository || '')
|
|
506
539
|
.trim()
|
|
@@ -584,7 +617,7 @@ function buildWatchedSources(inputs) {
|
|
|
584
617
|
rows.push({ source, enabled: true });
|
|
585
618
|
}
|
|
586
619
|
}
|
|
587
|
-
return rows;
|
|
620
|
+
return normalizeWatchedSourceDeclarations(rows);
|
|
588
621
|
}
|
|
589
622
|
/**
|
|
590
623
|
* One line an author can act on, or nothing.
|
package/dist/lib/compasses.d.ts
CHANGED
|
@@ -88,6 +88,9 @@ export declare const compassApi: {
|
|
|
88
88
|
}>;
|
|
89
89
|
create: (body: Record<string, unknown>) => Promise<Compass>;
|
|
90
90
|
get: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<Compass>;
|
|
91
|
+
effectivePolicy: (id: string, query: {
|
|
92
|
+
agentId: string;
|
|
93
|
+
}) => Promise<Record<string, unknown>>;
|
|
91
94
|
update: (id: string, body: Record<string, unknown>) => Promise<Compass>;
|
|
92
95
|
archive: (id: string, query: Parameters<typeof withQuery>[1]) => Promise<{
|
|
93
96
|
ok: boolean;
|
package/dist/lib/compasses.js
CHANGED
|
@@ -35,6 +35,7 @@ exports.compassApi = {
|
|
|
35
35
|
list: (query) => data_provider_2.request.get((0, triggers_1.withQuery)('/api/compasses', query)),
|
|
36
36
|
create: (body) => data_provider_2.request.post('/api/compasses', body),
|
|
37
37
|
get: (id, query) => data_provider_2.request.get((0, triggers_1.withQuery)(base(id), query)),
|
|
38
|
+
effectivePolicy: (id, query) => data_provider_2.request.get((0, triggers_1.withQuery)(`${base(id)}/effective-policy`, query)),
|
|
38
39
|
update: (id, body) => data_provider_2.request.patch(base(id), body),
|
|
39
40
|
archive: (id, query) => data_provider_2.request.delete((0, triggers_1.withQuery)(base(id), query)),
|
|
40
41
|
revisions: (id, query) => data_provider_2.request.get((0, triggers_1.withQuery)(`${base(id)}/revisions`, query)),
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Scope/version coordinates are intent. The server re-authorizes every read and write. */
|
|
2
|
+
export declare const RESPONSIBILITY_CONTROL_SCOPES: readonly ["mute_updates", "pause_observation", "cancel_worker", "cancel_episode", "cancel_subtree", "retire"];
|
|
3
|
+
/**
|
|
4
|
+
* Apply only the saved preview, never a newly fetched version or live target set.
|
|
5
|
+
* Presentation and authority fields cannot become request fields. The canonical
|
|
6
|
+
* service validates every attempt pin and rejects stale or enlarged selections.
|
|
7
|
+
*/
|
|
8
|
+
export declare function controlFromPreview(commitmentId: string, preview: Record<string, unknown>, idempotencyKey: string, reason?: string): Record<string, unknown>;
|
|
9
|
+
export declare function renderControl(value: Record<string, unknown>, log: (line: string) => void): void;
|
|
10
|
+
export declare function renderReadiness(value: Record<string, unknown>, log: (line: string) => void): void;
|
|
11
|
+
export declare function renderProbe(value: Record<string, unknown>, log: (line: string) => void): void;
|