@skrr-ai/cli 0.1.129 → 0.1.130
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.js +30 -0
- package/dist/commands/agenda/trigger/create.js +9 -1
- package/dist/commands/agents/reflect.d.ts +6 -0
- package/dist/commands/agents/reflect.js +45 -6
- package/dist/commands/agents/schedule-overview.d.ts +17 -0
- package/dist/commands/agents/schedule-overview.js +33 -9
- package/dist/commands/agents/triggers/policy.js +3 -1
- package/dist/commands/northstars/create.js +6 -1
- package/dist/commands/northstars/list.js +1 -1
- package/dist/commands/northstars/show.js +1 -1
- package/dist/commands/spaces/heartbeats/status.js +10 -0
- package/dist/commands/tasks/archive.js +6 -0
- package/dist/commands/tasks/pause-schedule.js +1 -1
- package/dist/commands/tasks/resume-schedule.js +4 -1
- package/dist/commands/tasks/schedule.js +6 -1
- package/dist/commands/tasks/self-schedule.d.ts +56 -0
- package/dist/commands/tasks/self-schedule.js +179 -21
- package/dist/lib/northstars.d.ts +44 -4
- package/dist/lib/northstars.js +108 -92
- package/dist/lib/reflection.d.ts +58 -0
- package/dist/lib/reflection.js +115 -6
- package/dist/lib/task-operations.js +2 -0
- package/dist/lib/task-schedule-notes.d.ts +46 -0
- package/dist/lib/task-schedule-notes.js +71 -0
- package/dist/lib/triggers.d.ts +18 -0
- package/dist/lib/triggers.js +16 -6
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +2 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
- package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
- package/dist/node_modules/@skrr-ai/data-provider/index.js +840 -826
- package/oclif.manifest.json +20 -12
- package/package.json +1 -1
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.decisionClause = decisionClause;
|
|
4
|
+
exports.describeSpentReturn = describeSpentReturn;
|
|
5
|
+
exports.resolveRelativeRunAt = resolveRelativeRunAt;
|
|
6
|
+
exports.resolveAbsoluteRunAt = resolveAbsoluteRunAt;
|
|
7
|
+
exports.resolveRunAt = resolveRunAt;
|
|
3
8
|
const core_1 = require("@oclif/core");
|
|
4
9
|
const base_command_1 = require("../../base-command");
|
|
5
10
|
const task_extras_1 = require("../../lib/task-extras");
|
|
@@ -20,6 +25,7 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
20
25
|
static examples = [
|
|
21
26
|
'<%= config.bin %> tasks self-schedule <task-id> --in 2h --note "check whether the deploy settled"',
|
|
22
27
|
'<%= config.bin %> tasks self-schedule <task-id> --at 2026-09-09T14:00:00Z',
|
|
28
|
+
'<%= config.bin %> tasks self-schedule <task-id> --in 90m --re-enable',
|
|
23
29
|
'<%= config.bin %> tasks self-schedule <task-id> --show',
|
|
24
30
|
'<%= config.bin %> tasks self-schedule <task-id> --clear',
|
|
25
31
|
];
|
|
@@ -28,8 +34,14 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
28
34
|
};
|
|
29
35
|
static flags = {
|
|
30
36
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
31
|
-
at: core_1.Flags.string({
|
|
32
|
-
|
|
37
|
+
at: core_1.Flags.string({
|
|
38
|
+
description: 'Absolute return time: ISO-8601 with a UTC offset, such as 2026-11-05T10:00:00Z or ' +
|
|
39
|
+
'2026-11-05T10:00:00-08:00',
|
|
40
|
+
}),
|
|
41
|
+
in: core_1.Flags.string({
|
|
42
|
+
description: 'Relative return time: a number with a unit (s, m, h, d), such as 90m, 1.5h or 3d. ' +
|
|
43
|
+
'A bare number means minutes. At least 60s, at most 14 days',
|
|
44
|
+
}),
|
|
33
45
|
note: core_1.Flags.string({
|
|
34
46
|
// This DOES reach the run now. It is read by `buildTaskExecutionPrompt`,
|
|
35
47
|
// the one place every dispatch path assembles a task prompt, and rendered
|
|
@@ -40,7 +52,13 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
40
52
|
'prompt section, and readable any time with --show',
|
|
41
53
|
}),
|
|
42
54
|
'max-per-day': core_1.Flags.integer({
|
|
43
|
-
description: 'Cap on self-initiated returns per day (1-24
|
|
55
|
+
description: 'Cap on self-initiated returns per day (1-24). Omit to keep the cap this task ' +
|
|
56
|
+
'already has (4 for a task that never had one)',
|
|
57
|
+
}),
|
|
58
|
+
're-enable': core_1.Flags.boolean({
|
|
59
|
+
description: "Switch the task's return back on if it was turned off, then park it. Without this a " +
|
|
60
|
+
'switched-off return is left off and the park is refused. A person may; an agent only ' +
|
|
61
|
+
'where its initiative policy allows autonomous loops',
|
|
44
62
|
}),
|
|
45
63
|
show: core_1.Flags.boolean({ description: 'Print the pending return without changing it' }),
|
|
46
64
|
clear: core_1.Flags.boolean({ description: 'Cancel the pending return' }),
|
|
@@ -61,20 +79,47 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
61
79
|
result = await task_extras_1.taskSelfScheduleApi.clear(taskRef);
|
|
62
80
|
}
|
|
63
81
|
else {
|
|
64
|
-
const
|
|
65
|
-
if (!
|
|
82
|
+
const resolved = resolveRunAt(flags.at, flags.in);
|
|
83
|
+
if (!resolved) {
|
|
66
84
|
this.error('Pass --at <ISO-8601>, --in <duration>, or one of --show / --clear.', {
|
|
67
85
|
exit: 1,
|
|
68
86
|
});
|
|
69
87
|
}
|
|
88
|
+
if ('error' in resolved) {
|
|
89
|
+
this.failWithCliError({
|
|
90
|
+
message: resolved.error,
|
|
91
|
+
code: 'INVALID_RETURN_TIME',
|
|
92
|
+
exit: 1,
|
|
93
|
+
retryable: false,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// The server's field path, read back as the flag that wrote it.
|
|
97
|
+
this.requestFieldFlags = selfScheduleFieldFlags(Boolean(flags.at));
|
|
70
98
|
result = await task_extras_1.taskSelfScheduleApi.park(taskRef, {
|
|
71
|
-
runAt,
|
|
99
|
+
runAt: resolved.runAt,
|
|
72
100
|
...(flags.note ? { note: flags.note } : {}),
|
|
73
|
-
|
|
101
|
+
// `!== undefined`, not truthiness: `--max-per-day 0` is a value the server
|
|
102
|
+
// refuses (1-24), and dropping it here parked the task with the default
|
|
103
|
+
// while printing a cap the person never asked for.
|
|
104
|
+
...(flags['max-per-day'] !== undefined ? { maxFiresPerDay: flags['max-per-day'] } : {}),
|
|
105
|
+
...(flags['re-enable'] ? { reEnable: true } : {}),
|
|
74
106
|
});
|
|
75
107
|
}
|
|
76
108
|
}
|
|
77
109
|
catch (err) {
|
|
110
|
+
// A return someone switched off is left off. The server's sentence is the
|
|
111
|
+
// same for the web and the API; the CLI adds the flag that answers it.
|
|
112
|
+
if ((0, base_command_1.apiRefusal)(err).code === 'TASK_RETURN_DISABLED') {
|
|
113
|
+
this.failWithCliError({
|
|
114
|
+
message: `Task ${taskRef}'s return is switched off, so it was not parked again.\n` +
|
|
115
|
+
"→ Ask the task's owner to switch it back on in the task's Automations. A person " +
|
|
116
|
+
'who means to turn it back on can run this command again with --re-enable.',
|
|
117
|
+
code: 'TASK_RETURN_DISABLED',
|
|
118
|
+
status: 409,
|
|
119
|
+
exit: 1,
|
|
120
|
+
retryable: false,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
78
123
|
this.handleApiError(err);
|
|
79
124
|
}
|
|
80
125
|
if (flags.json) {
|
|
@@ -82,15 +127,35 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
82
127
|
return;
|
|
83
128
|
}
|
|
84
129
|
const runAt = result?.runAt;
|
|
130
|
+
const decision = result?.lastDecision;
|
|
85
131
|
if (!runAt) {
|
|
86
132
|
this.log(`Task ${taskRef} has no pending return.`);
|
|
133
|
+
// The interim tick clears the instant after a fire, so the decision may be
|
|
134
|
+
// all there is to say about the return that just went.
|
|
135
|
+
if (decision)
|
|
136
|
+
this.log(`Its last return ${decisionClause(decision)}.`);
|
|
87
137
|
return;
|
|
88
138
|
}
|
|
89
139
|
// `pending` is derived server-side: a return that has already fired keeps
|
|
90
140
|
// its instant on the row, and printing that verbatim would report a return
|
|
91
141
|
// as upcoming an hour after it happened.
|
|
92
142
|
const spent = result?.pending === false;
|
|
93
|
-
|
|
143
|
+
if (!spent) {
|
|
144
|
+
this.log(`Task ${taskRef} returns at ${runAt}`);
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// "Spent" only means the instant passed. Whether a run started is the
|
|
148
|
+
// decision's to say; "last returned at" was printed for returns that were
|
|
149
|
+
// skipped because the task was blocked, in review, done or archived.
|
|
150
|
+
const triggerId = result?.triggerId;
|
|
151
|
+
this.log(describeSpentReturn({
|
|
152
|
+
taskRef,
|
|
153
|
+
runAt,
|
|
154
|
+
decision,
|
|
155
|
+
triggerId: typeof triggerId === 'string' ? triggerId : undefined,
|
|
156
|
+
bin: this.config.bin,
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
94
159
|
if (result?.note)
|
|
95
160
|
this.log(`Note: ${result.note}`);
|
|
96
161
|
if (!spent && result?.maxFiresPerDay) {
|
|
@@ -99,20 +164,113 @@ class TasksSelfSchedule extends base_command_1.BaseCommand {
|
|
|
99
164
|
}
|
|
100
165
|
}
|
|
101
166
|
exports.default = TasksSelfSchedule;
|
|
102
|
-
/**
|
|
103
|
-
function
|
|
167
|
+
/** The part after "the return came due and": what became of it, with the reason. */
|
|
168
|
+
function decisionClause(decision) {
|
|
169
|
+
const why = decision.reasonText || decision.reason || decision.outcome || 'no reason was recorded';
|
|
170
|
+
switch (decision.verdict) {
|
|
171
|
+
case 'ran':
|
|
172
|
+
return 'started a run';
|
|
173
|
+
case 'failed':
|
|
174
|
+
return `could not be started: ${why}`;
|
|
175
|
+
case 'waiting':
|
|
176
|
+
return `has not started yet: ${why}`;
|
|
177
|
+
default:
|
|
178
|
+
return `was skipped: ${why}`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* The line `--show` prints for a return whose time has passed. "returned at" is
|
|
183
|
+
* said only when a run really started; a skipped, failed or unrecorded return
|
|
184
|
+
* says what it was.
|
|
185
|
+
*/
|
|
186
|
+
function describeSpentReturn({ taskRef, runAt, decision, triggerId, bin = 'skrr', }) {
|
|
187
|
+
if (decision?.verdict === 'ran')
|
|
188
|
+
return `Task ${taskRef} returned at ${runAt}`;
|
|
189
|
+
if (decision)
|
|
190
|
+
return `Task ${taskRef}: the return came due at ${runAt} and ${decisionClause(decision)}.`;
|
|
191
|
+
return (`Task ${taskRef}: the return came due at ${runAt}. Whether it ran is not recorded here; ` +
|
|
192
|
+
`\`${bin} triggers audit ${triggerId ?? `task-self-schedule:${taskRef}`}\` lists every fire.`);
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Body paths the server may name in a refusal, and the flag that wrote each. `runAt`
|
|
196
|
+
* is written by whichever of --at / --in the person used, so a "runAt must be at
|
|
197
|
+
* least 60s in the future" for `--in 0m` is not read back as a flag they never typed.
|
|
198
|
+
*/
|
|
199
|
+
function selfScheduleFieldFlags(usedAt) {
|
|
200
|
+
return Object.freeze({
|
|
201
|
+
runAt: usedAt ? '--at' : '--in',
|
|
202
|
+
maxFiresPerDay: '--max-per-day',
|
|
203
|
+
note: '--note',
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
const MS_PER_UNIT = {
|
|
207
|
+
s: 1_000,
|
|
208
|
+
m: 60_000,
|
|
209
|
+
h: 3_600_000,
|
|
210
|
+
d: 86_400_000,
|
|
211
|
+
};
|
|
212
|
+
/**
|
|
213
|
+
* A number, optionally fractional, with an optional unit. The unit words
|
|
214
|
+
* collapse onto their first character (`sec`/`secs`, `min`/`mins`, `hr`/`hrs`,
|
|
215
|
+
* `day`/`days`), which is unambiguous across the four units this accepts.
|
|
216
|
+
*/
|
|
217
|
+
const RELATIVE_PATTERN = /^(\d+(?:\.\d+)?|\.\d+)\s*(s|secs?|seconds?|m|mins?|minutes?|h|hrs?|hours?|d|days?)?$/i;
|
|
218
|
+
/** What a person is told when `--in` is not a duration, in the words they would retype. */
|
|
219
|
+
const RELATIVE_HELP = 'use a number with a unit (s, m, h or d), such as 90m, 1.5h or 3d. A bare number means minutes.';
|
|
220
|
+
/**
|
|
221
|
+
* `--in` as an instant. A value that does not parse says WHICH value, because
|
|
222
|
+
* the generic "pass --at or --in" this used to fall through to told a person who
|
|
223
|
+
* had passed `--in 1.5h` to pass `--in`.
|
|
224
|
+
*
|
|
225
|
+
* Seconds are accepted: the server owns the 60 s floor and answers
|
|
226
|
+
* `RETURN_TOO_SOON` with the bound, which is a better explanation than the CLI
|
|
227
|
+
* refusing the unit.
|
|
228
|
+
*/
|
|
229
|
+
function resolveRelativeRunAt(relative, now = Date.now()) {
|
|
230
|
+
const match = RELATIVE_PATTERN.exec(relative.trim());
|
|
231
|
+
if (!match)
|
|
232
|
+
return { error: `--in ${relative}: ${RELATIVE_HELP}` };
|
|
233
|
+
const unit = (match[2] ?? 'm').toLowerCase()[0];
|
|
234
|
+
const ms = Math.round(Number(match[1]) * MS_PER_UNIT[unit]);
|
|
235
|
+
const at = new Date(now + ms);
|
|
236
|
+
if (!Number.isFinite(at.getTime())) {
|
|
237
|
+
return { error: `--in ${relative}: too far away. A return is at most 14 days out.` };
|
|
238
|
+
}
|
|
239
|
+
return { runAt: at.toISOString() };
|
|
240
|
+
}
|
|
241
|
+
/** An ISO-8601 date and time that names its zone: `Z`, or an offset such as `-08:00`. */
|
|
242
|
+
const ISO_WITH_ZONE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?(?:Z|[+-]\d{2}(?::?\d{2})?)$/i;
|
|
243
|
+
/** The same shape with no zone, which is a real ISO-8601 value and an ambiguous instant. */
|
|
244
|
+
const ISO_WITHOUT_ZONE = /^\d{4}-\d{2}-\d{2}(?:T\d{2}:\d{2}(?::\d{2}(?:\.\d+)?)?)?$/i;
|
|
245
|
+
const ABSOLUTE_HELP = 'use an ISO-8601 date and time with a UTC offset, such as 2026-11-05T10:00:00Z or ' +
|
|
246
|
+
'2026-11-05T10:00:00-08:00.';
|
|
247
|
+
/**
|
|
248
|
+
* `--at` as a UTC instant. Any offset is accepted and normalised, so the server
|
|
249
|
+
* always receives `...Z`. A value with no zone is refused by saying so: it is a
|
|
250
|
+
* valid ISO-8601 string, and "Invalid ISO datetime" for it read as if the CLI
|
|
251
|
+
* could not parse a thing the help had just told the person to type.
|
|
252
|
+
*/
|
|
253
|
+
function resolveAbsoluteRunAt(absolute) {
|
|
254
|
+
const value = absolute.trim();
|
|
255
|
+
if (ISO_WITH_ZONE.test(value)) {
|
|
256
|
+
const at = new Date(value);
|
|
257
|
+
if (Number.isFinite(at.getTime()))
|
|
258
|
+
return { runAt: at.toISOString() };
|
|
259
|
+
return { error: `--at ${absolute}: that is not a real date and time. ${ABSOLUTE_HELP}` };
|
|
260
|
+
}
|
|
261
|
+
if (ISO_WITHOUT_ZONE.test(value)) {
|
|
262
|
+
return {
|
|
263
|
+
error: `--at ${absolute}: no time zone, so the instant is ambiguous. Add Z for UTC or an ` +
|
|
264
|
+
'offset, such as 2026-11-05T10:00:00Z or 2026-11-05T10:00:00-08:00.',
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
return { error: `--at ${absolute}: not recognised as a date and time. ${ABSOLUTE_HELP}` };
|
|
268
|
+
}
|
|
269
|
+
/** `--at`, or `--in`, or neither (undefined). Absolute wins when both are given. */
|
|
270
|
+
function resolveRunAt(at, relative, now = Date.now()) {
|
|
104
271
|
if (at)
|
|
105
|
-
return at;
|
|
272
|
+
return resolveAbsoluteRunAt(at);
|
|
106
273
|
if (!relative)
|
|
107
274
|
return undefined;
|
|
108
|
-
|
|
109
|
-
if (!match)
|
|
110
|
-
return undefined;
|
|
111
|
-
const value = Number(match[1]);
|
|
112
|
-
const unit = match[2].toLowerCase();
|
|
113
|
-
const MS_PER_UNIT = { m: 60_000, h: 3_600_000, d: 86_400_000 };
|
|
114
|
-
// `min`/`mins`/`hr`/`hrs` collapse onto their first character, which is
|
|
115
|
-
// unambiguous across the three units this accepts.
|
|
116
|
-
const ms = value * (MS_PER_UNIT[unit[0]] ?? MS_PER_UNIT.h);
|
|
117
|
-
return new Date(Date.now() + ms).toISOString();
|
|
275
|
+
return resolveRelativeRunAt(relative, now);
|
|
118
276
|
}
|
package/dist/lib/northstars.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { NORTHSTAR_ANALYSIS_MODES, NORTHSTAR_APPROVAL_TRIGGERS, NORTHSTAR_EXECUTION_MODES, NORTHSTAR_INTENT_VERDICTS, NORTHSTAR_MANAGEMENT_INTENT_KINDS, NORTHSTAR_REVIEW_STATUSES } from '@skrr-ai/data-provider';
|
|
1
|
+
import { NORTHSTAR_ANALYSIS_MODES, NORTHSTAR_APPROVAL_TRIGGERS, NORTHSTAR_EXECUTION_MODES, NORTHSTAR_INTENT_VERDICTS, NORTHSTAR_MANAGEMENT_INTENT_KINDS, NORTHSTAR_REASON_WORDS, NORTHSTAR_REVIEW_STATUSES, describeNorthstarReason } from '@skrr-ai/data-provider';
|
|
2
2
|
import type { Northstar, NorthstarAnalysisMode, NorthstarExecutionMode, NorthstarGovernance, NorthstarGovernanceStatus, NorthstarGovernanceUpdateParams, NorthstarListResponse, NorthstarManagementIntent, NorthstarManagementIntentKind, NorthstarRecommendation, NorthstarReviewRecord, NorthstarStatusResponse } from '@skrr-ai/data-provider';
|
|
3
3
|
import { withQuery } from './triggers';
|
|
4
4
|
export { NORTHSTAR_ANALYSIS_MODES, NORTHSTAR_APPROVAL_TRIGGERS, NORTHSTAR_EXECUTION_MODES, NORTHSTAR_INTENT_VERDICTS, NORTHSTAR_MANAGEMENT_INTENT_KINDS, NORTHSTAR_REVIEW_STATUSES, };
|
|
5
|
+
export { NORTHSTAR_REASON_WORDS, describeNorthstarReason };
|
|
5
6
|
export type { Northstar, NorthstarAnalysisMode, NorthstarExecutionMode, NorthstarGovernance, NorthstarGovernanceStatus, NorthstarManagementIntent, NorthstarManagementIntentKind, NorthstarRecommendation, NorthstarReviewRecord, };
|
|
6
7
|
/**
|
|
7
8
|
* oclif's `options:` needs runtime arrays, and these two are LIFECYCLE
|
|
@@ -72,9 +73,6 @@ export declare function lookupReview(northstarId: string, typed: string): Promis
|
|
|
72
73
|
/** ms → a short human duration, for a table cell that must stay narrow. */
|
|
73
74
|
export declare function humanMs(ms?: number): string;
|
|
74
75
|
export declare function shortDate(value?: string): string;
|
|
75
|
-
export declare const NORTHSTAR_REASON_WORDS: Readonly<Record<string, string>>;
|
|
76
|
-
/** A code in words; an unknown code comes back unchanged, an absent one says so. */
|
|
77
|
-
export declare function describeNorthstarReason(code: string | null | undefined): string;
|
|
78
76
|
export type NorthstarDecisionContext = {
|
|
79
77
|
northstarId: string;
|
|
80
78
|
intentId: string;
|
|
@@ -131,6 +129,48 @@ export declare function listFooter(input: {
|
|
|
131
129
|
nextCursor?: string | null;
|
|
132
130
|
cursor?: boolean;
|
|
133
131
|
}): string;
|
|
132
|
+
/** What a northstar with no workspace is called wherever a workspace is named. */
|
|
133
|
+
export declare const PERSONAL_NORTHSTAR_WORKSPACE = "personal";
|
|
134
|
+
/** Why a personal northstar can look missing on the web. The one sentence the others build on. */
|
|
135
|
+
export declare const NORTHSTAR_WEB_SCOPE_SENTENCE: string;
|
|
136
|
+
/** Help text shared by `list` and `show`: where a northstar's workspace is printed. */
|
|
137
|
+
export declare const NORTHSTAR_WORKSPACE_READ_HELP: string;
|
|
138
|
+
/** Help text for `create`: which workspace it lands in, and how to choose one. */
|
|
139
|
+
export declare const NORTHSTAR_WORKSPACE_CREATE_HELP: string;
|
|
140
|
+
/**
|
|
141
|
+
* Anything that may carry a `workspaceId`: a typed Northstar, or the raw record `create` prints (it
|
|
142
|
+
* is read through `Record<string, unknown>` so `--json` is the server's body plus the url).
|
|
143
|
+
*/
|
|
144
|
+
type WorkspaceBearing = {
|
|
145
|
+
workspaceId?: unknown;
|
|
146
|
+
} | Record<string, unknown>;
|
|
147
|
+
export declare function shortWorkspaceId(id: string): string;
|
|
148
|
+
/** The workspace a northstar lives in, in one word or id: `personal` when it has none. */
|
|
149
|
+
export declare function northstarWorkspaceLabel(northstar: WorkspaceBearing, options?: {
|
|
150
|
+
short?: boolean;
|
|
151
|
+
}): string;
|
|
152
|
+
/** The line `show` and `create` print: the full workspace id, or `personal`. */
|
|
153
|
+
export declare function northstarWorkspaceLine(northstar: WorkspaceBearing): string;
|
|
154
|
+
/** What the process that ran `create` asked for: the inputs to which note a personal northstar gets. */
|
|
155
|
+
export type PersonalNorthstarContext = {
|
|
156
|
+
/** An Agent session: its workspace comes from the session, and `--workspace` is ignored. */
|
|
157
|
+
autonomous?: boolean;
|
|
158
|
+
/** The workspace the request carried (`--workspace`, or the one `workspaces use` chose). */
|
|
159
|
+
requestedWorkspaceId?: string;
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* What `create` adds when the northstar landed in no workspace. Three causes, three endings:
|
|
163
|
+
*
|
|
164
|
+
* - an Agent session takes its workspace from the session and ignores `--workspace`, so telling
|
|
165
|
+
* it to pass the flag would send it round in a circle;
|
|
166
|
+
* - a workspace WAS requested but the person is not a member of it: the server does not refuse,
|
|
167
|
+
* it creates the northstar personal (`resolveWorkspaceContext`), so "pass --workspace" would
|
|
168
|
+
* be said to someone who just did;
|
|
169
|
+
* - nothing was requested, and the way out is the flag.
|
|
170
|
+
*/
|
|
171
|
+
export declare function personalNorthstarNote(context?: PersonalNorthstarContext): string;
|
|
172
|
+
/** The note for a northstar `create` just made: nothing when it is in a workspace. */
|
|
173
|
+
export declare function createdNorthstarNote(northstar: WorkspaceBearing, context?: PersonalNorthstarContext): string | undefined;
|
|
134
174
|
export declare function renderNorthstarList(rows: Northstar[], log: (line: string) => void): void;
|
|
135
175
|
/**
|
|
136
176
|
* The active-commitment cap, in words. `0` is accepted and stored, and a review
|
package/dist/lib/northstars.js
CHANGED
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.northstarWideFlag = exports.CRITICAL_COMMITMENT_APPROVAL_NOTE = exports.ALLOCATION_INPUT_FLAGS = exports.
|
|
3
|
+
exports.northstarWideFlag = exports.CRITICAL_COMMITMENT_APPROVAL_NOTE = exports.ALLOCATION_INPUT_FLAGS = exports.NORTHSTAR_WORKSPACE_CREATE_HELP = exports.NORTHSTAR_WORKSPACE_READ_HELP = exports.NORTHSTAR_WEB_SCOPE_SENTENCE = exports.PERSONAL_NORTHSTAR_WORKSPACE = exports.NORTHSTAR_LIST_DEFAULT_LIMIT = exports.northstarApi = exports.NORTHSTAR_RECOMMENDATION_STATUSES = exports.NORTHSTAR_PRIORITY_TIERS = exports.NORTHSTAR_STATUSES = exports.describeNorthstarReason = exports.NORTHSTAR_REASON_WORDS = exports.NORTHSTAR_REVIEW_STATUSES = exports.NORTHSTAR_MANAGEMENT_INTENT_KINDS = exports.NORTHSTAR_INTENT_VERDICTS = exports.NORTHSTAR_EXECUTION_MODES = exports.NORTHSTAR_APPROVAL_TRIGGERS = exports.NORTHSTAR_ANALYSIS_MODES = void 0;
|
|
4
4
|
exports.intentRisk = intentRisk;
|
|
5
5
|
exports.shortReviewId = shortReviewId;
|
|
6
6
|
exports.lookupReview = lookupReview;
|
|
7
7
|
exports.humanMs = humanMs;
|
|
8
8
|
exports.shortDate = shortDate;
|
|
9
|
-
exports.describeNorthstarReason = describeNorthstarReason;
|
|
10
9
|
exports.describeDecisionRefusal = describeDecisionRefusal;
|
|
11
10
|
exports.describeDecisionOutcome = describeDecisionOutcome;
|
|
12
11
|
exports.listFooter = listFooter;
|
|
12
|
+
exports.shortWorkspaceId = shortWorkspaceId;
|
|
13
|
+
exports.northstarWorkspaceLabel = northstarWorkspaceLabel;
|
|
14
|
+
exports.northstarWorkspaceLine = northstarWorkspaceLine;
|
|
15
|
+
exports.personalNorthstarNote = personalNorthstarNote;
|
|
16
|
+
exports.createdNorthstarNote = createdNorthstarNote;
|
|
13
17
|
exports.renderNorthstarList = renderNorthstarList;
|
|
14
18
|
exports.describeMaxActive = describeMaxActive;
|
|
15
19
|
exports.renderNorthstar = renderNorthstar;
|
|
@@ -55,7 +59,9 @@ Object.defineProperty(exports, "NORTHSTAR_APPROVAL_TRIGGERS", { enumerable: true
|
|
|
55
59
|
Object.defineProperty(exports, "NORTHSTAR_EXECUTION_MODES", { enumerable: true, get: function () { return data_provider_1.NORTHSTAR_EXECUTION_MODES; } });
|
|
56
60
|
Object.defineProperty(exports, "NORTHSTAR_INTENT_VERDICTS", { enumerable: true, get: function () { return data_provider_1.NORTHSTAR_INTENT_VERDICTS; } });
|
|
57
61
|
Object.defineProperty(exports, "NORTHSTAR_MANAGEMENT_INTENT_KINDS", { enumerable: true, get: function () { return data_provider_1.NORTHSTAR_MANAGEMENT_INTENT_KINDS; } });
|
|
62
|
+
Object.defineProperty(exports, "NORTHSTAR_REASON_WORDS", { enumerable: true, get: function () { return data_provider_1.NORTHSTAR_REASON_WORDS; } });
|
|
58
63
|
Object.defineProperty(exports, "NORTHSTAR_REVIEW_STATUSES", { enumerable: true, get: function () { return data_provider_1.NORTHSTAR_REVIEW_STATUSES; } });
|
|
64
|
+
Object.defineProperty(exports, "describeNorthstarReason", { enumerable: true, get: function () { return data_provider_1.describeNorthstarReason; } });
|
|
59
65
|
const format_1 = require("./format");
|
|
60
66
|
const triggers_1 = require("./triggers");
|
|
61
67
|
/**
|
|
@@ -169,86 +175,6 @@ function shortDate(value) {
|
|
|
169
175
|
const date = new Date(value);
|
|
170
176
|
return Number.isNaN(date.getTime()) ? '—' : date.toISOString().replace('T', ' ').slice(0, 16);
|
|
171
177
|
}
|
|
172
|
-
/* --------------------------------------------------------------------------
|
|
173
|
-
* Reason codes in words
|
|
174
|
-
*
|
|
175
|
-
* The server refuses, skips and decides with machine codes
|
|
176
|
-
* (`reviewer_agent_not_always_on`, `northstar_version_drift`, ...). A code is
|
|
177
|
-
* for a script; a person reading a table or an error needs the sentence. This
|
|
178
|
-
* is the ONE place those sentences live, so a command never invents its own
|
|
179
|
-
* wording (the old governance line guessed "offline" for a reviewer that was
|
|
180
|
-
* simply not Always-On) and `--json` can carry the same text as `reasonText`.
|
|
181
|
-
*
|
|
182
|
-
* Entries are CLAUSES, lower-case with no trailing full stop, so a caller can
|
|
183
|
-
* put them after "agent turn not started:" or inside a longer sentence. A code
|
|
184
|
-
* with no entry is returned AS IT IS: an unknown code printed raw is honest,
|
|
185
|
-
* a guessed explanation is not.
|
|
186
|
-
* ------------------------------------------------------------------------ */
|
|
187
|
-
exports.NORTHSTAR_REASON_WORDS = {
|
|
188
|
-
// An agent turn that was not reserved for an agent_assisted review.
|
|
189
|
-
no_reviewer_agent_configured: 'no reviewer agent is set on this northstar',
|
|
190
|
-
northstar_paused: 'the northstar is paused',
|
|
191
|
-
northstar_achieved: 'the northstar is marked achieved',
|
|
192
|
-
northstar_archived: 'the northstar is archived',
|
|
193
|
-
analysis_mode_not_agent_assisted: 'analysis mode is deterministic, so no agent is asked',
|
|
194
|
-
managed_agent_missing: 'the reviewer agent does not exist or is not visible to you',
|
|
195
|
-
always_on_control_unavailable: "the reviewer agent's Always-On state could not be read just now",
|
|
196
|
-
always_on_disabled: 'Always-On is turned off for the reviewer agent',
|
|
197
|
-
always_on_suspended: 'Always-On is suspended for the reviewer agent',
|
|
198
|
-
always_on_stale_generation: "the reviewer agent's Always-On state changed while this ran",
|
|
199
|
-
reviewer_agent_foreign_owner: "the reviewer agent belongs to someone other than this northstar's owner",
|
|
200
|
-
reviewer_agent_not_always_on: 'the reviewer agent is not Always-On (it is not offline; turn Always-On on for it)',
|
|
201
|
-
run_already_reserved: 'an agent run was already reserved for this review',
|
|
202
|
-
envelope_build_failed: 'the review could not be packaged for the agent',
|
|
203
|
-
governance_run_reservation_failed: 'reserving the agent turn failed unexpectedly',
|
|
204
|
-
run_not_reserved: 'the run queue declined to reserve the agent turn',
|
|
205
|
-
enqueue_failed: 'the agent turn could not be queued',
|
|
206
|
-
agent_run_reserved: 'an agent turn is reserved; its results land asynchronously',
|
|
207
|
-
// Why an intent was recorded, held or refused by policy.
|
|
208
|
-
execution_mode_advisory: 'execution mode is advisory: recorded for you to read, nothing is changed',
|
|
209
|
-
execution_mode_approval: 'execution mode is approval: it waits for a person',
|
|
210
|
-
proposal_only: 'a proposal: the governor never applies it, a person does',
|
|
211
|
-
commitment_protected: "the commitment is on this northstar's protected list",
|
|
212
|
-
intent_kind_not_allowed: "this kind of intent is not in the northstar's allowed list",
|
|
213
|
-
critical_commitment_lifecycle: 'it changes a critical commitment, which always needs a person',
|
|
214
|
-
resume_requires_approval: 'resuming always needs a person',
|
|
215
|
-
budget_telemetry_unavailable: 'the automatic-change budget could not be read, so it fell back to approval',
|
|
216
|
-
critical_demotion: 'it would demote a critical commitment',
|
|
217
|
-
tier_change_too_large: 'the tier change is larger than one step',
|
|
218
|
-
weight_change_too_large: 'the weight change exceeds the allowed relative change',
|
|
219
|
-
bounds_increase_compute: 'it would buy more compute',
|
|
220
|
-
bounds_change_exceeds_max_relative_change: 'the bounds change exceeds the allowed relative change',
|
|
221
|
-
auto_pause_evidence_incomplete: 'the evidence for an automatic pause is incomplete',
|
|
222
|
-
no_auto_rule_for_kind: 'no automatic rule covers this kind of change',
|
|
223
|
-
auto_mutation_review_cap_reached: 'this review already made its maximum automatic changes',
|
|
224
|
-
auto_mutation_daily_cap_reached: "today's automatic-change cap is used up",
|
|
225
|
-
mutation_cooldown_active: 'the commitment was changed automatically too recently (cooldown)',
|
|
226
|
-
commitment_not_linked_to_northstar: 'the commitment is no longer linked to this northstar',
|
|
227
|
-
commitment_not_found: 'the commitment no longer exists',
|
|
228
|
-
approved_by_user: 'approved by a person',
|
|
229
|
-
rejected_by_user: 'rejected by a person',
|
|
230
|
-
// Why a decision (approve / reject) was refused or did not take effect.
|
|
231
|
-
commitment_version_drift: 'the commitment changed after this was proposed',
|
|
232
|
-
northstar_version_drift: 'the northstar changed after this was proposed',
|
|
233
|
-
// Two ways to the same ending: a newer review asks the same question again, or a concurrent review
|
|
234
|
-
// is already making (or has made) this very change. Either way another review has it.
|
|
235
|
-
superseded: 'another review already proposed or made the same change',
|
|
236
|
-
recommendation_dismissed: 'its recommendation was dismissed',
|
|
237
|
-
intent_not_found: "no such intent in this northstar's 50 most recent reviews",
|
|
238
|
-
review_not_found: 'no such review on this northstar',
|
|
239
|
-
northstar_not_found: 'no such northstar',
|
|
240
|
-
owner_approval_required: "only the northstar's owner can approve a proposal",
|
|
241
|
-
transition_lost: 'another decision landed first',
|
|
242
|
-
claim_lost: 'another process is already applying it',
|
|
243
|
-
mutation_error: 'the change failed part-way and was left for the repair pass',
|
|
244
|
-
proposal_kind_requires_manual_action: 'this kind of proposal is completed by hand',
|
|
245
|
-
};
|
|
246
|
-
/** A code in words; an unknown code comes back unchanged, an absent one says so. */
|
|
247
|
-
function describeNorthstarReason(code) {
|
|
248
|
-
if (!code)
|
|
249
|
-
return 'no reason was recorded';
|
|
250
|
-
return exports.NORTHSTAR_REASON_WORDS[code] ?? code;
|
|
251
|
-
}
|
|
252
178
|
function utcStamp(value) {
|
|
253
179
|
const stamp = shortDate(typeof value === 'string' ? value : undefined);
|
|
254
180
|
return stamp === '—' ? undefined : `${stamp} UTC`;
|
|
@@ -303,7 +229,7 @@ function describeDecisionRefusal(body, ctx) {
|
|
|
303
229
|
const when = utcStamp(body.decidedAt);
|
|
304
230
|
return {
|
|
305
231
|
code,
|
|
306
|
-
text: `This intent was already invalidated${why ? ` (${describeNorthstarReason(why)})` : ''}${when ? ` at ${when}` : ''} — nothing changed.`,
|
|
232
|
+
text: `This intent was already invalidated${why ? ` (${(0, data_provider_1.describeNorthstarReason)(why)})` : ''}${when ? ` at ${when}` : ''} — nothing changed.`,
|
|
307
233
|
};
|
|
308
234
|
}
|
|
309
235
|
let by = '';
|
|
@@ -329,7 +255,7 @@ function describeDecisionRefusal(body, ctx) {
|
|
|
329
255
|
text: `No intent ${ctx.intentId} in this northstar's 50 most recent reviews. If it is older, pass --review <review-id>.`,
|
|
330
256
|
};
|
|
331
257
|
}
|
|
332
|
-
const words = describeNorthstarReason(code);
|
|
258
|
+
const words = (0, data_provider_1.describeNorthstarReason)(code);
|
|
333
259
|
return { code, text: `${words.charAt(0).toUpperCase()}${words.slice(1)}.` };
|
|
334
260
|
}
|
|
335
261
|
/**
|
|
@@ -390,7 +316,7 @@ function describeDecisionOutcome(result, ctx) {
|
|
|
390
316
|
text = `${what}, so the approval was not applied and the intent was invalidated. ${reviewNow}`;
|
|
391
317
|
}
|
|
392
318
|
else if (code) {
|
|
393
|
-
text = `Your approval is recorded, but nothing was applied: ${describeNorthstarReason(code)}.`;
|
|
319
|
+
text = `Your approval is recorded, but nothing was applied: ${(0, data_provider_1.describeNorthstarReason)(code)}.`;
|
|
394
320
|
}
|
|
395
321
|
else {
|
|
396
322
|
text = `Your approval is recorded, but the change has not landed yet (verdict: ${verdict}). It is retried automatically; check \`${bin} northstars intents list ${ctx.northstarId}\`.`;
|
|
@@ -423,6 +349,93 @@ function listFooter(input) {
|
|
|
423
349
|
? `${head} --limit is already at its maximum (${input.max}), so narrow the list with --status`
|
|
424
350
|
: `${head} raise --limit (max ${input.max})`;
|
|
425
351
|
}
|
|
352
|
+
/* --------------------------------------------------------------------------
|
|
353
|
+
* Which workspace a northstar lives in (OSK-21643)
|
|
354
|
+
*
|
|
355
|
+
* The web Northstars list is scoped to the person's ACTIVE workspace and sends `X-Workspace-Id`, so
|
|
356
|
+
* a northstar with no workspace is hidden from it while a workspace is active. The API creates a
|
|
357
|
+
* northstar in the workspace the request names and in none otherwise, which is what a CLI without
|
|
358
|
+
* `--workspace` sends. Nothing on either surface said so, and a northstar made here simply seemed
|
|
359
|
+
* to be missing from the web.
|
|
360
|
+
*
|
|
361
|
+
* Every sentence about it comes from this block, so `list`, `show`, `create`, their `--help` and
|
|
362
|
+
* the docs cannot say different things. The docs quote `personalNorthstarNote` byte for byte and a
|
|
363
|
+
* spec holds them to it.
|
|
364
|
+
* -------------------------------------------------------------------------- */
|
|
365
|
+
/** What a northstar with no workspace is called wherever a workspace is named. */
|
|
366
|
+
exports.PERSONAL_NORTHSTAR_WORKSPACE = 'personal';
|
|
367
|
+
/** Why a personal northstar can look missing on the web. The one sentence the others build on. */
|
|
368
|
+
exports.NORTHSTAR_WEB_SCOPE_SENTENCE = `The ${data_provider_1.BRAND.displayName} web Northstars list shows only the active workspace's Northstars, so a personal one is hidden there while a workspace is active.`;
|
|
369
|
+
/** Help text shared by `list` and `show`: where a northstar's workspace is printed. */
|
|
370
|
+
exports.NORTHSTAR_WORKSPACE_READ_HELP = `A northstar belongs to the workspace it was created in, or is "${exports.PERSONAL_NORTHSTAR_WORKSPACE}" when it has none. ` +
|
|
371
|
+
`${exports.NORTHSTAR_WEB_SCOPE_SENTENCE}`;
|
|
372
|
+
/** Help text for `create`: which workspace it lands in, and how to choose one. */
|
|
373
|
+
exports.NORTHSTAR_WORKSPACE_CREATE_HELP = 'The northstar is created in the workspace the global --workspace <id> flag names, or the one chosen once with ' +
|
|
374
|
+
'`workspaces use`. With neither, or for a workspace you are not a member of, it is personal (no workspace), and ' +
|
|
375
|
+
'the output says so. ' +
|
|
376
|
+
`${exports.NORTHSTAR_WEB_SCOPE_SENTENCE} An Agent session always uses its own session workspace, and --workspace does not change it.`;
|
|
377
|
+
/** The workspace id a northstar carries, or undefined for a personal one (absent, null or blank). */
|
|
378
|
+
function northstarWorkspaceId(northstar) {
|
|
379
|
+
const id = typeof northstar.workspaceId === 'string' ? northstar.workspaceId.trim() : '';
|
|
380
|
+
return id || undefined;
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* A workspace id short enough for a table: `038b2251-6a0c-4e2b-9f7d-c1d0f3a8b2aa` is `038b2251…` and
|
|
384
|
+
* `ws_01H8XKPQ7ABCDEF` is `ws_01H8XKPQ…`. Unlike a review id (`shortReviewId`) the short form is NOT
|
|
385
|
+
* accepted back by `--workspace`, which needs the full id, so a shortened value ends in `…` to say
|
|
386
|
+
* it is a prefix. The full id is in `--json` and in `skrr workspaces list`.
|
|
387
|
+
*/
|
|
388
|
+
const WORKSPACE_ID_SHORT_CHARS = 8;
|
|
389
|
+
function shortWorkspaceId(id) {
|
|
390
|
+
const prefixed = /^([a-z]+_)(.+)$/i.exec(id);
|
|
391
|
+
const prefix = prefixed?.[1] ?? '';
|
|
392
|
+
const rest = prefixed?.[2] ?? id;
|
|
393
|
+
return rest.length > WORKSPACE_ID_SHORT_CHARS
|
|
394
|
+
? `${prefix}${rest.slice(0, WORKSPACE_ID_SHORT_CHARS)}…`
|
|
395
|
+
: id;
|
|
396
|
+
}
|
|
397
|
+
/** The workspace a northstar lives in, in one word or id: `personal` when it has none. */
|
|
398
|
+
function northstarWorkspaceLabel(northstar, options = {}) {
|
|
399
|
+
const id = northstarWorkspaceId(northstar);
|
|
400
|
+
if (!id)
|
|
401
|
+
return exports.PERSONAL_NORTHSTAR_WORKSPACE;
|
|
402
|
+
return options.short ? shortWorkspaceId(id) : id;
|
|
403
|
+
}
|
|
404
|
+
/** The line `show` and `create` print: the full workspace id, or `personal`. */
|
|
405
|
+
function northstarWorkspaceLine(northstar) {
|
|
406
|
+
return `Workspace ${northstarWorkspaceLabel(northstar)}`;
|
|
407
|
+
}
|
|
408
|
+
/** Read from the environment `--workspace` and `workspaces use` both write (`withWorkspaceHeader`). */
|
|
409
|
+
function ambientPersonalContext(env = process.env) {
|
|
410
|
+
return {
|
|
411
|
+
autonomous: env.OVERSKY_AUTONOMOUS_SESSION === '1',
|
|
412
|
+
requestedWorkspaceId: env.OVERSKY_WORKSPACE_ID?.trim() || undefined,
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* What `create` adds when the northstar landed in no workspace. Three causes, three endings:
|
|
417
|
+
*
|
|
418
|
+
* - an Agent session takes its workspace from the session and ignores `--workspace`, so telling
|
|
419
|
+
* it to pass the flag would send it round in a circle;
|
|
420
|
+
* - a workspace WAS requested but the person is not a member of it: the server does not refuse,
|
|
421
|
+
* it creates the northstar personal (`resolveWorkspaceContext`), so "pass --workspace" would
|
|
422
|
+
* be said to someone who just did;
|
|
423
|
+
* - nothing was requested, and the way out is the flag.
|
|
424
|
+
*/
|
|
425
|
+
function personalNorthstarNote(context = ambientPersonalContext()) {
|
|
426
|
+
const lead = `Not in a workspace (${exports.PERSONAL_NORTHSTAR_WORKSPACE}). ${exports.NORTHSTAR_WEB_SCOPE_SENTENCE}`;
|
|
427
|
+
if (context.autonomous) {
|
|
428
|
+
return `${lead} This Agent session has no workspace to create it in, and --workspace does not change that.`;
|
|
429
|
+
}
|
|
430
|
+
if (context.requestedWorkspaceId) {
|
|
431
|
+
return `${lead} Workspace ${context.requestedWorkspaceId} was requested, but a northstar is created in a workspace only for a member of it: check \`skrr workspaces list\`.`;
|
|
432
|
+
}
|
|
433
|
+
return `${lead} Pass the global --workspace <id> flag to create one inside a workspace.`;
|
|
434
|
+
}
|
|
435
|
+
/** The note for a northstar `create` just made: nothing when it is in a workspace. */
|
|
436
|
+
function createdNorthstarNote(northstar, context) {
|
|
437
|
+
return northstarWorkspaceId(northstar) ? undefined : personalNorthstarNote(context);
|
|
438
|
+
}
|
|
426
439
|
function renderNorthstarList(rows, log) {
|
|
427
440
|
if (rows.length === 0) {
|
|
428
441
|
log('No northstars.');
|
|
@@ -437,6 +450,7 @@ function renderNorthstarList(rows, log) {
|
|
|
437
450
|
commitments: String(row.state?.activeCommitmentCount ?? 0),
|
|
438
451
|
open: String(row.state?.openRecommendationCount ?? 0),
|
|
439
452
|
reviewed: shortDate(row.state?.lastReviewedAt),
|
|
453
|
+
workspace: northstarWorkspaceLabel(row, { short: true }),
|
|
440
454
|
})), [
|
|
441
455
|
{ key: 'id', header: 'ID' },
|
|
442
456
|
{ key: 'title', header: 'TITLE', maxWidth: 40 },
|
|
@@ -446,6 +460,7 @@ function renderNorthstarList(rows, log) {
|
|
|
446
460
|
{ key: 'commitments', header: 'ACTIVE' },
|
|
447
461
|
{ key: 'open', header: 'OPEN REC' },
|
|
448
462
|
{ key: 'reviewed', header: 'REVIEWED' },
|
|
463
|
+
{ key: 'workspace', header: 'WORKSPACE' },
|
|
449
464
|
], log);
|
|
450
465
|
}
|
|
451
466
|
/** A budget that was never set says so; `—` is kept for a timestamp that does not exist yet. */
|
|
@@ -470,6 +485,7 @@ function renderNorthstar(status, log) {
|
|
|
470
485
|
log(`Attention total ${notSet(ns.attentionBudget?.totalWeight)} · max active ${describeMaxActive(ns.attentionBudget?.maxActiveCommitments)}`);
|
|
471
486
|
const cadence = ns.reviewCadence ?? {};
|
|
472
487
|
log(`Review ${cadence.mode ?? 'manual'}${cadence.intervalMs ? ` every ${humanMs(cadence.intervalMs)}` : ''} next ${shortDate(cadence.nextReviewAt)} last ${shortDate(ns.state?.lastReviewedAt)}`);
|
|
488
|
+
log(northstarWorkspaceLine(ns));
|
|
473
489
|
if (ns.goalIds?.length)
|
|
474
490
|
log(`Goals ${ns.goalIds.join(', ')}`);
|
|
475
491
|
if (ns.agentIds?.length)
|
|
@@ -544,15 +560,15 @@ function reviewerReadiness(reviewer) {
|
|
|
544
560
|
if (admission.admitted)
|
|
545
561
|
return ' [ready]';
|
|
546
562
|
if (admission.reason === 'analysis_mode_not_agent_assisted') {
|
|
547
|
-
return ` [not in use: ${describeNorthstarReason(admission.reason)}]`;
|
|
563
|
+
return ` [not in use: ${(0, data_provider_1.describeNorthstarReason)(admission.reason)}]`;
|
|
548
564
|
}
|
|
549
|
-
return ` [not ready: ${describeNorthstarReason(admission.reason)}]`;
|
|
565
|
+
return ` [not ready: ${(0, data_provider_1.describeNorthstarReason)(admission.reason)}]`;
|
|
550
566
|
}
|
|
551
567
|
if (reviewer.ready)
|
|
552
568
|
return ' [ready]';
|
|
553
569
|
// No reason from an older server is "unknown", never a guess: the old fallback
|
|
554
570
|
// said "offline" about an agent that was reachable and merely not Always-On.
|
|
555
|
-
return ` [not ready: ${reviewer.reason ? describeNorthstarReason(reviewer.reason) : 'reason unknown'}]`;
|
|
571
|
+
return ` [not ready: ${reviewer.reason ? (0, data_provider_1.describeNorthstarReason)(reviewer.reason) : 'reason unknown'}]`;
|
|
556
572
|
}
|
|
557
573
|
/** The `governance configure` flags that move what a commitment's allocation is computed from. */
|
|
558
574
|
exports.ALLOCATION_INPUT_FLAGS = [
|
|
@@ -761,13 +777,13 @@ function reviewNote(review) {
|
|
|
761
777
|
}
|
|
762
778
|
function agentTurnNote(review) {
|
|
763
779
|
if (review.status === 'failed') {
|
|
764
|
-
return `failed: ${review.errorMessage ?? describeNorthstarReason(review.reason)}`;
|
|
780
|
+
return `failed: ${review.errorMessage ?? (0, data_provider_1.describeNorthstarReason)(review.reason)}`;
|
|
765
781
|
}
|
|
766
782
|
if (review.status === 'awaiting_agent')
|
|
767
783
|
return 'waiting for the agent turn';
|
|
768
784
|
if (review.analysisMode === 'agent_assisted' && review.status === 'succeeded') {
|
|
769
785
|
if (review.reason && AGENT_TURN_NOT_STARTED.has(review.reason)) {
|
|
770
|
-
return `agent turn not started: ${describeNorthstarReason(review.reason)}`;
|
|
786
|
+
return `agent turn not started: ${(0, data_provider_1.describeNorthstarReason)(review.reason)}`;
|
|
771
787
|
}
|
|
772
788
|
if (!review.autonomousRunId)
|
|
773
789
|
return 'agent turn not started: no reason was recorded';
|
|
@@ -776,7 +792,7 @@ function agentTurnNote(review) {
|
|
|
776
792
|
}
|
|
777
793
|
/** `Reason` line of one review: the words, with the raw code kept beside them for grep. */
|
|
778
794
|
function describeReviewReason(reason) {
|
|
779
|
-
const words = describeNorthstarReason(reason);
|
|
795
|
+
const words = (0, data_provider_1.describeNorthstarReason)(reason);
|
|
780
796
|
return words === reason ? reason : `${words} [${reason}]`;
|
|
781
797
|
}
|
|
782
798
|
function renderReviewList(reviews, log) {
|
|
@@ -848,7 +864,7 @@ function renderIntentList(intents, log, options = {}) {
|
|
|
848
864
|
// The intent's OWN rating (see `intentRisk`), never its recommendation's.
|
|
849
865
|
risk: intentRisk(intent),
|
|
850
866
|
// No sentence from the governor: the policy code in words (an unknown code stays as it is).
|
|
851
|
-
why: intent.reason ?? (intent.verdictReason ? describeNorthstarReason(intent.verdictReason) : '—'),
|
|
867
|
+
why: intent.reason ?? (intent.verdictReason ? (0, data_provider_1.describeNorthstarReason)(intent.verdictReason) : '—'),
|
|
852
868
|
code: intent.verdictReason ?? '',
|
|
853
869
|
source: intent.source ?? 'deterministic',
|
|
854
870
|
}));
|