@skrr-ai/cli 0.1.129 → 0.1.131
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/agent/message/send.js +3 -0
- 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/schedule-trace.js +14 -5
- package/dist/commands/agents/triggers/policy.js +3 -1
- package/dist/commands/harnesses/prune.d.ts +1 -0
- package/dist/commands/harnesses/prune.js +24 -4
- 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/exec-runtime-binary.js +3 -2
- package/dist/lib/harnesses.d.ts +4 -1
- package/dist/lib/harnesses.js +3 -0
- 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/schedule.d.ts +2 -0
- package/dist/lib/schedule.js +12 -0
- 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 +1602 -1578
- package/oclif.manifest.json +44 -28
- package/package.json +1 -1
package/dist/base-command.js
CHANGED
|
@@ -1617,6 +1617,21 @@ class BaseCommand extends core_1.Command {
|
|
|
1617
1617
|
details: envelopeDetails(body),
|
|
1618
1618
|
});
|
|
1619
1619
|
}
|
|
1620
|
+
else if (commitmentBodyErrorCode(body)) {
|
|
1621
|
+
// These Commitment routes send the code in upper-case `error`, which `refusalCodeOf` does not
|
|
1622
|
+
// read as a code, so the documented code (the guide's refusal table) never reached `error.code`:
|
|
1623
|
+
// a program saw `HTTP_400` or `HTTP_409` and had to know to look in `details.error` (OSK-21102).
|
|
1624
|
+
// Read them by name, as the activation and Compass refusals are, rather than widening
|
|
1625
|
+
// `refusalCodeOf` for every route. The message is the server's, unchanged.
|
|
1626
|
+
this.failWithCliError({
|
|
1627
|
+
message: say(bodyMessage || e.message),
|
|
1628
|
+
code: commitmentBodyErrorCode(body),
|
|
1629
|
+
status: e.status,
|
|
1630
|
+
exit: 1,
|
|
1631
|
+
retryable: false,
|
|
1632
|
+
details: envelopeDetails(body),
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1620
1635
|
else if ((e.status === 409 || e.status === 404) && (0, compass_refusals_1.compassRefusalCode)(body)) {
|
|
1621
1636
|
// The Compass routes sent the code in upper-case `error`, which `refusalCodeOf` does not
|
|
1622
1637
|
// read as a code, so a stale writer saw `HTTP_409` and a mistyped version `NOT_FOUND`,
|
|
@@ -2309,6 +2324,21 @@ function apiRefusal(err) {
|
|
|
2309
2324
|
}
|
|
2310
2325
|
/** The typed code the server refuses a Commitment activation with. */
|
|
2311
2326
|
const COMMITMENT_ACTIVATION_BLOCKED = 'COMMITMENT_ACTIVATION_BLOCKED';
|
|
2327
|
+
/**
|
|
2328
|
+
* Commitment refusals the routes send in upper-case `error` (OSK-21102): the retired `mode` and
|
|
2329
|
+
* `policy.autonomyMode` fields, a cadence window no single wall-clock pattern holds, and an edit that
|
|
2330
|
+
* tries to activate a draft (which only `resume` may do).
|
|
2331
|
+
*/
|
|
2332
|
+
const COMMITMENT_BODY_ERROR_CODES = new Set([
|
|
2333
|
+
'COMMITMENT_MODE_RETIRED',
|
|
2334
|
+
'COMMITMENT_POLICY_RETIRED',
|
|
2335
|
+
'COMMITMENT_CADENCE_WINDOW_UNSUPPORTED',
|
|
2336
|
+
'COMMITMENT_RESUME_REQUIRES_EXPLICIT_ACTION',
|
|
2337
|
+
]);
|
|
2338
|
+
function commitmentBodyErrorCode(body) {
|
|
2339
|
+
const value = stringProp(body, 'error');
|
|
2340
|
+
return value && COMMITMENT_BODY_ERROR_CODES.has(value) ? value : undefined;
|
|
2341
|
+
}
|
|
2312
2342
|
/**
|
|
2313
2343
|
* The remedy a refused Commitment activation carries.
|
|
2314
2344
|
*
|
|
@@ -48,7 +48,9 @@ class AgendaTriggerCreate extends base_command_1.BaseCommand {
|
|
|
48
48
|
description: 'IANA timezone for the cron (defaults to UTC). Only used with --kind=schedule.',
|
|
49
49
|
}),
|
|
50
50
|
'delay-minutes': core_1.Flags.integer({
|
|
51
|
-
description: 'Minutes from now to fire — required when --kind=self_scheduled.
|
|
51
|
+
description: 'Minutes from now to fire — required when --kind=self_scheduled. ' +
|
|
52
|
+
`Range: ${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MIN_MINUTES}..${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MAX_MINUTES} ` +
|
|
53
|
+
`(${data_provider_1.AGENDA_SELF_SCHEDULED_DELAY_MIN_MINUTES} minutes..24 hours).`,
|
|
52
54
|
}),
|
|
53
55
|
'max-fires-per-day': core_1.Flags.integer({
|
|
54
56
|
description: 'Daily cap on fires. Required for self_scheduled (1..96); defaults to 96 for schedule when omitted.',
|
|
@@ -91,6 +93,12 @@ class AgendaTriggerCreate extends base_command_1.BaseCommand {
|
|
|
91
93
|
if (typeof flags['delay-minutes'] !== 'number') {
|
|
92
94
|
this.error('--delay-minutes is required when --kind=self_scheduled', { exit: 2 });
|
|
93
95
|
}
|
|
96
|
+
// The server holds the same range, but its refusal arrives after the round trip. The sentence
|
|
97
|
+
// is data-provider's, so what is said here and what the route says cannot differ (OSK-21500).
|
|
98
|
+
const delayRefusal = (0, data_provider_1.describeAgendaDelayMinutesRefusal)(flags['delay-minutes']);
|
|
99
|
+
if (delayRefusal) {
|
|
100
|
+
this.error(`--delay-minutes: ${delayRefusal}`, { exit: 2 });
|
|
101
|
+
}
|
|
94
102
|
if (typeof flags['max-fires-per-day'] !== 'number') {
|
|
95
103
|
this.error('--max-fires-per-day is required when --kind=self_scheduled', { exit: 2 });
|
|
96
104
|
}
|
|
@@ -747,6 +747,9 @@ function normalizeIntentPayload({ agentId, jsonBody, merged, }) {
|
|
|
747
747
|
const overlayChannel = optionalString(merged.channelId);
|
|
748
748
|
const overlayThreadTs = optionalString(merged.threadTs);
|
|
749
749
|
const destinationKind = pickDestinationKind({ bodyDestination, overlayKind });
|
|
750
|
+
if (destinationKind !== 'slack_channel' && (overlayThreadTs || bodyDestination?.threadTs)) {
|
|
751
|
+
throw invalidIntent('--thread-ts / destination.threadTs only applies to slack_channel.');
|
|
752
|
+
}
|
|
750
753
|
const destination = buildDestination({
|
|
751
754
|
kind: destinationKind,
|
|
752
755
|
bodyDestination,
|
|
@@ -32,5 +32,11 @@ export default class AgentsReflect extends BaseCommand {
|
|
|
32
32
|
run(): Promise<void>;
|
|
33
33
|
private runDisable;
|
|
34
34
|
private runEnableOrRetune;
|
|
35
|
+
/**
|
|
36
|
+
* Parse `--set-cadence` and apply the reflection window. A cadence that is not one
|
|
37
|
+
* (`0`, `1 month`, empty) is a usage error naming the rule, raised before anything is
|
|
38
|
+
* written, not an API failure.
|
|
39
|
+
*/
|
|
40
|
+
private boundRequestedCadence;
|
|
35
41
|
private runStatus;
|
|
36
42
|
}
|
|
@@ -55,12 +55,16 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
55
55
|
this.requireAuth();
|
|
56
56
|
const { args, flags } = await this.parse(AgentsReflect);
|
|
57
57
|
const agentId = args.id;
|
|
58
|
+
// Only the status view prints whether the loop's fires run, so only it asks the server for
|
|
59
|
+
// that verdict (a policy read); enabling, retuning and pausing do not.
|
|
60
|
+
const isStatus = !flags.disable && !flags.enable && flags['set-cadence'] === undefined;
|
|
58
61
|
let existing;
|
|
59
62
|
try {
|
|
60
63
|
const list = await triggers_1.triggerApi.list((0, triggers_1.withQuery)('/api/triggers', {
|
|
61
64
|
scope: 'agent',
|
|
62
65
|
refEntity: (0, triggers_1.agentRefEntity)(agentId),
|
|
63
66
|
limit: 200,
|
|
67
|
+
include: isStatus ? 'autonomy' : undefined,
|
|
64
68
|
}));
|
|
65
69
|
existing = (0, reflection_1.findReflectionTrigger)(list.triggers ?? []);
|
|
66
70
|
}
|
|
@@ -103,14 +107,23 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
103
107
|
// Resolve the target cadence: explicit --set-cadence, else keep the existing
|
|
104
108
|
// interval when just --enabling, else the default.
|
|
105
109
|
let minutes;
|
|
110
|
+
let bounds = null;
|
|
106
111
|
if (flags['set-cadence'] !== undefined) {
|
|
107
|
-
|
|
112
|
+
bounds = this.boundRequestedCadence(flags['set-cadence']);
|
|
113
|
+
minutes = bounds.minutes;
|
|
108
114
|
}
|
|
109
115
|
else {
|
|
110
116
|
minutes =
|
|
111
117
|
(existing && (0, reflection_1.reflectionCadenceMinutes)(existing)) || reflection_1.DEFAULT_REFLECTION_CADENCE_MINUTES;
|
|
112
118
|
}
|
|
113
119
|
const cadence = (0, reflection_1.describeCadenceMinutes)(minutes);
|
|
120
|
+
// What was ASKED for, beside what was applied: a request the window moved is said so
|
|
121
|
+
// (OSK-21493), in the text and in `--json`, instead of only the clamped number.
|
|
122
|
+
const clampNote = bounds ? (0, reflection_1.describeCadenceClamp)(bounds) : null;
|
|
123
|
+
const requested = {
|
|
124
|
+
requestedCadence: bounds ? (0, reflection_1.describeCadenceMinutes)(bounds.requestedMinutes) : null,
|
|
125
|
+
...(clampNote ? { clamped: true, note: clampNote } : {}),
|
|
126
|
+
};
|
|
114
127
|
if (existing?.id) {
|
|
115
128
|
const patch = {
|
|
116
129
|
enabled: true,
|
|
@@ -124,10 +137,12 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
124
137
|
}
|
|
125
138
|
const updated = await triggers_1.triggerApi.update(existing.id, patch);
|
|
126
139
|
if (flags.json) {
|
|
127
|
-
this.log(JSON.stringify({ ok: true, agentId, action: 'retuned', cadence, trigger: updated }, null, 2));
|
|
140
|
+
this.log(JSON.stringify({ ok: true, agentId, action: 'retuned', ...requested, cadence, trigger: updated }, null, 2));
|
|
128
141
|
return;
|
|
129
142
|
}
|
|
130
143
|
this.log(`Self-reflection loop retuned — agent ${agentId} now reflects every ${cadence}.`);
|
|
144
|
+
if (clampNote)
|
|
145
|
+
this.log(`Note: ${clampNote}`);
|
|
131
146
|
this.log(`Trigger: ${existing.id}`);
|
|
132
147
|
return;
|
|
133
148
|
}
|
|
@@ -138,12 +153,30 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
138
153
|
});
|
|
139
154
|
const created = await triggers_1.triggerApi.create(payload);
|
|
140
155
|
if (flags.json) {
|
|
141
|
-
this.log(JSON.stringify({ ok: true, agentId, action: 'enabled', cadence, trigger: created }, null, 2));
|
|
156
|
+
this.log(JSON.stringify({ ok: true, agentId, action: 'enabled', ...requested, cadence, trigger: created }, null, 2));
|
|
142
157
|
return;
|
|
143
158
|
}
|
|
144
159
|
this.log(`Self-reflection loop enabled — agent ${agentId} will reflect every ${cadence}.`);
|
|
160
|
+
if (clampNote)
|
|
161
|
+
this.log(`Note: ${clampNote}`);
|
|
145
162
|
this.log(`Trigger: ${created.id ?? '(created)'}`);
|
|
146
|
-
this.log(`Next run: ${(0,
|
|
163
|
+
this.log(`Next run: ${(0, reflection_1.reflectionNextRunLabel)(created)}`);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* Parse `--set-cadence` and apply the reflection window. A cadence that is not one
|
|
167
|
+
* (`0`, `1 month`, empty) is a usage error naming the rule, raised before anything is
|
|
168
|
+
* written, not an API failure.
|
|
169
|
+
*/
|
|
170
|
+
boundRequestedCadence(raw) {
|
|
171
|
+
try {
|
|
172
|
+
return (0, reflection_1.applyCadenceBounds)((0, reflection_1.parseCadenceToMinutes)(raw));
|
|
173
|
+
}
|
|
174
|
+
catch (err) {
|
|
175
|
+
if (err instanceof reflection_1.InvalidCadenceError) {
|
|
176
|
+
this.failWithCliError({ message: err.message, code: err.code, exit: 2, retryable: false });
|
|
177
|
+
}
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
147
180
|
}
|
|
148
181
|
runStatus(agentId, existing, json) {
|
|
149
182
|
if (!existing) {
|
|
@@ -165,7 +198,8 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
165
198
|
enabled: existing.enabled !== false,
|
|
166
199
|
cadence,
|
|
167
200
|
cadenceMinutes: minutes ?? null,
|
|
168
|
-
nextRun: (0,
|
|
201
|
+
nextRun: (0, reflection_1.reflectionNextRunLabel)(existing),
|
|
202
|
+
...(0, reflection_1.reflectionAutonomyFields)(existing),
|
|
169
203
|
trigger: existing,
|
|
170
204
|
}, null, 2));
|
|
171
205
|
return;
|
|
@@ -173,8 +207,13 @@ class AgentsReflect extends base_command_1.BaseCommand {
|
|
|
173
207
|
this.log(`Self-reflection loop for agent ${agentId}:`);
|
|
174
208
|
this.log(` Status: ${existing.enabled === false ? 'paused' : 'active'}`);
|
|
175
209
|
this.log(` Cadence: every ${cadence}`);
|
|
176
|
-
this.log(` Next run: ${(0,
|
|
210
|
+
this.log(` Next run: ${(0, reflection_1.reflectionNextRunLabel)(existing)}`);
|
|
177
211
|
this.log(` Trigger: ${existing.id ?? '-'}`);
|
|
212
|
+
// "active" with a next run says the loop is armed, not that its fires run: under some
|
|
213
|
+
// initiative modes they only create an inbox suggestion (OSK-21606).
|
|
214
|
+
const autonomyNote = (0, reflection_1.reflectionAutonomyNote)(existing, this.config.bin);
|
|
215
|
+
if (autonomyNote)
|
|
216
|
+
this.log(` Note: ${autonomyNote}`);
|
|
178
217
|
if (existing.enabled === false) {
|
|
179
218
|
this.log('');
|
|
180
219
|
this.log('Re-enable it with: ' + `${this.config.bin} agents reflect ${agentId} --enable`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BaseCommand } from '../../base-command';
|
|
2
|
+
import { type TriggerDefinition } from '../../lib/triggers';
|
|
2
3
|
/**
|
|
3
4
|
* `skrr agents schedule-overview <agent-id>` — the one-call "full visibility"
|
|
4
5
|
* view an agent needs when it wakes to think: PAST (recent fires) + FUTURE
|
|
@@ -25,3 +26,19 @@ export default class AgentsScheduleOverview extends BaseCommand {
|
|
|
25
26
|
};
|
|
26
27
|
run(): Promise<void>;
|
|
27
28
|
}
|
|
29
|
+
/** The reflection loop as `--json` reports it. A paused loop has no next run (`-`). */
|
|
30
|
+
export declare function reflectionSummary(reflection: TriggerDefinition): {
|
|
31
|
+
autonomy?: "runs" | "suggests_only";
|
|
32
|
+
note?: string;
|
|
33
|
+
triggerId: string | undefined;
|
|
34
|
+
enabled: boolean;
|
|
35
|
+
cadence: string;
|
|
36
|
+
nextRun: string;
|
|
37
|
+
};
|
|
38
|
+
/** The reflection loop as the text view prints it. */
|
|
39
|
+
export declare function reflectionLine(reflection: TriggerDefinition): string;
|
|
40
|
+
/**
|
|
41
|
+
* The line under the loop when the server says its fires do not run for the owner (OSK-21606),
|
|
42
|
+
* else null. "active · next <time>" only says the loop is armed.
|
|
43
|
+
*/
|
|
44
|
+
export declare function reflectionNoteLine(reflection: TriggerDefinition, bin: string): string | null;
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reflectionSummary = reflectionSummary;
|
|
4
|
+
exports.reflectionLine = reflectionLine;
|
|
5
|
+
exports.reflectionNoteLine = reflectionNoteLine;
|
|
3
6
|
const core_1 = require("@oclif/core");
|
|
4
7
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
8
|
const base_command_1 = require("../../base-command");
|
|
@@ -51,6 +54,8 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
|
|
|
51
54
|
scope: 'agent',
|
|
52
55
|
refEntity: (0, triggers_1.agentRefEntity)(agentId),
|
|
53
56
|
limit: 200,
|
|
57
|
+
// Whether the reflection loop's fires run: its row carries the verdict (OSK-21606).
|
|
58
|
+
include: 'autonomy',
|
|
54
59
|
})),
|
|
55
60
|
// Filter SERVER-side. Fetching an unfiltered page and filtering in memory
|
|
56
61
|
// silently truncates the section for any user with more tasks than
|
|
@@ -72,14 +77,7 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
|
|
|
72
77
|
if (flags.json) {
|
|
73
78
|
this.log(JSON.stringify({
|
|
74
79
|
agentId,
|
|
75
|
-
reflection: reflection
|
|
76
|
-
? {
|
|
77
|
-
triggerId: reflection.id,
|
|
78
|
-
enabled: reflection.enabled !== false,
|
|
79
|
-
cadence: describeCadence(reflection),
|
|
80
|
-
nextRun: (0, triggers_1.nextRunLabel)(reflection),
|
|
81
|
-
}
|
|
82
|
-
: null,
|
|
80
|
+
reflection: reflection ? reflectionSummary(reflection) : null,
|
|
83
81
|
upcoming,
|
|
84
82
|
recentFires: fires,
|
|
85
83
|
openTasks,
|
|
@@ -97,7 +95,10 @@ class AgentsScheduleOverview extends base_command_1.BaseCommand {
|
|
|
97
95
|
this.log(` none — enable with: ${this.config.bin} agents reflect ${agentId} --set-cadence 6h`);
|
|
98
96
|
}
|
|
99
97
|
else {
|
|
100
|
-
this.log(
|
|
98
|
+
this.log(reflectionLine(reflection));
|
|
99
|
+
const note = reflectionNoteLine(reflection, this.config.bin);
|
|
100
|
+
if (note)
|
|
101
|
+
this.log(note);
|
|
101
102
|
}
|
|
102
103
|
// FUTURE.
|
|
103
104
|
this.log('');
|
|
@@ -190,6 +191,29 @@ function describeCadence(trigger) {
|
|
|
190
191
|
const minutes = (0, reflection_1.reflectionCadenceMinutes)(trigger);
|
|
191
192
|
return minutes ? (0, reflection_1.describeCadenceMinutes)(minutes) : 'unknown';
|
|
192
193
|
}
|
|
194
|
+
/** The reflection loop as `--json` reports it. A paused loop has no next run (`-`). */
|
|
195
|
+
function reflectionSummary(reflection) {
|
|
196
|
+
return {
|
|
197
|
+
triggerId: reflection.id,
|
|
198
|
+
enabled: reflection.enabled !== false,
|
|
199
|
+
cadence: describeCadence(reflection),
|
|
200
|
+
nextRun: (0, reflection_1.reflectionNextRunLabel)(reflection),
|
|
201
|
+
// Whether its fires run, when the server said; absent means it did not.
|
|
202
|
+
...(0, reflection_1.reflectionAutonomyFields)(reflection),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/** The reflection loop as the text view prints it. */
|
|
206
|
+
function reflectionLine(reflection) {
|
|
207
|
+
return ` ${reflection.enabled === false ? 'paused' : 'active'} · every ${describeCadence(reflection)} · next ${(0, reflection_1.reflectionNextRunLabel)(reflection)} · ${reflection.id ?? '-'}`;
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* The line under the loop when the server says its fires do not run for the owner (OSK-21606),
|
|
211
|
+
* else null. "active · next <time>" only says the loop is armed.
|
|
212
|
+
*/
|
|
213
|
+
function reflectionNoteLine(reflection, bin) {
|
|
214
|
+
const note = (0, reflection_1.reflectionAutonomyNote)(reflection, bin);
|
|
215
|
+
return note ? ` Note: ${note}` : null;
|
|
216
|
+
}
|
|
193
217
|
function settledValue(result) {
|
|
194
218
|
return result.status === 'fulfilled' ? result.value : undefined;
|
|
195
219
|
}
|
|
@@ -25,7 +25,7 @@ class AgentsScheduleTrace extends base_command_1.BaseCommand {
|
|
|
25
25
|
};
|
|
26
26
|
static flags = {
|
|
27
27
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
28
|
-
date: core_1.Flags.string({ description: 'Plan date (YYYY-MM-DD). Defaults to today.' }),
|
|
28
|
+
date: core_1.Flags.string({ description: 'Plan date (YYYY-MM-DD). Defaults to today in the plan timezone.' }),
|
|
29
29
|
todo: core_1.Flags.string({ description: 'Scope the trace to one to-do' }),
|
|
30
30
|
timezone: core_1.Flags.string({ description: 'IANA timezone for the plan day' }),
|
|
31
31
|
limit: core_1.Flags.integer({ description: 'Events to return (1-200)' }),
|
|
@@ -34,16 +34,25 @@ class AgentsScheduleTrace extends base_command_1.BaseCommand {
|
|
|
34
34
|
async run() {
|
|
35
35
|
this.requireAuth();
|
|
36
36
|
const { args, flags } = await this.parse(AgentsScheduleTrace);
|
|
37
|
-
//
|
|
38
|
-
//
|
|
39
|
-
const
|
|
37
|
+
// Use the same zone for choosing the day and querying its events. UTC can
|
|
38
|
+
// already be tomorrow while it is still evening for the reader.
|
|
39
|
+
const timezone = flags.timezone ?? (flags.date ? undefined : Intl.DateTimeFormat().resolvedOptions().timeZone);
|
|
40
|
+
let date = flags.date;
|
|
41
|
+
if (!date) {
|
|
42
|
+
try {
|
|
43
|
+
date = (0, schedule_1.scheduleTraceToday)(new Date(), timezone);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
this.error(`Invalid IANA timezone: ${timezone}`, { exit: 1 });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
40
49
|
let trace;
|
|
41
50
|
try {
|
|
42
51
|
trace = await schedule_1.scheduleApi.scheduleTrace({
|
|
43
52
|
agentId: args.agent,
|
|
44
53
|
date,
|
|
45
54
|
todoId: flags.todo,
|
|
46
|
-
timezone
|
|
55
|
+
timezone,
|
|
47
56
|
limit: flags.limit,
|
|
48
57
|
cursor: flags.cursor,
|
|
49
58
|
});
|
|
@@ -56,7 +56,9 @@ class AgentsTriggersPolicy extends base_command_1.BaseCommand {
|
|
|
56
56
|
description: 'Allowed trigger kind under auto_run_limited (repeatable or comma-separated)',
|
|
57
57
|
multiple: true,
|
|
58
58
|
}),
|
|
59
|
-
'max-fires-per-day': core_1.Flags.integer({
|
|
59
|
+
'max-fires-per-day': core_1.Flags.integer({
|
|
60
|
+
description: 'Daily cap on how often this agent can wake itself: its Task returns and self_scheduled triggers, whatever cap they carry',
|
|
61
|
+
}),
|
|
60
62
|
'max-per-hour': core_1.Flags.integer({ description: 'Agent initiative hourly fire cap' }),
|
|
61
63
|
'working-hours': core_1.Flags.string({ description: 'Policy workingHours JSON object' }),
|
|
62
64
|
'quiet-hours': core_1.Flags.string({ description: 'Policy quietHours JSON object' }),
|
|
@@ -18,6 +18,7 @@ export default class HarnessesPrune extends BaseCommand {
|
|
|
18
18
|
yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
19
19
|
limit: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
20
|
'older-than': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'include-gone-leases': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
21
22
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
22
23
|
};
|
|
23
24
|
run(): Promise<void>;
|
|
@@ -30,12 +30,20 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
30
30
|
'pinned row is KEPT, with the agents named. Move the pin (`agents attach-harness`) and run this again.\n\n' +
|
|
31
31
|
'At most --limit rows are removed per run, oldest first; the rest are counted and the next run takes ' +
|
|
32
32
|
'them. Running it again when nothing qualifies removes nothing. A daemon that is still running ' +
|
|
33
|
-
're-registers on its next connection, so removing a row never stops a machine
|
|
33
|
+
're-registers on its next connection, so removing a row never stops a machine.\n\n' +
|
|
34
|
+
'A hosted, dedicated or cloud machine belongs to its lease, so by default it is never touched. ' +
|
|
35
|
+
'--include-gone-leases also considers the rows of such a machine whose lease is GONE (archived, ' +
|
|
36
|
+
'terminating, terminated, failed or lost): no compute runs on it again, so the row can never come ' +
|
|
37
|
+
'back online. A row with no lease, a lease that can come back (stopped, ready, active) and a row ' +
|
|
38
|
+
'with usage recorded against it are kept, and so is everything the rules above keep. Use --older-than ' +
|
|
39
|
+
'to leave recent ones, for example 30d.';
|
|
34
40
|
static examples = [
|
|
35
41
|
'<%= config.bin %> harnesses prune',
|
|
36
42
|
'<%= config.bin %> harnesses prune --yes',
|
|
37
43
|
'<%= config.bin %> harnesses prune --yes --limit 20',
|
|
38
44
|
'<%= config.bin %> harnesses prune --older-than 7d --json',
|
|
45
|
+
'<%= config.bin %> harnesses prune --include-gone-leases --older-than 30d',
|
|
46
|
+
'<%= config.bin %> harnesses prune --include-gone-leases --older-than 30d --yes --limit 200',
|
|
39
47
|
];
|
|
40
48
|
static flags = {
|
|
41
49
|
yes: core_1.Flags.boolean({
|
|
@@ -50,6 +58,10 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
50
58
|
'older-than': core_1.Flags.string({
|
|
51
59
|
description: 'Only rows unseen for at least this long: 90m, 36h or 7d (default 24h). The server never goes below its offline window.',
|
|
52
60
|
}),
|
|
61
|
+
'include-gone-leases': core_1.Flags.boolean({
|
|
62
|
+
description: 'Also consider the rows of hosted, dedicated or cloud machines whose lease is gone. Off by default.',
|
|
63
|
+
default: false,
|
|
64
|
+
}),
|
|
53
65
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
54
66
|
};
|
|
55
67
|
async run() {
|
|
@@ -71,6 +83,7 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
71
83
|
dryRun: !flags.yes,
|
|
72
84
|
...(flags.limit !== undefined ? { limit: flags.limit } : {}),
|
|
73
85
|
...(minUnseenMs !== undefined ? { minUnseenMs } : {}),
|
|
86
|
+
...(flags['include-gone-leases'] ? { includeGoneLeases: true } : {}),
|
|
74
87
|
});
|
|
75
88
|
}
|
|
76
89
|
catch (err) {
|
|
@@ -86,7 +99,9 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
86
99
|
render(result) {
|
|
87
100
|
const { dryRun } = result;
|
|
88
101
|
const removing = dryRun ? result.remove.length : result.removedCount;
|
|
89
|
-
|
|
102
|
+
// "superseded" is only true of the personal-machine rows; a gone-lease row was not replaced by anything.
|
|
103
|
+
const kind = result.remove.some((row) => row.goneLease) ? '' : 'superseded ';
|
|
104
|
+
const noun = (n) => `${n} ${kind}harness row${n === 1 ? '' : 's'}`;
|
|
90
105
|
if (dryRun) {
|
|
91
106
|
this.log('Dry run - nothing was removed.');
|
|
92
107
|
}
|
|
@@ -141,6 +156,9 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
141
156
|
if (count('not_personal_machine') > 0) {
|
|
142
157
|
passedOver.push(`${count('not_personal_machine')} hosted, dedicated, cloud or of unknown origin`);
|
|
143
158
|
}
|
|
159
|
+
if (count('lease_not_gone') > 0) {
|
|
160
|
+
passedOver.push(`${count('lease_not_gone')} of a machine whose lease is not gone`);
|
|
161
|
+
}
|
|
144
162
|
if (count('no_machine_identity') > 0) {
|
|
145
163
|
passedOver.push(`${count('no_machine_identity')} that name no machine`);
|
|
146
164
|
}
|
|
@@ -159,13 +177,15 @@ class HarnessesPrune extends base_command_1.BaseCommand {
|
|
|
159
177
|
provider: (0, format_1.formatCell)(row.provider),
|
|
160
178
|
daemon: (0, format_1.formatCell)(row.daemonId),
|
|
161
179
|
seen: (0, format_1.localStamp)(row.lastSeenAt),
|
|
162
|
-
replacedBy:
|
|
180
|
+
replacedBy: row.goneLease
|
|
181
|
+
? `lease gone (${row.goneLease})`
|
|
182
|
+
: (0, format_1.formatCell)(row.supersededBy?.daemonId),
|
|
163
183
|
})), [
|
|
164
184
|
{ key: 'id', header: 'HARNESS ID' },
|
|
165
185
|
{ key: 'provider', header: 'PROVIDER' },
|
|
166
186
|
{ key: 'daemon', header: 'DAEMON ID', verbatim: true },
|
|
167
187
|
{ key: 'seen', header: `LAST SEEN (${(0, format_1.localZone)()})` },
|
|
168
|
-
{ key: 'replacedBy', header: 'REPLACED BY (ONLINE)', verbatim: true },
|
|
188
|
+
{ key: 'replacedBy', header: 'REPLACED BY (ONLINE) / LEASE', verbatim: true },
|
|
169
189
|
], (line) => this.log(line));
|
|
170
190
|
}
|
|
171
191
|
}
|
|
@@ -15,10 +15,11 @@ const HOUR_MS = 60 * 60 * 1000;
|
|
|
15
15
|
* `skrr northstars governance configure`.
|
|
16
16
|
*/
|
|
17
17
|
class NorthstarsCreate extends base_command_1.BaseCommand {
|
|
18
|
-
static description =
|
|
18
|
+
static description = `Create a northstar (the portfolio layer above commitments)\n\n${northstars_1.NORTHSTAR_WORKSPACE_CREATE_HELP}`;
|
|
19
19
|
static examples = [
|
|
20
20
|
'<%= config.bin %> northstars create --title "Reach $10M ARR"',
|
|
21
21
|
'<%= config.bin %> northstars create --title "Reliability" --tier critical --max-active 5 --review-interval-hours 24',
|
|
22
|
+
'<%= config.bin %> northstars create --title "Reliability" --workspace <workspace-id>',
|
|
22
23
|
];
|
|
23
24
|
static flags = {
|
|
24
25
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
@@ -98,6 +99,10 @@ class NorthstarsCreate extends base_command_1.BaseCommand {
|
|
|
98
99
|
}
|
|
99
100
|
this.log(`Created ${created.id} — ${created.title} (${created.status})`);
|
|
100
101
|
this.log(`URL: ${created.url ?? '-'}`);
|
|
102
|
+
this.log((0, northstars_1.northstarWorkspaceLine)(created));
|
|
103
|
+
const workspaceNote = (0, northstars_1.createdNorthstarNote)(created);
|
|
104
|
+
if (workspaceNote)
|
|
105
|
+
this.log(workspaceNote);
|
|
101
106
|
if (flags['max-active'] === 0)
|
|
102
107
|
this.log('Attention max active 0 (no cap)');
|
|
103
108
|
this.log(flags['review-interval-hours'] !== undefined
|
|
@@ -12,7 +12,7 @@ const northstars_1 = require("../../lib/northstars");
|
|
|
12
12
|
* their health on a cadence.
|
|
13
13
|
*/
|
|
14
14
|
class NorthstarsList extends base_command_1.BaseCommand {
|
|
15
|
-
static description =
|
|
15
|
+
static description = `List northstars (the portfolio layer above commitments)\n\nThe WORKSPACE column is a shortened workspace id (--json has the full workspaceId) or "personal". ${northstars_1.NORTHSTAR_WORKSPACE_READ_HELP}`;
|
|
16
16
|
static examples = [
|
|
17
17
|
'<%= config.bin %> northstars list',
|
|
18
18
|
'<%= config.bin %> northstars list --status active',
|
|
@@ -27,7 +27,7 @@ function normalizeNorthstarShowResponse(status) {
|
|
|
27
27
|
* asking of a portfolio, which is what is actually in it.
|
|
28
28
|
*/
|
|
29
29
|
class NorthstarsShow extends base_command_1.BaseCommand {
|
|
30
|
-
static description =
|
|
30
|
+
static description = `Show a northstar, its linked commitments, and open recommendations\n\nThe Workspace line is the full workspace id, or "personal". ${northstars_1.NORTHSTAR_WORKSPACE_READ_HELP}`;
|
|
31
31
|
static examples = [
|
|
32
32
|
'<%= config.bin %> northstars show nst_abc123',
|
|
33
33
|
'<%= config.bin %> northstars show nst_abc123 --json',
|
|
@@ -49,6 +49,16 @@ class SpacesHeartbeatsStatus extends base_command_1.BaseCommand {
|
|
|
49
49
|
this.log(`Running: ${s.isRunning === true ? 'yes' : 'no'}`);
|
|
50
50
|
this.log(`Next Run: ${s.nextRunAt ? (0, format_1.localTime)(s.nextRunAt) : '-'}`);
|
|
51
51
|
this.log(`BullMQ Job Key: ${s.bullmqJobKey ?? '-'}`);
|
|
52
|
+
// The newest fire and what became of it. `Next Run` moves whether or not a fire does anything,
|
|
53
|
+
// so a heartbeat the agent's policy skips on every fire would otherwise read as healthy.
|
|
54
|
+
const fire = s.lastFire;
|
|
55
|
+
if (fire && typeof fire === 'object') {
|
|
56
|
+
const what = [fire.outcome, fire.reason].filter(Boolean).join(': ');
|
|
57
|
+
this.log(`Last Fire: ${fire.firedAt ? (0, format_1.localTime)(fire.firedAt) : '-'} ${what || '-'}`);
|
|
58
|
+
if (fire.outcome === 'skipped') {
|
|
59
|
+
this.log('The last fire did nothing: the heartbeat was skipped, not run. `skrr triggers audit` lists every fire.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
52
62
|
}
|
|
53
63
|
}
|
|
54
64
|
exports.default = SpacesHeartbeatsStatus;
|
|
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const web_url_1 = require("../../lib/web-url");
|
|
7
|
+
const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
|
|
7
8
|
class TasksArchive extends base_command_1.BaseCommand {
|
|
8
9
|
static description = 'Archive a task (reversible — use --unarchive to restore)';
|
|
9
10
|
static examples = [
|
|
@@ -37,6 +38,11 @@ class TasksArchive extends base_command_1.BaseCommand {
|
|
|
37
38
|
}
|
|
38
39
|
const action = flags.unarchive ? 'unarchived' : 'archived';
|
|
39
40
|
this.log(`Task ${args.id} ${action}`);
|
|
41
|
+
const lines = flags.unarchive
|
|
42
|
+
? (0, task_schedule_notes_1.unarchiveResultLines)(result, String(args.id), this.config.bin)
|
|
43
|
+
: (0, task_schedule_notes_1.archiveResultLines)(result);
|
|
44
|
+
for (const line of lines)
|
|
45
|
+
this.log(line);
|
|
40
46
|
}
|
|
41
47
|
}
|
|
42
48
|
exports.default = TasksArchive;
|
|
@@ -5,7 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const web_url_1 = require("../../lib/web-url");
|
|
7
7
|
class TasksPauseSchedule extends base_command_1.BaseCommand {
|
|
8
|
-
static description = 'Pause future recurring runs; current runs and already queued work are unchanged';
|
|
8
|
+
static description = 'Pause future recurring runs; current runs and already queued work are unchanged. A run that comes due while paused is skipped, not replayed, when you resume';
|
|
9
9
|
static examples = [
|
|
10
10
|
'<%= config.bin %> tasks pause-schedule <task-id>',
|
|
11
11
|
'<%= config.bin %> tasks pause-schedule <task-id> --json',
|
|
@@ -4,8 +4,9 @@ const core_1 = require("@oclif/core");
|
|
|
4
4
|
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const web_url_1 = require("../../lib/web-url");
|
|
7
|
+
const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
|
|
7
8
|
class TasksResumeSchedule extends base_command_1.BaseCommand {
|
|
8
|
-
static description = 'Resume a paused recurring task
|
|
9
|
+
static description = 'Resume a paused recurring task. A run that came due while it was paused is skipped (not replayed); the next run comes from the cron pattern';
|
|
9
10
|
static examples = [
|
|
10
11
|
'<%= config.bin %> tasks resume-schedule <task-id>',
|
|
11
12
|
'<%= config.bin %> tasks resume-schedule <task-id> --json',
|
|
@@ -34,6 +35,8 @@ class TasksResumeSchedule extends base_command_1.BaseCommand {
|
|
|
34
35
|
const t = result;
|
|
35
36
|
const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
|
|
36
37
|
this.log(`Resumed schedule on task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''}`);
|
|
38
|
+
for (const line of (0, task_schedule_notes_1.resumeResultLines)(t))
|
|
39
|
+
this.log(line);
|
|
37
40
|
}
|
|
38
41
|
}
|
|
39
42
|
exports.default = TasksResumeSchedule;
|
|
@@ -5,6 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
|
|
|
5
5
|
const base_command_1 = require("../../base-command");
|
|
6
6
|
const web_url_1 = require("../../lib/web-url");
|
|
7
7
|
const prompt_1 = require("../../lib/prompt");
|
|
8
|
+
const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
|
|
8
9
|
const iso_instant_1 = require("../../lib/iso-instant");
|
|
9
10
|
const task_recurrence_summary_1 = require("../../lib/task-recurrence-summary");
|
|
10
11
|
class TasksSchedule extends base_command_1.BaseCommand {
|
|
@@ -91,7 +92,8 @@ class TasksSchedule extends base_command_1.BaseCommand {
|
|
|
91
92
|
}
|
|
92
93
|
const t = result;
|
|
93
94
|
const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
|
|
94
|
-
|
|
95
|
+
const paused = Boolean(t.schedulePaused);
|
|
96
|
+
this.log(`${paused ? 'Recorded the time for' : 'Scheduled'} task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''} for ${payload.scheduledFor}`);
|
|
95
97
|
if (t.recurrence?.pattern) {
|
|
96
98
|
// Always name the zone: the pattern is read in UTC unless one was set, and a rule with no zone beside it
|
|
97
99
|
// is exactly what could not be told apart from a local-time one.
|
|
@@ -108,6 +110,9 @@ class TasksSchedule extends base_command_1.BaseCommand {
|
|
|
108
110
|
if (t.recurrence.endDate)
|
|
109
111
|
this.log(`Ends: ${t.recurrence.endDate}`);
|
|
110
112
|
}
|
|
113
|
+
for (const line of (0, task_schedule_notes_1.scheduleResultLines)(t, String(t.id ?? t._id ?? taskRef), this.config.bin)) {
|
|
114
|
+
this.log(line);
|
|
115
|
+
}
|
|
111
116
|
}
|
|
112
117
|
}
|
|
113
118
|
exports.default = TasksSchedule;
|