@skrr-ai/cli 0.1.127 → 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/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/planner-config.d.ts +11 -0
- package/dist/commands/goals/planner-config.js +16 -0
- 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/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/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 +1 -1
- package/dist/lib/triggers.js +11 -3
- package/dist/node_modules/@skrr-ai/data-provider/index.js +6504 -6493
- package/oclif.manifest.json +350 -34
- package/package.json +4 -4
package/dist/lib/command-miss.js
CHANGED
|
@@ -359,8 +359,17 @@ function nearestCommands(id, all, limit = 3) {
|
|
|
359
359
|
// A third of the length keeps this to genuine near-misses; without a bound
|
|
360
360
|
// every miss suggests three unrelated commands, which is noise wearing the
|
|
361
361
|
// shape of help.
|
|
362
|
-
|
|
363
|
-
|
|
362
|
+
// A command NESTED under what was typed (`northstars:intents` -> `northstars:intents:list`)
|
|
363
|
+
// is the correction the reader was reaching for, whatever its edit distance, so it is
|
|
364
|
+
// neither bound-filtered nor ranked by distance. Distance alone kept `intents:decide` out
|
|
365
|
+
// and put shorter siblings ahead of `intents:list`, so `northstars intents <id>` was
|
|
366
|
+
// answered with "Did you mean: northstars create, northstars delete, northstars intents
|
|
367
|
+
// list?" and the first guess a reader acts on was a destructive one.
|
|
368
|
+
.filter(({ d, candidate }) => candidate.startsWith(`${id}:`) || d <= Math.max(2, Math.floor(id.length / 3)))
|
|
369
|
+
.sort((x, y) => {
|
|
370
|
+
const nested = (c) => (c.startsWith(`${id}:`) ? 0 : 1);
|
|
371
|
+
return nested(x.candidate) - nested(y.candidate) || x.d - y.d;
|
|
372
|
+
})
|
|
364
373
|
.slice(0, limit)
|
|
365
374
|
.map(({ candidate }) => candidate));
|
|
366
375
|
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The attention settings of a Commitment, said in words.
|
|
3
|
+
*
|
|
4
|
+
* `show`, `effective-policy` and `preflight` all print these, from this one
|
|
5
|
+
* file, so the three cannot phrase a setting three ways. A value is described
|
|
6
|
+
* from the RAW stored setting and never through a coercion: `null` is "no
|
|
7
|
+
* limit" and `0` is "muted", and `Number(null)` is `0`, which is exactly how a
|
|
8
|
+
* cap that was never set would read as a mute (the server documents the same
|
|
9
|
+
* trap at `policySchema.maxUserNudgesPerDay` in api/server/routes/commitments.js).
|
|
10
|
+
*/
|
|
11
|
+
/** What the owner can type for `--max-nudges`, as `--help` prints it. */
|
|
12
|
+
export declare const MAX_NUDGES_FLAG_HELP: string;
|
|
13
|
+
/**
|
|
14
|
+
* Parse `--max-nudges`: a whole number, or the word `unlimited` (stored as `null`).
|
|
15
|
+
* The server accepts 0 up to MAX_SAFE_INTEGER, so there is no 0..20 ceiling here.
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseMaxNudgesFlag(raw: string | undefined): number | null | undefined;
|
|
18
|
+
/** `null` and an unset cap are "no daily limit"; `0` is a mute; N is "at most N a day". */
|
|
19
|
+
export declare function describeNudgeCap(value: unknown): string;
|
|
20
|
+
/** The least time the platform leaves between two nudges to you. */
|
|
21
|
+
export declare function describeNudgeGap(ms: unknown): string;
|
|
22
|
+
/** The back-off for a person who is not answering. The server's default count is 2. */
|
|
23
|
+
export declare function describeNoReplyRule(noReply: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
afterNudges?: number;
|
|
26
|
+
} | null | undefined): string;
|
|
27
|
+
export type NudgeSettings = {
|
|
28
|
+
/** `null` = unlimited, `0` = muted. `undefined` = never set, which is unlimited. */
|
|
29
|
+
maxUserNudgesPerDay?: number | null;
|
|
30
|
+
minUserNudgeIntervalMs?: number | null;
|
|
31
|
+
noReply?: {
|
|
32
|
+
enabled?: boolean;
|
|
33
|
+
afterNudges?: number;
|
|
34
|
+
} | null;
|
|
35
|
+
};
|
|
36
|
+
/** Read the three settings out of a stored `policy`. */
|
|
37
|
+
export declare function nudgeSettingsFromPolicy(policy: Record<string, any>): NudgeSettings;
|
|
38
|
+
/**
|
|
39
|
+
* The lines that say how much a Commitment may interrupt its owner. A muted one
|
|
40
|
+
* prints the mute alone: a gap and a back-off on a Commitment that never speaks
|
|
41
|
+
* would only suggest it does. `withNoReply: false` leaves the back-off out when the
|
|
42
|
+
* stored rule could not be read, rather than claim it is off.
|
|
43
|
+
*/
|
|
44
|
+
export declare function nudgeSettingLines(settings: NudgeSettings, { withNoReply }?: {
|
|
45
|
+
withNoReply?: boolean;
|
|
46
|
+
}): string[];
|
|
47
|
+
/** What a time of day is, in the words a person can type. */
|
|
48
|
+
export declare const CLOCK_TIME_HINT = "use HH:MM, 00:00-23:59, for example 22:00";
|
|
49
|
+
/**
|
|
50
|
+
* How to set quiet hours, for the `--from-json` help of `create` and `update`. There is no flag for
|
|
51
|
+
* them, so without this sentence a user had to know the `policy.quietHours` key from outside the CLI
|
|
52
|
+
* (OSK-21123).
|
|
53
|
+
*/
|
|
54
|
+
export declare const QUIET_HOURS_HELP: string;
|
|
55
|
+
/**
|
|
56
|
+
* What `policy.executionBudget.allowedEventSources` means, for the same help. An empty list is the
|
|
57
|
+
* unrestricted case, which is the opposite of what `[]` suggests (OSK-21113).
|
|
58
|
+
*/
|
|
59
|
+
export declare const EVENT_SOURCES_HELP: string;
|
|
60
|
+
/**
|
|
61
|
+
* The allowlist in words. An empty or missing list means every source is allowed
|
|
62
|
+
* (`sourceAllowed` in api/server/services/EventBus/commitmentWakeCapabilities.js and the check in
|
|
63
|
+
* WakeIntentResolver.js both treat it so); the stored form of "never set" is the same `[]`.
|
|
64
|
+
*/
|
|
65
|
+
export declare function describeEventSources(allowed: unknown): string;
|
|
66
|
+
/**
|
|
67
|
+
* The problem with the quiet-hours times in a `policy` about to be sent, or null. The server
|
|
68
|
+
* answered a bad time with its raw pattern (`must match pattern /^\d{1,2}:\d{2}$/`), which says
|
|
69
|
+
* nothing a person can type, and accepted `25:00` (a window that then never applies). Checked
|
|
70
|
+
* here, so the request never leaves with either.
|
|
71
|
+
*/
|
|
72
|
+
export declare function quietHoursInputProblem(policy: unknown): string | null;
|
|
73
|
+
type QuietHoursPolicy = {
|
|
74
|
+
enabled?: boolean;
|
|
75
|
+
start?: string;
|
|
76
|
+
end?: string;
|
|
77
|
+
timezone?: string;
|
|
78
|
+
} | null;
|
|
79
|
+
/**
|
|
80
|
+
* The Commitment's own quiet-hours window in words. Without one of its own a Commitment uses its
|
|
81
|
+
* Agent's Compass window (`scheduleIssues.js` documents the same rule), so "none set" says where
|
|
82
|
+
* else a window may come from rather than claiming there is no quiet time at all.
|
|
83
|
+
*/
|
|
84
|
+
export declare function describeQuietHours(quietHours: QuietHoursPolicy | undefined): string;
|
|
85
|
+
/**
|
|
86
|
+
* When a held message is released, for the preflight line "Held until your quiet hours end".
|
|
87
|
+
* The report's `quietHours` carries the end as typed, the zone, and the exact instant (`endsAt`,
|
|
88
|
+
* daylight saving included); null when it carries no instant, so the caller keeps its generic
|
|
89
|
+
* sentence rather than inventing a time.
|
|
90
|
+
*/
|
|
91
|
+
export declare function describeQuietHoursEnd(quietHours: {
|
|
92
|
+
end?: string;
|
|
93
|
+
endsAt?: string | null;
|
|
94
|
+
timezone?: string;
|
|
95
|
+
} | null | undefined): string | null;
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The attention settings of a Commitment, said in words.
|
|
4
|
+
*
|
|
5
|
+
* `show`, `effective-policy` and `preflight` all print these, from this one
|
|
6
|
+
* file, so the three cannot phrase a setting three ways. A value is described
|
|
7
|
+
* from the RAW stored setting and never through a coercion: `null` is "no
|
|
8
|
+
* limit" and `0` is "muted", and `Number(null)` is `0`, which is exactly how a
|
|
9
|
+
* cap that was never set would read as a mute (the server documents the same
|
|
10
|
+
* trap at `policySchema.maxUserNudgesPerDay` in api/server/routes/commitments.js).
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.EVENT_SOURCES_HELP = exports.QUIET_HOURS_HELP = exports.CLOCK_TIME_HINT = exports.MAX_NUDGES_FLAG_HELP = void 0;
|
|
14
|
+
exports.parseMaxNudgesFlag = parseMaxNudgesFlag;
|
|
15
|
+
exports.describeNudgeCap = describeNudgeCap;
|
|
16
|
+
exports.describeNudgeGap = describeNudgeGap;
|
|
17
|
+
exports.describeNoReplyRule = describeNoReplyRule;
|
|
18
|
+
exports.nudgeSettingsFromPolicy = nudgeSettingsFromPolicy;
|
|
19
|
+
exports.nudgeSettingLines = nudgeSettingLines;
|
|
20
|
+
exports.describeEventSources = describeEventSources;
|
|
21
|
+
exports.quietHoursInputProblem = quietHoursInputProblem;
|
|
22
|
+
exports.describeQuietHours = describeQuietHours;
|
|
23
|
+
exports.describeQuietHoursEnd = describeQuietHoursEnd;
|
|
24
|
+
/** What the owner can type for `--max-nudges`, as `--help` prints it. */
|
|
25
|
+
exports.MAX_NUDGES_FLAG_HELP = 'Daily limit on messages that interrupt you: a whole number from 0 up (0 mutes it: ' +
|
|
26
|
+
'it never contacts you) or "unlimited" to remove the limit. Left unset it is unlimited';
|
|
27
|
+
/**
|
|
28
|
+
* Parse `--max-nudges`: a whole number, or the word `unlimited` (stored as `null`).
|
|
29
|
+
* The server accepts 0 up to MAX_SAFE_INTEGER, so there is no 0..20 ceiling here.
|
|
30
|
+
*/
|
|
31
|
+
function parseMaxNudgesFlag(raw) {
|
|
32
|
+
if (raw === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const value = raw.trim().toLowerCase();
|
|
35
|
+
if (value === 'unlimited')
|
|
36
|
+
return null;
|
|
37
|
+
if (/^\d+$/.test(value)) {
|
|
38
|
+
const parsed = Number(value);
|
|
39
|
+
if (Number.isSafeInteger(parsed))
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`--max-nudges must be a whole number (0 mutes it) or the word "unlimited"; got "${raw}".`);
|
|
43
|
+
}
|
|
44
|
+
/** `null` and an unset cap are "no daily limit"; `0` is a mute; N is "at most N a day". */
|
|
45
|
+
function describeNudgeCap(value) {
|
|
46
|
+
if (value === null || value === undefined)
|
|
47
|
+
return 'no daily limit';
|
|
48
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
49
|
+
return `unreadable (${String(value)})`;
|
|
50
|
+
}
|
|
51
|
+
if (value <= 0)
|
|
52
|
+
return 'muted: it never contacts you';
|
|
53
|
+
return `at most ${value} a day`;
|
|
54
|
+
}
|
|
55
|
+
function durationWords(ms) {
|
|
56
|
+
if (ms < 60_000)
|
|
57
|
+
return `${Math.max(1, Math.round(ms / 1000))} s`;
|
|
58
|
+
const totalMinutes = Math.round(ms / 60_000);
|
|
59
|
+
const days = Math.floor(totalMinutes / 1440);
|
|
60
|
+
const hours = Math.floor((totalMinutes % 1440) / 60);
|
|
61
|
+
const minutes = totalMinutes % 60;
|
|
62
|
+
return [days ? `${days} d` : '', hours ? `${hours} h` : '', minutes ? `${minutes} min` : '']
|
|
63
|
+
.filter(Boolean)
|
|
64
|
+
.join(' ');
|
|
65
|
+
}
|
|
66
|
+
/** The least time the platform leaves between two nudges to you. */
|
|
67
|
+
function describeNudgeGap(ms) {
|
|
68
|
+
if (ms === null || ms === undefined || ms === 0)
|
|
69
|
+
return 'no minimum gap between nudges';
|
|
70
|
+
if (typeof ms !== 'number' || !Number.isFinite(ms) || ms < 0) {
|
|
71
|
+
return `unreadable (${String(ms)})`;
|
|
72
|
+
}
|
|
73
|
+
return `at least ${durationWords(ms)} between nudges`;
|
|
74
|
+
}
|
|
75
|
+
/** The back-off for a person who is not answering. The server's default count is 2. */
|
|
76
|
+
function describeNoReplyRule(noReply) {
|
|
77
|
+
if (noReply?.enabled !== true)
|
|
78
|
+
return 'off: it keeps nudging whether or not you answer';
|
|
79
|
+
const after = Number.isInteger(noReply.afterNudges) ? noReply.afterNudges : 2;
|
|
80
|
+
return `after ${after} unanswered nudge${after === 1 ? '' : 's'} it waits until you reply`;
|
|
81
|
+
}
|
|
82
|
+
/** Read the three settings out of a stored `policy`. */
|
|
83
|
+
function nudgeSettingsFromPolicy(policy) {
|
|
84
|
+
return {
|
|
85
|
+
maxUserNudgesPerDay: policy.maxUserNudgesPerDay,
|
|
86
|
+
minUserNudgeIntervalMs: policy.minUserNudgeIntervalMs,
|
|
87
|
+
noReply: policy.attention?.noReply,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The lines that say how much a Commitment may interrupt its owner. A muted one
|
|
92
|
+
* prints the mute alone: a gap and a back-off on a Commitment that never speaks
|
|
93
|
+
* would only suggest it does. `withNoReply: false` leaves the back-off out when the
|
|
94
|
+
* stored rule could not be read, rather than claim it is off.
|
|
95
|
+
*/
|
|
96
|
+
function nudgeSettingLines(settings, { withNoReply = true } = {}) {
|
|
97
|
+
const lines = [`Nudges: ${describeNudgeCap(settings.maxUserNudgesPerDay)}`];
|
|
98
|
+
const muted = typeof settings.maxUserNudgesPerDay === 'number' && settings.maxUserNudgesPerDay <= 0;
|
|
99
|
+
if (muted)
|
|
100
|
+
return lines;
|
|
101
|
+
lines.push(`Min gap: ${describeNudgeGap(settings.minUserNudgeIntervalMs)}`);
|
|
102
|
+
if (withNoReply)
|
|
103
|
+
lines.push(`No reply: ${describeNoReplyRule(settings.noReply)}`);
|
|
104
|
+
return lines;
|
|
105
|
+
}
|
|
106
|
+
const CLOCK = /^(\d{1,2}):(\d{2})$/;
|
|
107
|
+
/** What a time of day is, in the words a person can type. */
|
|
108
|
+
exports.CLOCK_TIME_HINT = 'use HH:MM, 00:00-23:59, for example 22:00';
|
|
109
|
+
/**
|
|
110
|
+
* How to set quiet hours, for the `--from-json` help of `create` and `update`. There is no flag for
|
|
111
|
+
* them, so without this sentence a user had to know the `policy.quietHours` key from outside the CLI
|
|
112
|
+
* (OSK-21123).
|
|
113
|
+
*/
|
|
114
|
+
exports.QUIET_HOURS_HELP = 'Quiet hours (no messages to you) go in the JSON as policy.quietHours: ' +
|
|
115
|
+
'{"enabled":true,"start":"22:00","end":"07:00","timezone":"America/Los_Angeles"}. ' +
|
|
116
|
+
'Times are 24-hour HH:MM (00:00-23:59); the window may cross midnight and its end is not ' +
|
|
117
|
+
'inside it; timezone is an IANA name, and when omitted it is UTC, not your local zone.';
|
|
118
|
+
/**
|
|
119
|
+
* What `policy.executionBudget.allowedEventSources` means, for the same help. An empty list is the
|
|
120
|
+
* unrestricted case, which is the opposite of what `[]` suggests (OSK-21113).
|
|
121
|
+
*/
|
|
122
|
+
exports.EVENT_SOURCES_HELP = 'policy.executionBudget.allowedEventSources limits which event sources may wake the Commitment: ' +
|
|
123
|
+
'an empty or missing list allows every source (it is not "none"); name sources to restrict it.';
|
|
124
|
+
/**
|
|
125
|
+
* The allowlist in words. An empty or missing list means every source is allowed
|
|
126
|
+
* (`sourceAllowed` in api/server/services/EventBus/commitmentWakeCapabilities.js and the check in
|
|
127
|
+
* WakeIntentResolver.js both treat it so); the stored form of "never set" is the same `[]`.
|
|
128
|
+
*/
|
|
129
|
+
function describeEventSources(allowed) {
|
|
130
|
+
return Array.isArray(allowed) && allowed.length
|
|
131
|
+
? `limited to: ${allowed.map(String).join(', ')}`
|
|
132
|
+
: 'all allowed (the list is empty)';
|
|
133
|
+
}
|
|
134
|
+
function validClock(value) {
|
|
135
|
+
if (typeof value !== 'string')
|
|
136
|
+
return false;
|
|
137
|
+
const match = CLOCK.exec(value);
|
|
138
|
+
return Boolean(match) && Number(match[1]) <= 23 && Number(match[2]) <= 59;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The problem with the quiet-hours times in a `policy` about to be sent, or null. The server
|
|
142
|
+
* answered a bad time with its raw pattern (`must match pattern /^\d{1,2}:\d{2}$/`), which says
|
|
143
|
+
* nothing a person can type, and accepted `25:00` (a window that then never applies). Checked
|
|
144
|
+
* here, so the request never leaves with either.
|
|
145
|
+
*/
|
|
146
|
+
function quietHoursInputProblem(policy) {
|
|
147
|
+
const quietHours = policy?.quietHours;
|
|
148
|
+
if (!quietHours || typeof quietHours !== 'object')
|
|
149
|
+
return null;
|
|
150
|
+
for (const key of ['start', 'end']) {
|
|
151
|
+
const value = quietHours[key];
|
|
152
|
+
if (value !== undefined && !validClock(value)) {
|
|
153
|
+
return `policy.quietHours.${key} ${JSON.stringify(value)} is not a time of day: ${exports.CLOCK_TIME_HINT}.`;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
/** `9:00` and `09:00` are the same time to the server; show the zero-padded form. */
|
|
159
|
+
function clockText(value) {
|
|
160
|
+
const match = CLOCK.exec(String(value ?? '').trim());
|
|
161
|
+
return match ? `${match[1].padStart(2, '0')}:${match[2]}` : String(value ?? '');
|
|
162
|
+
}
|
|
163
|
+
/** An omitted timezone is UTC, and no text surface said so (OSK-21122). */
|
|
164
|
+
const NO_TIMEZONE_NOTE = 'UTC (no timezone set, UTC is used)';
|
|
165
|
+
/**
|
|
166
|
+
* The Commitment's own quiet-hours window in words. Without one of its own a Commitment uses its
|
|
167
|
+
* Agent's Compass window (`scheduleIssues.js` documents the same rule), so "none set" says where
|
|
168
|
+
* else a window may come from rather than claiming there is no quiet time at all.
|
|
169
|
+
*/
|
|
170
|
+
function describeQuietHours(quietHours) {
|
|
171
|
+
if (!quietHours) {
|
|
172
|
+
return "none set on this Commitment (a window in its Agent's Compass applies, if it has one)";
|
|
173
|
+
}
|
|
174
|
+
const zone = quietHours.timezone ? quietHours.timezone : NO_TIMEZONE_NOTE;
|
|
175
|
+
const window = quietHours.start && quietHours.end
|
|
176
|
+
? `${clockText(quietHours.start)}-${clockText(quietHours.end)} ${zone}`
|
|
177
|
+
: null;
|
|
178
|
+
if (quietHours.enabled !== true) {
|
|
179
|
+
return window ? `off (${window} is saved but switched off)` : 'off';
|
|
180
|
+
}
|
|
181
|
+
if (!window)
|
|
182
|
+
return 'on, but start and end are not both set, so no quiet window applies';
|
|
183
|
+
if (clockText(quietHours.start) === clockText(quietHours.end)) {
|
|
184
|
+
return `${window}: start and end are the same time, so the window is empty and nothing is held`;
|
|
185
|
+
}
|
|
186
|
+
return window;
|
|
187
|
+
}
|
|
188
|
+
/** `2026-11-01T15:00:00.000Z` as `2026-11-01 15:00 UTC`. */
|
|
189
|
+
function utcText(iso) {
|
|
190
|
+
const parsed = typeof iso === 'string' ? new Date(iso) : null;
|
|
191
|
+
if (!parsed || Number.isNaN(parsed.getTime()))
|
|
192
|
+
return null;
|
|
193
|
+
return `${parsed.toISOString().slice(0, 16).replace('T', ' ')} UTC`;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* When a held message is released, for the preflight line "Held until your quiet hours end".
|
|
197
|
+
* The report's `quietHours` carries the end as typed, the zone, and the exact instant (`endsAt`,
|
|
198
|
+
* daylight saving included); null when it carries no instant, so the caller keeps its generic
|
|
199
|
+
* sentence rather than inventing a time.
|
|
200
|
+
*/
|
|
201
|
+
function describeQuietHoursEnd(quietHours) {
|
|
202
|
+
const utc = utcText(quietHours?.endsAt);
|
|
203
|
+
if (!utc)
|
|
204
|
+
return null;
|
|
205
|
+
const zone = quietHours?.timezone || 'UTC';
|
|
206
|
+
if (zone === 'UTC' || !quietHours?.end)
|
|
207
|
+
return `Held until your quiet hours end: ${utc}.`;
|
|
208
|
+
return `Held until your quiet hours end: ${clockText(quietHours.end)} ${zone}, ${utc}.`;
|
|
209
|
+
}
|
|
@@ -10,6 +10,12 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { type CommitmentLoopStatus, type CommitmentRoster } from '@skrr-ai/data-provider';
|
|
12
12
|
export declare function loopStateLabel(state: unknown): string;
|
|
13
|
+
/**
|
|
14
|
+
* What an empty "next wakes" list means. "Nothing is scheduled" is true only of an unarmed loop: a held
|
|
15
|
+
* wake IS scheduled and is being refused, and the line above it says "armed" (OSK-21261). Unknown stays
|
|
16
|
+
* "could not be read" because cannot-see is not none.
|
|
17
|
+
*/
|
|
18
|
+
export declare function noNextWakeText(status: Pick<CommitmentLoopStatus, 'loopState' | 'loopReason'>): string;
|
|
13
19
|
/** Roster table: one row per Commitment. */
|
|
14
20
|
export declare function formatCommitmentRoster(roster: Partial<CommitmentRoster>): string[];
|
|
15
21
|
/**
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loopStateLabel = loopStateLabel;
|
|
4
|
+
exports.noNextWakeText = noNextWakeText;
|
|
4
5
|
exports.formatCommitmentRoster = formatCommitmentRoster;
|
|
5
6
|
exports.formatLoopSummary = formatLoopSummary;
|
|
6
7
|
exports.formatCommitmentLoopStatus = formatCommitmentLoopStatus;
|
|
@@ -64,6 +65,19 @@ function spend(status) {
|
|
|
64
65
|
// A partial ledger is a lower bound; never print it as the whole answer.
|
|
65
66
|
return status.cost?.spendKnown ? amount : `≥ ${amount} (some runs unpriced)`;
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* What an empty "next wakes" list means. "Nothing is scheduled" is true only of an unarmed loop: a held
|
|
70
|
+
* wake IS scheduled and is being refused, and the line above it says "armed" (OSK-21261). Unknown stays
|
|
71
|
+
* "could not be read" because cannot-see is not none.
|
|
72
|
+
*/
|
|
73
|
+
function noNextWakeText(status) {
|
|
74
|
+
if (status.loopState === 'unknown')
|
|
75
|
+
return 'could not be read';
|
|
76
|
+
if (status.loopReason === 'wake_control_blocked' || status.loopReason === 'wake_admission_blocked') {
|
|
77
|
+
return 'none while its wakes are held (see the loop line)';
|
|
78
|
+
}
|
|
79
|
+
return 'none — nothing is scheduled to wake it';
|
|
80
|
+
}
|
|
67
81
|
function nextWake(status) {
|
|
68
82
|
const wake = status.nextWakes?.[0];
|
|
69
83
|
return wake ? `${when(wake.at)} ${wake.kind}` : '-';
|
|
@@ -136,9 +150,7 @@ function formatLoopSummary(status) {
|
|
|
136
150
|
const wake = status.nextWakes?.[0];
|
|
137
151
|
lines.push(`next wake: ${wake
|
|
138
152
|
? `${when(wake.at)} ${wake.kind}`
|
|
139
|
-
: status
|
|
140
|
-
? 'could not be read'
|
|
141
|
-
: 'none — nothing is scheduled to wake it'}`);
|
|
153
|
+
: noNextWakeText(status)}`);
|
|
142
154
|
if (status.lastStop) {
|
|
143
155
|
const stop = status.lastStop;
|
|
144
156
|
lines.push(`last stop: ${stop.question}${stop.reason ? ` — ${stop.reason}` : ''}${stop.detail ? `: ${stop.detail}` : ''}`);
|
|
@@ -189,9 +201,7 @@ function formatCommitmentLoopStatus(status) {
|
|
|
189
201
|
const wakes = Array.isArray(status.nextWakes) ? status.nextWakes : [];
|
|
190
202
|
lines.push(' Next wakes:');
|
|
191
203
|
if (!wakes.length) {
|
|
192
|
-
lines.push(status.loopState === 'unknown'
|
|
193
|
-
? ' (could not be read)'
|
|
194
|
-
: ' none — nothing is scheduled to wake it');
|
|
204
|
+
lines.push(status.loopState === 'unknown' ? ' (could not be read)' : ` ${noNextWakeText(status)}`);
|
|
195
205
|
}
|
|
196
206
|
for (const wake of wakes) {
|
|
197
207
|
lines.push(` ${when(wake.at)} ${wake.kind}${wake.reason ? ` (${wake.reason})` : ''}`);
|
|
@@ -60,6 +60,12 @@ export type CommitmentPreflightReport = {
|
|
|
60
60
|
id: string;
|
|
61
61
|
label: string;
|
|
62
62
|
verdict: 'pass' | 'indeterminate' | 'block';
|
|
63
|
+
/**
|
|
64
|
+
* Whether this gate can stop a Run (`blocking`) or only informs (`advisory`). An advisory gate
|
|
65
|
+
* can still say `block` (a Pack that watches its Goal through a link, not a watched source,
|
|
66
|
+
* has "no observation source"); it stops nothing and never feeds the verdict.
|
|
67
|
+
*/
|
|
68
|
+
admission?: 'blocking' | 'advisory';
|
|
63
69
|
/** Short machine code for why a gate did NOT pass. */
|
|
64
70
|
reason?: string | null;
|
|
65
71
|
/**
|
|
@@ -96,7 +102,29 @@ export type CommitmentPreflightReport = {
|
|
|
96
102
|
retired?: string[];
|
|
97
103
|
retiredReason?: string;
|
|
98
104
|
} & Record<string, unknown>;
|
|
105
|
+
/**
|
|
106
|
+
* Whether Resume / `create --activate` would be accepted, answered by the server's one activation
|
|
107
|
+
* rule (`CommitmentActivation`). Absent from an older server. A gate can be advisory for a run and
|
|
108
|
+
* still refuse activation, so this is the answer, not the gate list.
|
|
109
|
+
*/
|
|
110
|
+
activation?: {
|
|
111
|
+
canActivate?: boolean;
|
|
112
|
+
blockers?: Array<{
|
|
113
|
+
gate: string;
|
|
114
|
+
label?: string;
|
|
115
|
+
reason?: string;
|
|
116
|
+
hint?: string;
|
|
117
|
+
}>;
|
|
118
|
+
} | null;
|
|
99
119
|
};
|
|
120
|
+
/**
|
|
121
|
+
* The lines preflight prints when the server says activation would be refused (OSK-21258). The gate
|
|
122
|
+
* list marks an advisory gate `info (advisory: does not stop a run)`, which is true of a run and false
|
|
123
|
+
* of activation: `resume` refused the same gate with a 409 while preflight read as if nothing stopped
|
|
124
|
+
* it. The server owns which gates refuse; this lays out its answer and never re-derives it. Nothing
|
|
125
|
+
* for an allowed activation or an older server that does not send the block.
|
|
126
|
+
*/
|
|
127
|
+
export declare function activationRefusalLines(activation: CommitmentPreflightReport['activation']): string[];
|
|
100
128
|
/**
|
|
101
129
|
* The lines every Commitment read surface prints for "whose permissions does a
|
|
102
130
|
* run use?" — `show`, `effective-policy` and `preflight` share it so the three
|
|
@@ -161,6 +189,8 @@ export type CommitmentPackPreflightResponse = {
|
|
|
161
189
|
export type CommitmentPackDefinition = {
|
|
162
190
|
id: string;
|
|
163
191
|
version: string;
|
|
192
|
+
/** The Pack's name as the server sends it. `title` is not a field of the catalogue (OSK-21110). */
|
|
193
|
+
label?: string;
|
|
164
194
|
title?: string;
|
|
165
195
|
description?: string;
|
|
166
196
|
[key: string]: unknown;
|
|
@@ -179,6 +209,17 @@ export type CommitmentPackPlanResponse = {
|
|
|
179
209
|
agentId: string;
|
|
180
210
|
holder?: 'space_lead' | 'explicit';
|
|
181
211
|
};
|
|
212
|
+
/**
|
|
213
|
+
* What the plan cannot fix and `pack preflight` would report later: today only a holder whose
|
|
214
|
+
* runtime can never activate this loop. Beside the plan, outside its digest.
|
|
215
|
+
*/
|
|
216
|
+
readiness?: Array<{
|
|
217
|
+
gate: string;
|
|
218
|
+
label: string;
|
|
219
|
+
verdict: 'block';
|
|
220
|
+
reason: string;
|
|
221
|
+
hint?: string;
|
|
222
|
+
}>;
|
|
182
223
|
[key: string]: unknown;
|
|
183
224
|
};
|
|
184
225
|
export type CommitmentPackBindResponse = {
|
|
@@ -719,6 +760,21 @@ export declare function readCommitmentAgentDirective({ directive, file, }: {
|
|
|
719
760
|
directive?: string;
|
|
720
761
|
file?: string;
|
|
721
762
|
}): Promise<string | undefined>;
|
|
763
|
+
/**
|
|
764
|
+
* The top-level keys of a `--from-json` body that `create`/`preflight` will not send. A key within two edits
|
|
765
|
+
* of a real one is a `typo`; the rest are `ignored`. A server-owned dump field is neither.
|
|
766
|
+
*/
|
|
767
|
+
export declare function unrecognizedCreateKeys(src: Record<string, unknown>): {
|
|
768
|
+
typos: Array<{
|
|
769
|
+
key: string;
|
|
770
|
+
suggestion: string;
|
|
771
|
+
}>;
|
|
772
|
+
ignored: string[];
|
|
773
|
+
};
|
|
774
|
+
/** The keys of a `--from-json` body that are neither sent nor server-owned and not a typo: to be warned about. */
|
|
775
|
+
export declare function ignoredCreateKeys(input: Record<string, unknown>): string[];
|
|
776
|
+
/** The one sentence `create` and `preflight` print for them. */
|
|
777
|
+
export declare function ignoredCreateKeysWarning(keys: string[]): string | null;
|
|
722
778
|
/**
|
|
723
779
|
* Normalize a `--from-json` body into something the create endpoint accepts.
|
|
724
780
|
*
|
|
@@ -951,6 +1007,22 @@ export declare function commitmentWriteWarnings(response: unknown, fallback: {
|
|
|
951
1007
|
/** One `Warning:` line per authoring warning, after the write's own output. */
|
|
952
1008
|
export declare function renderCommitmentWriteWarnings(warnings: CommitmentAuthoringWarning[], warn: (line: string) => void): void;
|
|
953
1009
|
export declare function describeCommitmentRevision(revision: CommitmentRevisionReceipt | undefined): string;
|
|
1010
|
+
/**
|
|
1011
|
+
* A commitment's priority, as one line: `high · weight 70 · from northstar`.
|
|
1012
|
+
*
|
|
1013
|
+
* Linking a commitment to a northstar rewrites its priority (`source: northstar`) and a governor
|
|
1014
|
+
* reprioritization changes it again, so the place a person looks to confirm either is `show`, which
|
|
1015
|
+
* printed everything but this and sent them to `--json`. An unset priority says so rather than
|
|
1016
|
+
* printing nothing.
|
|
1017
|
+
*/
|
|
1018
|
+
export declare function describeCommitmentPriority(priority: CommitmentView['priority']): string;
|
|
1019
|
+
/**
|
|
1020
|
+
* The `Compass:` line of a bound commitment. A commitment names its compass (`compassId`) but the
|
|
1021
|
+
* read does not carry that compass's STATUS, and an archived one governs nothing (the commitment
|
|
1022
|
+
* runs on defaults), so the id alone could be read as "governed by it". Only what is known is
|
|
1023
|
+
* stated: the id, and the command that says whether it is active (OSK-21242).
|
|
1024
|
+
*/
|
|
1025
|
+
export declare function describeBoundCompass(commitment: Pick<CommitmentView, 'compassId' | 'agentId'>): string;
|
|
954
1026
|
export declare function renderCommitmentSummary(commitment: CommitmentView, log: (line: string) => void,
|
|
955
1027
|
/**
|
|
956
1028
|
* The most recent check, when the caller has one. Optional because pause /
|
|
@@ -1011,10 +1083,26 @@ export declare function hypothesisRefusalLines(run: {
|
|
|
1011
1083
|
count: number;
|
|
1012
1084
|
}>;
|
|
1013
1085
|
}): string[];
|
|
1086
|
+
/**
|
|
1087
|
+
* A wake the Agent asked for and the platform did not arm, in the server's words.
|
|
1088
|
+
* The only record used to be `ledger --json`; the cycle read `advanced` (OSK-21132).
|
|
1089
|
+
*/
|
|
1090
|
+
export declare function wakeOutcomeLines(run: {
|
|
1091
|
+
wake?: {
|
|
1092
|
+
status?: string;
|
|
1093
|
+
reason?: string;
|
|
1094
|
+
detail?: string;
|
|
1095
|
+
fix?: string;
|
|
1096
|
+
};
|
|
1097
|
+
}): string[];
|
|
1014
1098
|
export declare function formatCommitmentExplanation(explanation: Partial<CommitmentExplanationResponse>): string[];
|
|
1015
1099
|
/**
|
|
1016
1100
|
* Print the continuation hint for a paged `object:'list'` response — the line
|
|
1017
1101
|
* that makes history beyond the newest page reachable instead of invisible.
|
|
1102
|
+
*
|
|
1103
|
+
* Text mode ONLY. Under `--json` stdout must be one JSON document, and the same
|
|
1104
|
+
* facts are already in it as `has_more` / `next_cursor`: a caller that prints
|
|
1105
|
+
* this after the JSON makes `| json.load` fail (OSK-21118).
|
|
1018
1106
|
*/
|
|
1019
1107
|
export declare function renderListPageHint(page: {
|
|
1020
1108
|
has_more?: boolean;
|