@skrr-ai/cli 0.1.126 → 0.1.128
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 +23 -0
- package/dist/base-command.js +72 -0
- package/dist/commands/agents/chat.js +7 -0
- package/dist/commands/agents/goals/list.d.ts +2 -2
- package/dist/commands/agents/goals/list.js +2 -2
- package/dist/commands/commitments/action-history.js +1 -1
- package/dist/commands/commitments/cases.js +3 -3
- package/dist/commands/commitments/check-now.d.ts +10 -2
- package/dist/commands/commitments/check-now.js +53 -3
- package/dist/commands/commitments/create.d.ts +6 -14
- package/dist/commands/commitments/create.js +61 -6
- package/dist/commands/commitments/doctor.js +3 -1
- package/dist/commands/commitments/effective-policy.d.ts +11 -1
- package/dist/commands/commitments/effective-policy.js +84 -4
- package/dist/commands/commitments/hypotheses.js +12 -12
- package/dist/commands/commitments/next-tick.js +5 -0
- package/dist/commands/commitments/observations.js +18 -17
- package/dist/commands/commitments/pack/list.js +2 -1
- package/dist/commands/commitments/pack/plan.d.ts +6 -0
- package/dist/commands/commitments/pack/plan.js +19 -0
- package/dist/commands/commitments/preflight.d.ts +19 -0
- package/dist/commands/commitments/preflight.js +108 -10
- package/dist/commands/commitments/trigger-status.js +8 -0
- package/dist/commands/commitments/update.d.ts +1 -0
- package/dist/commands/commitments/update.js +45 -4
- package/dist/commands/commitments/wake-capabilities.js +13 -0
- package/dist/commands/compass/archive.js +4 -0
- package/dist/commands/compass/commitments.js +15 -1
- package/dist/commands/compass/create.js +11 -3
- package/dist/commands/compass/effective-policy.d.ts +1 -0
- package/dist/commands/compass/effective-policy.js +8 -7
- package/dist/commands/compass/revert.d.ts +8 -0
- package/dist/commands/compass/revert.js +25 -1
- package/dist/commands/compass/revisions.d.ts +2 -0
- package/dist/commands/compass/revisions.js +18 -2
- package/dist/commands/compass/tactic.d.ts +5 -3
- package/dist/commands/compass/tactic.js +8 -7
- package/dist/commands/compass/update.js +19 -3
- package/dist/commands/followups/act.js +1 -1
- package/dist/commands/followups/cancel.js +13 -0
- package/dist/commands/followups/observability.js +20 -1
- package/dist/commands/followups/plans/create.js +12 -1
- package/dist/commands/followups/show.js +13 -0
- package/dist/commands/followups/watch.js +1 -1
- package/dist/commands/goals/create.d.ts +1 -0
- package/dist/commands/goals/create.js +4 -1
- package/dist/commands/goals/planner-config.d.ts +11 -0
- package/dist/commands/goals/planner-config.js +16 -0
- package/dist/commands/goals/revisions.js +3 -19
- package/dist/commands/machines/update.d.ts +5 -3
- package/dist/commands/machines/update.js +70 -9
- package/dist/commands/northstars/create.js +3 -1
- package/dist/commands/northstars/governance/configure.d.ts +4 -0
- package/dist/commands/northstars/governance/configure.js +75 -9
- package/dist/commands/northstars/intents/decide.d.ts +6 -0
- package/dist/commands/northstars/intents/decide.js +69 -15
- package/dist/commands/northstars/intents/list.d.ts +2 -1
- package/dist/commands/northstars/intents/list.js +15 -5
- package/dist/commands/northstars/intents.d.ts +26 -0
- package/dist/commands/northstars/intents.js +49 -0
- package/dist/commands/northstars/list.d.ts +1 -0
- package/dist/commands/northstars/list.js +24 -3
- package/dist/commands/northstars/recommendations/list.d.ts +1 -1
- package/dist/commands/northstars/recommendations/list.js +3 -3
- package/dist/commands/northstars/recommendations.d.ts +19 -0
- package/dist/commands/northstars/recommendations.js +42 -0
- package/dist/commands/northstars/review-now.d.ts +1 -0
- package/dist/commands/northstars/review-now.js +10 -4
- package/dist/commands/northstars/reviews.d.ts +1 -0
- package/dist/commands/northstars/reviews.js +19 -3
- package/dist/commands/northstars/update.js +8 -1
- package/dist/commands/proposals/list.js +11 -2
- package/dist/commands/proposals/show.js +9 -1
- package/dist/commands/spaces/auto-executor/preview.d.ts +14 -1
- package/dist/commands/spaces/auto-executor/preview.js +35 -1
- package/dist/commands/spaces/auto-executor/run.d.ts +17 -0
- package/dist/commands/spaces/auto-executor/run.js +60 -2
- package/dist/commands/spaces/files/upload.js +1 -2
- package/dist/commands/spaces/index.js +23 -0
- package/dist/commands/spaces/revisions.js +3 -2
- package/dist/commands/tasks/revisions.js +3 -2
- package/dist/commands/triggers/fire-detail.js +2 -0
- package/dist/generated/agent-guides.json +69 -69
- package/dist/lib/agentic-stream.d.ts +9 -0
- package/dist/lib/agentic-stream.js +14 -0
- package/dist/lib/command-miss.js +11 -2
- package/dist/lib/commitment-attention-copy.d.ts +96 -0
- package/dist/lib/commitment-attention-copy.js +209 -0
- package/dist/lib/commitment-loop-status.d.ts +6 -0
- package/dist/lib/commitment-loop-status.js +16 -6
- package/dist/lib/commitments.d.ts +88 -0
- package/dist/lib/commitments.js +188 -0
- package/dist/lib/compass-body-help.d.ts +20 -0
- package/dist/lib/compass-body-help.js +61 -0
- package/dist/lib/compass-refusals.d.ts +35 -0
- package/dist/lib/compass-refusals.js +50 -0
- package/dist/lib/compasses.d.ts +80 -4
- package/dist/lib/compasses.js +232 -12
- package/dist/lib/followups.d.ts +22 -1
- package/dist/lib/followups.js +21 -1
- package/dist/lib/format.d.ts +11 -0
- package/dist/lib/format.js +29 -0
- package/dist/lib/initiatives.d.ts +9 -2
- package/dist/lib/initiatives.js +13 -2
- package/dist/lib/machines.d.ts +14 -1
- package/dist/lib/machines.js +24 -0
- package/dist/lib/northstars.d.ts +158 -24
- package/dist/lib/northstars.js +538 -26
- package/dist/lib/proposal-decision-command.js +21 -1
- package/dist/lib/proposals.d.ts +26 -1
- package/dist/lib/proposals.js +163 -0
- package/dist/lib/rate-limit-retry.d.ts +5 -0
- package/dist/lib/rate-limit-retry.js +19 -0
- package/dist/lib/revision-actor.d.ts +2 -0
- package/dist/lib/revision-actor.js +16 -0
- package/dist/lib/roles.d.ts +1 -1
- package/dist/lib/space-charter.js +9 -0
- package/dist/lib/task-asset-upload.js +1 -2
- package/dist/lib/triggers.d.ts +33 -1
- package/dist/lib/triggers.js +77 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +7537 -7474
- package/oclif.manifest.json +357 -34
- package/package.json +4 -4
package/dist/base-command.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command, Flags, type Interfaces } from '@oclif/core';
|
|
2
2
|
import { type CliOperationOptions } from './lib/json-output';
|
|
3
|
+
import { type CompassRefusalContext } from './lib/compass-refusals';
|
|
3
4
|
export { resolveProfileFromArgv } from './lib/keychain';
|
|
4
5
|
import { dataService } from '@skrr-ai/data-provider';
|
|
5
6
|
import { type WriteTarget } from './lib/write-target';
|
|
@@ -413,6 +414,12 @@ export declare abstract class BaseCommand extends Command {
|
|
|
413
414
|
* path the person never typed. See `lib/request-field-flags.ts`.
|
|
414
415
|
*/
|
|
415
416
|
protected requestFieldFlags?: Readonly<Record<string, string>>;
|
|
417
|
+
/**
|
|
418
|
+
* The Compass (and Agent) this invocation is about, set by a `compass` command before it sends.
|
|
419
|
+
* A stale version or a missing revision is answered with the command to run next, and that
|
|
420
|
+
* command names these. Unset, the hint carries placeholders. See `lib/compass-refusals.ts`.
|
|
421
|
+
*/
|
|
422
|
+
protected compassRef?: CompassRefusalContext;
|
|
416
423
|
protected handleApiError(err: unknown, { subject }?: {
|
|
417
424
|
subject?: string;
|
|
418
425
|
}): never;
|
|
@@ -515,6 +522,22 @@ export declare function apiRefusal(err: unknown): {
|
|
|
515
522
|
/** The parsed JSON body, for a refusal that carries structured context. */
|
|
516
523
|
body?: Record<string, unknown>;
|
|
517
524
|
};
|
|
525
|
+
/**
|
|
526
|
+
* The remedy a refused Commitment activation carries.
|
|
527
|
+
*
|
|
528
|
+
* The refusal's message names the blocking Preflight gate by its QUESTION and a reason token
|
|
529
|
+
* ("Can the runtime enforce this Run? (runtime_commitment_contract_unavailable)"). The gate's own
|
|
530
|
+
* `hint` — the sentence that says what to run — travels beside it at
|
|
531
|
+
* `diagnostic.firstBlocker.detail`, and the web client already shows it
|
|
532
|
+
* (`activationFailureMessage`). The CLI dropped it, so `commitments resume` and
|
|
533
|
+
* `commitments create --activate` told a person to "fix the gate the reason names" while
|
|
534
|
+
* `commitments preflight` knew the exact command (OSK-21126).
|
|
535
|
+
*
|
|
536
|
+
* Undefined when the gate offered none, so a caller keeps its generic wording.
|
|
537
|
+
*
|
|
538
|
+
* Exported for tests.
|
|
539
|
+
*/
|
|
540
|
+
export declare function commitmentActivationHint(body: Record<string, unknown> | null | undefined): string | undefined;
|
|
518
541
|
/**
|
|
519
542
|
* Prefix a failure message with the resource it is about.
|
|
520
543
|
*
|
package/dist/base-command.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.assigneePayloadNeedsResolution = assigneePayloadNeedsResolution;
|
|
|
5
5
|
exports.parseGlobalFlags = parseGlobalFlags;
|
|
6
6
|
exports.refusalCodeOf = refusalCodeOf;
|
|
7
7
|
exports.apiRefusal = apiRefusal;
|
|
8
|
+
exports.commitmentActivationHint = commitmentActivationHint;
|
|
8
9
|
exports.withSubject = withSubject;
|
|
9
10
|
exports.describeHarnessUnavailable = describeHarnessUnavailable;
|
|
10
11
|
exports.describeGitHubUnavailable = describeGitHubUnavailable;
|
|
@@ -34,6 +35,7 @@ const failed_lookup_1 = require("./lib/failed-lookup");
|
|
|
34
35
|
const internal_id_1 = require("./lib/internal-id");
|
|
35
36
|
const http_error_1 = require("./lib/http-error");
|
|
36
37
|
const api_error_reason_1 = require("./lib/api-error-reason");
|
|
38
|
+
const compass_refusals_1 = require("./lib/compass-refusals");
|
|
37
39
|
const guide_pointer_1 = require("./lib/guide-pointer");
|
|
38
40
|
// Re-exported from its home in `lib/keychain`, beside the other profile
|
|
39
41
|
// functions. It lived here while `init()` was its only caller; a lib module
|
|
@@ -1467,6 +1469,12 @@ class BaseCommand extends core_1.Command {
|
|
|
1467
1469
|
* path the person never typed. See `lib/request-field-flags.ts`.
|
|
1468
1470
|
*/
|
|
1469
1471
|
requestFieldFlags;
|
|
1472
|
+
/**
|
|
1473
|
+
* The Compass (and Agent) this invocation is about, set by a `compass` command before it sends.
|
|
1474
|
+
* A stale version or a missing revision is answered with the command to run next, and that
|
|
1475
|
+
* command names these. Unset, the hint carries placeholders. See `lib/compass-refusals.ts`.
|
|
1476
|
+
*/
|
|
1477
|
+
compassRef;
|
|
1470
1478
|
handleApiError(err, { subject } = {}) {
|
|
1471
1479
|
// An error this command already raised for the user is not an API failure
|
|
1472
1480
|
// to translate. `this.error()` throws a CLIError carrying `oclif.exit`, and
|
|
@@ -1579,6 +1587,36 @@ class BaseCommand extends core_1.Command {
|
|
|
1579
1587
|
details: envelopeDetails(body),
|
|
1580
1588
|
});
|
|
1581
1589
|
}
|
|
1590
|
+
else if ((bodyCode === COMMITMENT_ACTIVATION_BLOCKED ||
|
|
1591
|
+
stringProp(body, 'error') === COMMITMENT_ACTIVATION_BLOCKED) &&
|
|
1592
|
+
commitmentActivationHint(body)) {
|
|
1593
|
+
// The route puts the code in `error`, in upper case, which `refusalCodeOf` does not read
|
|
1594
|
+
// as a code — so this refusal arrived as `HTTP_409` with the gate's QUESTION as its only
|
|
1595
|
+
// explanation. Name the code, and say what to do (OSK-21126).
|
|
1596
|
+
this.failWithCliError({
|
|
1597
|
+
message: say(`${bodyMessage || e.message}\n → ${commitmentActivationHint(body)}`),
|
|
1598
|
+
code: COMMITMENT_ACTIVATION_BLOCKED,
|
|
1599
|
+
status: e.status,
|
|
1600
|
+
exit: 1,
|
|
1601
|
+
retryable: false,
|
|
1602
|
+
details: envelopeDetails(body),
|
|
1603
|
+
});
|
|
1604
|
+
}
|
|
1605
|
+
else if ((e.status === 409 || e.status === 404) && (0, compass_refusals_1.compassRefusalCode)(body)) {
|
|
1606
|
+
// The Compass routes sent the code in upper-case `error`, which `refusalCodeOf` does not
|
|
1607
|
+
// read as a code, so a stale writer saw `HTTP_409` and a mistyped version `NOT_FOUND`,
|
|
1608
|
+
// both without the versions that say what to do (OSK-21162, OSK-21152). Read these two
|
|
1609
|
+
// codes by name here rather than widening `refusalCodeOf` for every route.
|
|
1610
|
+
const compassCode = (0, compass_refusals_1.compassRefusalCode)(body);
|
|
1611
|
+
this.failWithCliError({
|
|
1612
|
+
message: say((0, compass_refusals_1.describeCompassRefusal)(compassCode, body, this.compassRef ?? {}, this.config.bin)),
|
|
1613
|
+
code: compassCode,
|
|
1614
|
+
status: e.status,
|
|
1615
|
+
exit: compassCode === compass_refusals_1.COMPASS_REVISION_NOT_FOUND ? 4 : 1,
|
|
1616
|
+
retryable: false,
|
|
1617
|
+
details: envelopeDetails(body),
|
|
1618
|
+
});
|
|
1619
|
+
}
|
|
1582
1620
|
else if (bodyCode === 'ASSIGNEE_NOT_IN_SPACE') {
|
|
1583
1621
|
// Code-specific enrichment takes precedence over the status heuristic:
|
|
1584
1622
|
// the server rejects a non-member assignee by id with no next step, so
|
|
@@ -2254,6 +2292,40 @@ function apiRefusal(err) {
|
|
|
2254
2292
|
...(body ? { body } : {}),
|
|
2255
2293
|
};
|
|
2256
2294
|
}
|
|
2295
|
+
/** The typed code the server refuses a Commitment activation with. */
|
|
2296
|
+
const COMMITMENT_ACTIVATION_BLOCKED = 'COMMITMENT_ACTIVATION_BLOCKED';
|
|
2297
|
+
/**
|
|
2298
|
+
* The remedy a refused Commitment activation carries.
|
|
2299
|
+
*
|
|
2300
|
+
* The refusal's message names the blocking Preflight gate by its QUESTION and a reason token
|
|
2301
|
+
* ("Can the runtime enforce this Run? (runtime_commitment_contract_unavailable)"). The gate's own
|
|
2302
|
+
* `hint` — the sentence that says what to run — travels beside it at
|
|
2303
|
+
* `diagnostic.firstBlocker.detail`, and the web client already shows it
|
|
2304
|
+
* (`activationFailureMessage`). The CLI dropped it, so `commitments resume` and
|
|
2305
|
+
* `commitments create --activate` told a person to "fix the gate the reason names" while
|
|
2306
|
+
* `commitments preflight` knew the exact command (OSK-21126).
|
|
2307
|
+
*
|
|
2308
|
+
* Undefined when the gate offered none, so a caller keeps its generic wording.
|
|
2309
|
+
*
|
|
2310
|
+
* Exported for tests.
|
|
2311
|
+
*/
|
|
2312
|
+
function commitmentActivationHint(body) {
|
|
2313
|
+
const diagnostic = body?.diagnostic;
|
|
2314
|
+
if (!diagnostic || typeof diagnostic !== 'object')
|
|
2315
|
+
return undefined;
|
|
2316
|
+
const blocker = diagnostic.firstBlocker;
|
|
2317
|
+
if (!blocker || typeof blocker !== 'object')
|
|
2318
|
+
return undefined;
|
|
2319
|
+
const detail = blocker.detail;
|
|
2320
|
+
if (!detail || typeof detail !== 'object')
|
|
2321
|
+
return undefined;
|
|
2322
|
+
const { hint, note } = detail;
|
|
2323
|
+
for (const candidate of [hint, note]) {
|
|
2324
|
+
if (typeof candidate === 'string' && candidate.trim())
|
|
2325
|
+
return candidate.trim();
|
|
2326
|
+
}
|
|
2327
|
+
return undefined;
|
|
2328
|
+
}
|
|
2257
2329
|
/**
|
|
2258
2330
|
* Prefix a failure message with the resource it is about.
|
|
2259
2331
|
*
|
|
@@ -336,6 +336,13 @@ function noStreamJsonPayload(input) {
|
|
|
336
336
|
// The run first failed as handed over and the machine's reply was then
|
|
337
337
|
// repaired onto the record (OSK-13543).
|
|
338
338
|
...(result.repaired ? { repaired: true } : {}),
|
|
339
|
+
// Tool calls refused for want of `--approve-tools`: the turn completed, and these did not run.
|
|
340
|
+
...(result.permissionDenials?.length
|
|
341
|
+
? {
|
|
342
|
+
permissionDenials: result.permissionDenials,
|
|
343
|
+
permissionDenialHint: 'Re-run with --approve-tools to allow these tool calls.',
|
|
344
|
+
}
|
|
345
|
+
: {}),
|
|
339
346
|
response: result.text,
|
|
340
347
|
// The model a local harness reported running (OSK-12111). Absent on a
|
|
341
348
|
// cloud turn and on a harness that reports none.
|
|
@@ -8,8 +8,8 @@ import { BaseCommand } from '../../../base-command';
|
|
|
8
8
|
* Wraps `GET /api/agents/:agent_id/goals` (data-provider `listAgentGoals`),
|
|
9
9
|
* which returns goals where the agent is owner, DRI, or a contributor, each
|
|
10
10
|
* enriched with `_agentGoalRole` (which of the three) and `_completionPercent`.
|
|
11
|
-
* The endpoint sorts by most-recently-updated and
|
|
12
|
-
*
|
|
11
|
+
* The endpoint sorts by most-recently-updated and supports cursor pagination.
|
|
12
|
+
* This command currently shows its first page of up to 100 goals.
|
|
13
13
|
*/
|
|
14
14
|
export default class AgentsGoalsList extends BaseCommand {
|
|
15
15
|
static description: string;
|
|
@@ -14,8 +14,8 @@ const format_1 = require("../../../lib/format");
|
|
|
14
14
|
* Wraps `GET /api/agents/:agent_id/goals` (data-provider `listAgentGoals`),
|
|
15
15
|
* which returns goals where the agent is owner, DRI, or a contributor, each
|
|
16
16
|
* enriched with `_agentGoalRole` (which of the three) and `_completionPercent`.
|
|
17
|
-
* The endpoint sorts by most-recently-updated and
|
|
18
|
-
*
|
|
17
|
+
* The endpoint sorts by most-recently-updated and supports cursor pagination.
|
|
18
|
+
* This command currently shows its first page of up to 100 goals.
|
|
19
19
|
*/
|
|
20
20
|
class AgentsGoalsList extends base_command_1.BaseCommand {
|
|
21
21
|
static description = 'List the goals an agent owns, is DRI for, or contributes to';
|
|
@@ -42,8 +42,8 @@ class CommitmentsActionHistory extends base_command_1.BaseCommand {
|
|
|
42
42
|
this.handleApiError(err);
|
|
43
43
|
}
|
|
44
44
|
if (flags.json) {
|
|
45
|
+
// One JSON document and nothing else: has_more / next_cursor are in it.
|
|
45
46
|
this.log(JSON.stringify(response, null, 2));
|
|
46
|
-
(0, commitments_1.renderListPageHint)(response, (line) => this.log(line));
|
|
47
47
|
return;
|
|
48
48
|
}
|
|
49
49
|
(0, commitment_ledgers_1.renderActionHistory)(response, (line) => this.log(line));
|
|
@@ -31,11 +31,11 @@ class CommitmentsCases extends base_command_1.BaseCommand {
|
|
|
31
31
|
this.handleApiError(error);
|
|
32
32
|
}
|
|
33
33
|
if (flags.json) {
|
|
34
|
+
// One JSON document and nothing else: has_more / next_cursor are in it.
|
|
34
35
|
this.log(JSON.stringify(result, null, 2));
|
|
36
|
+
return;
|
|
35
37
|
}
|
|
36
|
-
|
|
37
|
-
(0, commitments_1.renderCommitmentCases)(result, (line) => this.log(line));
|
|
38
|
-
}
|
|
38
|
+
(0, commitments_1.renderCommitmentCases)(result, (line) => this.log(line));
|
|
39
39
|
(0, commitments_1.renderListPageHint)(result, (line) => this.log(line));
|
|
40
40
|
}
|
|
41
41
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
-
/** Render runtime/Always-On blockers returned alongside
|
|
3
|
-
export declare function manualCheckBlockerLines(result: Record<string, unknown
|
|
2
|
+
/** Render runtime/Always-On blockers returned alongside a Check (accepted, or skipped). */
|
|
3
|
+
export declare function manualCheckBlockerLines(result: Record<string, unknown>, heading?: string): string[];
|
|
4
|
+
/**
|
|
5
|
+
* Why a Check was not queued, in words, and what to do. The server answers with a reason code
|
|
6
|
+
* (`commitment_draft`, `commitment_paused`, ...) and the CLI used to print only that, so "Check
|
|
7
|
+
* skipped / Reason: commitment_draft" gave no hint that the Commitment simply was not turned on
|
|
8
|
+
* (dogfood df-261031-commitment-authoring, F11). The code stays in parentheses so it can be
|
|
9
|
+
* searched for and quoted. `null` for a reason this does not know: the caller prints it as it came.
|
|
10
|
+
*/
|
|
11
|
+
export declare function describeNotQueuedReason(reason: unknown, id: string, bin?: string): string | null;
|
|
4
12
|
/**
|
|
5
13
|
* `skrr commitments check-now <id>` — run one control-loop iteration immediately.
|
|
6
14
|
*
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.manualCheckBlockerLines = manualCheckBlockerLines;
|
|
4
|
+
exports.describeNotQueuedReason = describeNotQueuedReason;
|
|
4
5
|
const core_1 = require("@oclif/core");
|
|
5
6
|
const base_command_1 = require("../../base-command");
|
|
6
7
|
const web_url_1 = require("../../lib/web-url");
|
|
7
8
|
const commitments_1 = require("../../lib/commitments");
|
|
8
|
-
|
|
9
|
-
|
|
9
|
+
const ACCEPTED_CHECK_BLOCKER_HEADING = 'Warning: this check can record state, but autonomous agent work is blocked:';
|
|
10
|
+
/** Render runtime/Always-On blockers returned alongside a Check (accepted, or skipped). */
|
|
11
|
+
function manualCheckBlockerLines(result, heading = ACCEPTED_CHECK_BLOCKER_HEADING) {
|
|
10
12
|
const blockers = Array.isArray(result.executionBlockers)
|
|
11
13
|
? result.executionBlockers
|
|
12
14
|
: [];
|
|
13
15
|
if (!blockers.length)
|
|
14
16
|
return [];
|
|
15
|
-
const lines = [
|
|
17
|
+
const lines = [heading];
|
|
16
18
|
for (const blocker of blockers) {
|
|
17
19
|
const label = typeof blocker.label === 'string' && blocker.label ? blocker.label : 'Execution';
|
|
18
20
|
const reason = typeof blocker.reason === 'string' && blocker.reason ? ` — ${blocker.reason}` : '';
|
|
@@ -23,6 +25,40 @@ function manualCheckBlockerLines(result) {
|
|
|
23
25
|
}
|
|
24
26
|
return lines;
|
|
25
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Why a Check was not queued, in words, and what to do. The server answers with a reason code
|
|
30
|
+
* (`commitment_draft`, `commitment_paused`, ...) and the CLI used to print only that, so "Check
|
|
31
|
+
* skipped / Reason: commitment_draft" gave no hint that the Commitment simply was not turned on
|
|
32
|
+
* (dogfood df-261031-commitment-authoring, F11). The code stays in parentheses so it can be
|
|
33
|
+
* searched for and quoted. `null` for a reason this does not know: the caller prints it as it came.
|
|
34
|
+
*/
|
|
35
|
+
function describeNotQueuedReason(reason, id, bin = 'skrr') {
|
|
36
|
+
if (typeof reason !== 'string' || !reason)
|
|
37
|
+
return null;
|
|
38
|
+
const resume = `Turn it on with \`${bin} commitments resume ${id}\`.`;
|
|
39
|
+
const sentence = (text) => `${text} (${reason})`;
|
|
40
|
+
switch (reason) {
|
|
41
|
+
case 'commitment_draft':
|
|
42
|
+
return sentence(`Nothing was queued: this Commitment is a draft, so it never runs. ${resume}`);
|
|
43
|
+
case 'commitment_paused':
|
|
44
|
+
return sentence(`Nothing was queued: this Commitment is paused. ${resume}`);
|
|
45
|
+
case 'commitment_archived':
|
|
46
|
+
case 'commitment_completed':
|
|
47
|
+
case 'commitment_cancelled':
|
|
48
|
+
return sentence(`Nothing was queued: this Commitment is ${reason.slice('commitment_'.length)}, so it no longer runs.`);
|
|
49
|
+
case 'commitment_deleted':
|
|
50
|
+
return sentence('Nothing was queued: this Commitment was deleted.');
|
|
51
|
+
case 'commitment_check_not_reserved':
|
|
52
|
+
return sentence('Nothing was queued: the platform did not reserve a new check for it just now. Try again shortly.');
|
|
53
|
+
case 'reservation_exists':
|
|
54
|
+
// Two requests at once: one reserves the check, the other meets that reservation.
|
|
55
|
+
return sentence(`Nothing new was queued: a check for this Commitment is already queued or running. See it with \`${bin} commitments checks ${id}\`.`);
|
|
56
|
+
case 'queue_unavailable':
|
|
57
|
+
return sentence('Nothing was queued: the check queue is unavailable right now. Try again shortly.');
|
|
58
|
+
default:
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
26
62
|
/**
|
|
27
63
|
* `skrr commitments check-now <id>` — run one control-loop iteration immediately.
|
|
28
64
|
*
|
|
@@ -60,6 +96,20 @@ class CommitmentsCheckNow extends base_command_1.BaseCommand {
|
|
|
60
96
|
const status = String(result.status ?? 'unknown');
|
|
61
97
|
const checkId = result.checkId ? String(result.checkId) : null;
|
|
62
98
|
this.log(`Check ${status}${checkId ? ` — ${checkId}` : ''}`);
|
|
99
|
+
if (status !== 'queued') {
|
|
100
|
+
const sentence = describeNotQueuedReason(result.reason, args.id, this.config.bin);
|
|
101
|
+
if (sentence)
|
|
102
|
+
this.log(sentence);
|
|
103
|
+
else if (result.reason)
|
|
104
|
+
this.log(`Reason: ${String(result.reason)}`);
|
|
105
|
+
// What would stop it from running once it is on, so the fix is not discovered one try at a time.
|
|
106
|
+
const blockerLines = manualCheckBlockerLines(result, 'When it is on, these will also keep agent work from running:');
|
|
107
|
+
if (blockerLines.length)
|
|
108
|
+
this.log('');
|
|
109
|
+
for (const line of blockerLines)
|
|
110
|
+
this.log(line);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
63
113
|
if (result.reason)
|
|
64
114
|
this.log(`Reason: ${String(result.reason)}`);
|
|
65
115
|
if (status === 'queued') {
|
|
@@ -20,19 +20,10 @@ export declare function createCadenceFromFlags({ eventDriven, cadence: cadenceIn
|
|
|
20
20
|
cadenceMode?: string;
|
|
21
21
|
timezone?: string;
|
|
22
22
|
}): Record<string, unknown> | undefined;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
* be said out loud at the CLI boundary; otherwise a successful create response
|
|
28
|
-
* reads like the requested autonomy is live when something else is running.
|
|
29
|
-
*/
|
|
30
|
-
/**
|
|
31
|
-
* A refused `--activate` leaves a draft, and the draft's id is the answer the
|
|
32
|
-
* caller needs — so the refusal is reported on the result, never thrown over
|
|
33
|
-
* it. `{code, reason, fix}` mirrors the MCP surface's refusal; `guide` points
|
|
34
|
-
* at the section that explains the code when one does (resolved offline).
|
|
35
|
-
*/
|
|
23
|
+
export type ActivationBlocker = {
|
|
24
|
+
gate: string;
|
|
25
|
+
reason: string;
|
|
26
|
+
};
|
|
36
27
|
export declare function commitmentActivationRefusal(err: unknown, { id, bin }: {
|
|
37
28
|
id: string;
|
|
38
29
|
bin: string;
|
|
@@ -41,6 +32,7 @@ export declare function commitmentActivationRefusal(err: unknown, { id, bin }: {
|
|
|
41
32
|
reason: string;
|
|
42
33
|
fix: string;
|
|
43
34
|
guide?: AgentGuidePointer;
|
|
35
|
+
blockers?: ActivationBlocker[];
|
|
44
36
|
};
|
|
45
37
|
/**
|
|
46
38
|
* `skrr commitments create` — author a durable commitment contract.
|
|
@@ -89,7 +81,7 @@ export default class CommitmentsCreate extends BaseCommand {
|
|
|
89
81
|
repo: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
90
82
|
base: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
91
83
|
isolation: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
92
|
-
'max-nudges': import("@oclif/core/interfaces").OptionFlag<
|
|
84
|
+
'max-nudges': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
93
85
|
'max-concurrent-runs': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
94
86
|
'concurrency-reason': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
95
87
|
};
|
|
@@ -10,6 +10,7 @@ const base_command_1 = require("../../base-command");
|
|
|
10
10
|
const guide_pointer_1 = require("../../lib/guide-pointer");
|
|
11
11
|
const web_url_1 = require("../../lib/web-url");
|
|
12
12
|
const prompt_1 = require("../../lib/prompt");
|
|
13
|
+
const commitment_attention_copy_1 = require("../../lib/commitment-attention-copy");
|
|
13
14
|
const triggers_1 = require("../../lib/triggers");
|
|
14
15
|
const commitment_product_1 = require("../../lib/commitment-product");
|
|
15
16
|
const commitments_1 = require("../../lib/commitments");
|
|
@@ -153,8 +154,27 @@ function createCadenceFromFlags({ eventDriven, cadence: cadenceInput, cadenceMod
|
|
|
153
154
|
* it. `{code, reason, fix}` mirrors the MCP surface's refusal; `guide` points
|
|
154
155
|
* at the section that explains the code when one does (resolved offline).
|
|
155
156
|
*/
|
|
157
|
+
/** Everything that blocks an activation, when the server listed more than the first gate. */
|
|
158
|
+
function activationBlockers(body) {
|
|
159
|
+
const diagnostic = body?.diagnostic;
|
|
160
|
+
if (!Array.isArray(diagnostic?.blockers))
|
|
161
|
+
return [];
|
|
162
|
+
const blockers = [];
|
|
163
|
+
for (const item of diagnostic.blockers) {
|
|
164
|
+
const gate = item?.gate;
|
|
165
|
+
const reason = item?.reason;
|
|
166
|
+
if (typeof gate === 'string' && gate && typeof reason === 'string' && reason) {
|
|
167
|
+
blockers.push({ gate, reason });
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return blockers;
|
|
171
|
+
}
|
|
156
172
|
function commitmentActivationRefusal(err, { id, bin }) {
|
|
157
173
|
const refusal = (0, base_command_1.apiRefusal)(err);
|
|
174
|
+
// `resume` answers with every blocking gate, but only the first is in the message. Fixing it
|
|
175
|
+
// revealed the next one on the following try (Always-On, then the runtime), so the refusal says
|
|
176
|
+
// all of them when there is more than one (dogfood df-261031-commitment-authoring, F12).
|
|
177
|
+
const blockers = activationBlockers(refusal.body);
|
|
158
178
|
const errorField = refusal.body?.error;
|
|
159
179
|
const code = refusal.code ||
|
|
160
180
|
(typeof errorField === 'string' && /^[A-Za-z][A-Za-z0-9_]*$/.test(errorField)
|
|
@@ -162,11 +182,17 @@ function commitmentActivationRefusal(err, { id, bin }) {
|
|
|
162
182
|
: undefined) ||
|
|
163
183
|
'COMMITMENT_ACTIVATION_REFUSED';
|
|
164
184
|
const guide = (0, guide_pointer_1.agentGuidePointerFor)(code);
|
|
185
|
+
// The blocking gate's own remedy beats the generic "fix the gate the reason names": the reason is
|
|
186
|
+
// a token, and the sentence that says what to run is the hint beside it (OSK-21126).
|
|
187
|
+
const hint = (0, base_command_1.commitmentActivationHint)(refusal.body);
|
|
165
188
|
return {
|
|
166
189
|
code,
|
|
167
190
|
reason: refusal.message || (err instanceof Error ? err.message : 'Activation was refused.'),
|
|
168
|
-
fix:
|
|
191
|
+
fix: hint
|
|
192
|
+
? `${hint} Then run \`${bin} commitments resume ${id}\`.`
|
|
193
|
+
: (0, data_provider_1.describeCommitmentActivationFix)(code === 'COMMITMENT_ACTIVATION_REFUSED' ? undefined : code, { turnOn: `run \`${bin} commitments resume ${id}\`` }),
|
|
169
194
|
...(guide ? { guide } : {}),
|
|
195
|
+
...(blockers.length > 1 ? { blockers } : {}),
|
|
170
196
|
};
|
|
171
197
|
}
|
|
172
198
|
/**
|
|
@@ -210,6 +236,7 @@ cat > commitment.json <<'JSON'
|
|
|
210
236
|
}],
|
|
211
237
|
"policy": {
|
|
212
238
|
"maxUserNudgesPerDay": 0,
|
|
239
|
+
"quietHours": { "enabled": true, "start": "22:00", "end": "07:00", "timezone": "Asia/Shanghai" },
|
|
213
240
|
"executionBudget": { "maxRunsPerDay": 0, "maxObservationRunsPerDay": 0, "maxCostUsdPerDay": 0 }
|
|
214
241
|
}
|
|
215
242
|
}
|
|
@@ -217,11 +244,25 @@ JSON
|
|
|
217
244
|
<%= config.bin %> commitments preflight --from-json commitment.json
|
|
218
245
|
<%= config.bin %> commitments create --from-json commitment.json --json`,
|
|
219
246
|
'<%= config.bin %> commitments create --agent <agent-id> --title "Keep CI healthy" --outcome "Failed checks are repaired" --watch github --github-event check_run_failed --event-driven --delivery draft-pr --repo owner/repo --base main --status draft',
|
|
247
|
+
`# Govern a Commitment with a Compass. There is no flag: "compassId" goes in the --from-json body.
|
|
248
|
+
# It must be an active Compass of the same Agent (a draft may name an archived one).
|
|
249
|
+
# The binding cannot be changed later: bind a successor Commitment to another Compass instead.
|
|
250
|
+
cat > commitment.json <<'JSON'
|
|
251
|
+
{
|
|
252
|
+
"agentId": "<agent-id>",
|
|
253
|
+
"compassId": "<compass-id>",
|
|
254
|
+
"title": "Keep p99 under 300 ms",
|
|
255
|
+
"status": "draft",
|
|
256
|
+
"target": { "outcome": "p99 API latency stays under 300 ms" }
|
|
257
|
+
}
|
|
258
|
+
JSON
|
|
259
|
+
<%= config.bin %> commitments create --from-json commitment.json --json
|
|
260
|
+
<%= config.bin %> compass commitments <compass-id> --agent <agent-id> # lists it once it is bound`,
|
|
220
261
|
];
|
|
221
262
|
static flags = {
|
|
222
263
|
json: core_1.Flags.boolean({ description: 'Output the created commitment as JSON' }),
|
|
223
264
|
'from-json': core_1.Flags.string({
|
|
224
|
-
description:
|
|
265
|
+
description: `JSON file (or "-" for stdin) for fields not exposed as flags. See examples below for daily times, named agent_run keys, zero budgets and "compassId" (binds a Compass; fixed at creation). ${commitment_attention_copy_1.QUIET_HOURS_HELP} ${commitment_attention_copy_1.EVENT_SOURCES_HELP}`,
|
|
225
266
|
}),
|
|
226
267
|
agent: core_1.Flags.string({ description: 'Agent that owns and watches this commitment (required)' }),
|
|
227
268
|
kind: core_1.Flags.string({
|
|
@@ -321,7 +362,7 @@ JSON
|
|
|
321
362
|
options: [...commitments_1.COMMITMENT_EXECUTION_ISOLATION_MODES],
|
|
322
363
|
hidden: true,
|
|
323
364
|
}),
|
|
324
|
-
'max-nudges': core_1.Flags.
|
|
365
|
+
'max-nudges': core_1.Flags.string({ description: commitment_attention_copy_1.MAX_NUDGES_FLAG_HELP }),
|
|
325
366
|
'max-concurrent-runs': core_1.Flags.string({
|
|
326
367
|
description: 'Owner concurrency policy: "unlimited" (default) or 1..100. Finite values require --concurrency-reason and cannot be activated by an autonomous Agent session.',
|
|
327
368
|
}),
|
|
@@ -337,7 +378,11 @@ JSON
|
|
|
337
378
|
// `createBodySchema` is a z.strictObject, so unwrap a `plan --json`
|
|
338
379
|
// envelope and drop any key it does not declare (a contract dumped from
|
|
339
380
|
// `list --json` carries id/version/state/… and would otherwise 400).
|
|
340
|
-
|
|
381
|
+
const rawBody = await (0, prompt_1.readJsonBody)(flags['from-json']);
|
|
382
|
+
payload = (0, commitments_1.normalizeCreateBody)(rawBody);
|
|
383
|
+
const ignoredWarning = (0, commitments_1.ignoredCreateKeysWarning)((0, commitments_1.ignoredCreateKeys)(rawBody));
|
|
384
|
+
if (ignoredWarning)
|
|
385
|
+
this.warn(ignoredWarning);
|
|
341
386
|
if (flags.agent)
|
|
342
387
|
payload.agentId = flags.agent;
|
|
343
388
|
if (flags.kind)
|
|
@@ -430,8 +475,9 @@ JSON
|
|
|
430
475
|
(0, commitment_product_1.normalizeTaskReviewPolicy)(declaredDelivery.reviewPolicy);
|
|
431
476
|
if (flags.isolation)
|
|
432
477
|
policy.executionIsolation = flags.isolation;
|
|
433
|
-
|
|
434
|
-
|
|
478
|
+
const maxNudges = (0, commitment_attention_copy_1.parseMaxNudgesFlag)(flags['max-nudges']);
|
|
479
|
+
if (maxNudges !== undefined)
|
|
480
|
+
policy.maxUserNudgesPerDay = maxNudges;
|
|
435
481
|
const concurrencyPatch = (0, commitments_1.commitmentConcurrencyEnvelopePatch)({
|
|
436
482
|
rawValue: flags['max-concurrent-runs'],
|
|
437
483
|
reason: flags['concurrency-reason'],
|
|
@@ -446,6 +492,9 @@ JSON
|
|
|
446
492
|
}
|
|
447
493
|
if (Object.keys(policy).length)
|
|
448
494
|
payload.policy = policy;
|
|
495
|
+
const quietHoursProblem = (0, commitment_attention_copy_1.quietHoursInputProblem)(policy);
|
|
496
|
+
if (quietHoursProblem)
|
|
497
|
+
throw new Error(quietHoursProblem);
|
|
449
498
|
if (payload.createdVia === undefined) {
|
|
450
499
|
payload.createdVia = process.env.OVERSKY_AUTONOMOUS_SESSION === '1' ? 'agent_cli' : 'cli';
|
|
451
500
|
}
|
|
@@ -533,6 +582,12 @@ JSON
|
|
|
533
582
|
if (activation && !activation.activated) {
|
|
534
583
|
const { refusal } = activation;
|
|
535
584
|
this.warn(`Activation refused (${refusal.code}): ${refusal.reason}`);
|
|
585
|
+
if (refusal.blockers) {
|
|
586
|
+
this.warn(`All ${refusal.blockers.length} things blocking it (it stays a draft until each is fixed):`);
|
|
587
|
+
for (const blocker of refusal.blockers)
|
|
588
|
+
this.warn(` - ${blocker.gate}: ${blocker.reason}`);
|
|
589
|
+
this.warn(`Details: ${this.config.bin} commitments preflight ${String(commitment?.id)}`);
|
|
590
|
+
}
|
|
536
591
|
if (refusal.fix)
|
|
537
592
|
this.warn(`Fix: ${refusal.fix}`);
|
|
538
593
|
if (refusal.guide)
|
|
@@ -86,7 +86,9 @@ function spaceLoopLine(value) {
|
|
|
86
86
|
if (status === 'admitted') {
|
|
87
87
|
detail = loop.principalVersion ? ` (principal v${loop.principalVersion})` : '';
|
|
88
88
|
}
|
|
89
|
-
|
|
89
|
+
// Said in words first, with the code kept in parentheses for anyone who searches by it.
|
|
90
|
+
const said = status !== 'admitted' && loop.detail ? ` — ${loop.detail}` : '';
|
|
91
|
+
return `Space loop: ${status}${loop.spaceId ? ` · ${loop.spaceId}` : ''}${said}${detail}`;
|
|
90
92
|
}
|
|
91
93
|
class CommitmentsDoctor extends base_command_1.BaseCommand {
|
|
92
94
|
static description = 'Summarize commitment readiness and the next operator action';
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* `2026-11-02 00:00` for an instant, in a time zone. Built from parts, never from a locale: `02/11/2026`
|
|
4
|
+
* is the 2nd of November to one reader and the 11th of February to the next, and the budget reset is
|
|
5
|
+
* exactly the date a person reads this line for.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isoWallClock(instant: unknown, timeZone?: string): string | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* `stored` is the Commitment's own policy, read beside the effective one: the no-reply back-off
|
|
10
|
+
* is not part of the effective-policy answer, so without it that line is left out, not guessed.
|
|
11
|
+
*/
|
|
12
|
+
export declare function effectivePolicyLines(policy: Record<string, any>, stored?: Record<string, any> | null): string[];
|
|
3
13
|
/** Show the policy actually governing a Commitment without changing it. */
|
|
4
14
|
export default class CommitmentsEffectivePolicy extends BaseCommand {
|
|
5
15
|
static description: string;
|