@skrr-ai/cli 0.1.131 → 0.1.133
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/agenda/plan.d.ts +4 -4
- package/dist/commands/agenda/plan.js +121 -9
- package/dist/commands/agent/message/send.d.ts +2 -0
- package/dist/commands/agent/message/send.js +49 -24
- package/dist/commands/agents/intuitions/generate.js +25 -6
- package/dist/commands/agents/intuitions/list.js +7 -2
- package/dist/commands/agents/plan-day.d.ts +4 -4
- package/dist/commands/agents/plan-day.js +36 -10
- package/dist/commands/agents/schedule-overview.d.ts +14 -0
- package/dist/commands/agents/schedule-overview.js +11 -1
- package/dist/commands/agents/spawn.js +6 -0
- package/dist/commands/agents/todos/edit.d.ts +14 -0
- package/dist/commands/agents/todos/edit.js +43 -0
- package/dist/commands/agents/todos/list.d.ts +1 -0
- package/dist/commands/agents/todos/list.js +4 -2
- package/dist/commands/agents/triggers/fire.d.ts +2 -2
- package/dist/commands/agents/triggers/fire.js +20 -3
- package/dist/commands/agents/triggers/policy.d.ts +6 -0
- package/dist/commands/agents/triggers/policy.js +22 -0
- package/dist/commands/commitments/create.js +13 -0
- package/dist/commands/daemon/install.js +9 -0
- package/dist/commands/goals/briefing/enable.js +1 -2
- package/dist/commands/goals/briefing/status.d.ts +8 -0
- package/dist/commands/goals/briefing/status.js +35 -0
- package/dist/commands/memory/list.js +9 -3
- package/dist/generated/agent-guides.json +19 -19
- package/dist/lib/schedule.d.ts +20 -1
- package/dist/lib/schedule.js +7 -3
- package/dist/lib/scoped-memory-command.d.ts +10 -0
- package/dist/lib/scoped-memory-command.js +21 -2
- package/dist/node_modules/@skrr-ai/data-provider/index.js +774 -774
- package/oclif.manifest.json +196 -3
- package/package.json +1 -1
|
@@ -21,14 +21,15 @@ class AgentsTodosList extends base_command_1.BaseCommand {
|
|
|
21
21
|
};
|
|
22
22
|
static flags = {
|
|
23
23
|
json: core_1.Flags.boolean({ description: 'Output as JSON' }),
|
|
24
|
-
date: core_1.Flags.string({ description: 'Day to list (YYYY-MM-DD
|
|
24
|
+
date: core_1.Flags.string({ description: 'Day to list (YYYY-MM-DD). Defaults to today in the selected timezone.' }),
|
|
25
|
+
timezone: core_1.Flags.string({ description: 'IANA timezone for the day (defaults to UTC)' }),
|
|
25
26
|
};
|
|
26
27
|
async run() {
|
|
27
28
|
this.requireAuth();
|
|
28
29
|
const { args, flags } = await this.parse(AgentsTodosList);
|
|
29
30
|
let response;
|
|
30
31
|
try {
|
|
31
|
-
response = await schedule_1.scheduleApi.listTodos(args.id, flags.date);
|
|
32
|
+
response = await schedule_1.scheduleApi.listTodos(args.id, flags.date, flags.timezone);
|
|
32
33
|
}
|
|
33
34
|
catch (err) {
|
|
34
35
|
this.handleApiError(err);
|
|
@@ -37,6 +38,7 @@ class AgentsTodosList extends base_command_1.BaseCommand {
|
|
|
37
38
|
this.printJsonResult((0, list_output_1.listResult)(response, 'todos'));
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
41
|
+
this.log(`Day timezone: ${flags.timezone || 'UTC'}`);
|
|
40
42
|
(0, schedule_1.renderTodoList)(response.todos ?? [], (line) => this.log(line));
|
|
41
43
|
}
|
|
42
44
|
}
|
|
@@ -2,8 +2,7 @@ import { BaseCommand } from '../../../base-command';
|
|
|
2
2
|
/**
|
|
3
3
|
* `skrr agents triggers fire <agent-id> <trigger-id>` — fire a trigger now.
|
|
4
4
|
* Mirrors the Schedule Board's "Run" button (`POST /api/triggers/:id/fire-now`).
|
|
5
|
-
* The
|
|
6
|
-
* concurrency) — the response `outcome` says whether it actually ran.
|
|
5
|
+
* The response distinguishes the manual fire from a scheduled-policy preview.
|
|
7
6
|
*/
|
|
8
7
|
export default class AgentsTriggersFire extends BaseCommand {
|
|
9
8
|
static description: string;
|
|
@@ -15,6 +14,7 @@ export default class AgentsTriggersFire extends BaseCommand {
|
|
|
15
14
|
static flags: {
|
|
16
15
|
json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
17
16
|
'from-json': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
strict: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
18
18
|
};
|
|
19
19
|
run(): Promise<void>;
|
|
20
20
|
}
|
|
@@ -7,8 +7,7 @@ const triggers_1 = require("../../../lib/triggers");
|
|
|
7
7
|
/**
|
|
8
8
|
* `skrr agents triggers fire <agent-id> <trigger-id>` — fire a trigger now.
|
|
9
9
|
* Mirrors the Schedule Board's "Run" button (`POST /api/triggers/:id/fire-now`).
|
|
10
|
-
* The
|
|
11
|
-
* concurrency) — the response `outcome` says whether it actually ran.
|
|
10
|
+
* The response distinguishes the manual fire from a scheduled-policy preview.
|
|
12
11
|
*/
|
|
13
12
|
class AgentsTriggersFire extends base_command_1.BaseCommand {
|
|
14
13
|
static description = "Fire one of an agent's triggers immediately";
|
|
@@ -25,6 +24,11 @@ class AgentsTriggersFire extends base_command_1.BaseCommand {
|
|
|
25
24
|
'from-json': core_1.Flags.string({
|
|
26
25
|
description: 'Path to a JSON file (or "-" for stdin) to use as fire options',
|
|
27
26
|
}),
|
|
27
|
+
strict: core_1.Flags.boolean({
|
|
28
|
+
allowNo: true,
|
|
29
|
+
default: true,
|
|
30
|
+
description: 'Exit nonzero if the fire was not accepted; use --no-strict for transport-only success',
|
|
31
|
+
}),
|
|
28
32
|
};
|
|
29
33
|
async run() {
|
|
30
34
|
this.requireAuth();
|
|
@@ -46,13 +50,26 @@ class AgentsTriggersFire extends base_command_1.BaseCommand {
|
|
|
46
50
|
this.error(err.message, { exit: 4 });
|
|
47
51
|
this.handleApiError(err);
|
|
48
52
|
}
|
|
53
|
+
const refused = flags.strict && !(0, triggers_1.fireWasAccepted)(result);
|
|
54
|
+
const refusal = refused ? (0, triggers_1.describeStrictFireRefusal)(result) : null;
|
|
49
55
|
if (flags.json) {
|
|
50
|
-
this.log(JSON.stringify(result, null, 2));
|
|
56
|
+
this.log(JSON.stringify(refused ? { ...result, ok: false, strictRefusal: refusal } : result, null, 2));
|
|
57
|
+
if (refusal) {
|
|
58
|
+
process.stderr.write(`${refusal}\n`);
|
|
59
|
+
this.exit(1);
|
|
60
|
+
}
|
|
51
61
|
return;
|
|
52
62
|
}
|
|
53
63
|
this.log(`Fire requested: outcome=${String(result.outcome ?? '-')} reason=${String(result.reason ?? '-')}`);
|
|
54
64
|
for (const line of (0, triggers_1.describeFireDetail)(result))
|
|
55
65
|
this.log(line);
|
|
66
|
+
const note = (0, triggers_1.describeScheduledFire)(result);
|
|
67
|
+
if (note)
|
|
68
|
+
this.log(note);
|
|
69
|
+
if (refusal) {
|
|
70
|
+
process.stderr.write(`${refusal}\n`);
|
|
71
|
+
this.exit(1);
|
|
72
|
+
}
|
|
56
73
|
}
|
|
57
74
|
}
|
|
58
75
|
exports.default = AgentsTriggersFire;
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { BaseCommand } from '../../../base-command';
|
|
2
|
+
/**
|
|
3
|
+
* `3` → 3, `0` → 0 (new lookback plans blocked), `default` → null (clears it). The server validates
|
|
4
|
+
* the range again; refusing a bad value here names the flag instead of a schema issue list.
|
|
5
|
+
*/
|
|
6
|
+
export declare function parseLookbackPlansPerDay(raw: string | undefined): number | null | undefined;
|
|
2
7
|
export default class AgentsTriggersPolicy extends BaseCommand {
|
|
3
8
|
static description: string;
|
|
4
9
|
static examples: string[];
|
|
@@ -16,6 +21,7 @@ export default class AgentsTriggersPolicy extends BaseCommand {
|
|
|
16
21
|
'working-hours': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
22
|
'quiet-hours': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
23
|
'followup-budget': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
'lookback-plans-per-day': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
25
|
};
|
|
20
26
|
run(): Promise<void>;
|
|
21
27
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseLookbackPlansPerDay = parseLookbackPlansPerDay;
|
|
3
4
|
const core_1 = require("@oclif/core");
|
|
4
5
|
const base_command_1 = require("../../../base-command");
|
|
5
6
|
const prompt_1 = require("../../../lib/prompt");
|
|
@@ -30,6 +31,21 @@ function parseFollowUpBudget(values) {
|
|
|
30
31
|
}
|
|
31
32
|
return budget;
|
|
32
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* `3` → 3, `0` → 0 (new lookback plans blocked), `default` → null (clears it). The server validates
|
|
36
|
+
* the range again; refusing a bad value here names the flag instead of a schema issue list.
|
|
37
|
+
*/
|
|
38
|
+
function parseLookbackPlansPerDay(raw) {
|
|
39
|
+
if (raw === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
if (raw === 'default')
|
|
42
|
+
return null;
|
|
43
|
+
const count = Number(raw);
|
|
44
|
+
if (raw.trim() === '' || !Number.isInteger(count) || count < 0 || count > 1000) {
|
|
45
|
+
throw new Error('--lookback-plans-per-day must be an integer from 0 to 1000, or "default"');
|
|
46
|
+
}
|
|
47
|
+
return count;
|
|
48
|
+
}
|
|
33
49
|
class AgentsTriggersPolicy extends base_command_1.BaseCommand {
|
|
34
50
|
static description = "Show or update an agent's initiative policy for agent-scoped triggers";
|
|
35
51
|
static examples = [
|
|
@@ -66,6 +82,9 @@ class AgentsTriggersPolicy extends base_command_1.BaseCommand {
|
|
|
66
82
|
description: 'Daily follow-up creation cap for this agent, as pool=count (pools: watch, checkin, act; count "default" clears it). Repeatable',
|
|
67
83
|
multiple: true,
|
|
68
84
|
}),
|
|
85
|
+
'lookback-plans-per-day': core_1.Flags.string({
|
|
86
|
+
description: 'Daily lookback plan allowance for this agent: an integer from 0 to 1000 (0 blocks new plans) or "default" to clear it. `followups plans capacity --agent <id>` reads what applies today',
|
|
87
|
+
}),
|
|
69
88
|
};
|
|
70
89
|
async run() {
|
|
71
90
|
this.requireAuth();
|
|
@@ -81,6 +100,7 @@ class AgentsTriggersPolicy extends base_command_1.BaseCommand {
|
|
|
81
100
|
workingHours: (0, triggers_1.parseJsonObject)(flags['working-hours'], '--working-hours'),
|
|
82
101
|
quietHours: (0, triggers_1.parseJsonObject)(flags['quiet-hours'], '--quiet-hours'),
|
|
83
102
|
followUpBudget: parseFollowUpBudget(flags['followup-budget']),
|
|
103
|
+
lookbackPlansPerDay: parseLookbackPlansPerDay(flags['lookback-plans-per-day']),
|
|
84
104
|
});
|
|
85
105
|
}
|
|
86
106
|
catch (err) {
|
|
@@ -111,6 +131,8 @@ class AgentsTriggersPolicy extends base_command_1.BaseCommand {
|
|
|
111
131
|
this.log(`Follow-up caps: ${budgetEntries.length > 0
|
|
112
132
|
? budgetEntries.map(([pool, cap]) => `${pool} ${String(cap)}/day`).join(', ')
|
|
113
133
|
: 'defaults'}`);
|
|
134
|
+
const lookbackPlans = policy.lookbackPlansPerDay;
|
|
135
|
+
this.log(`Lookback plans: ${typeof lookbackPlans === 'number' ? `${lookbackPlans}/day` : 'default'}`);
|
|
114
136
|
if (Object.keys(counts).length > 0) {
|
|
115
137
|
this.log(`Triggers: ${String(counts.total ?? 0)} total, ${String(counts.enabled ?? 0)} enabled`);
|
|
116
138
|
}
|
|
@@ -107,6 +107,19 @@ function assertEventDrivenWatchedSource(payload) {
|
|
|
107
107
|
hasEventCapableWatchedSource(payload)) {
|
|
108
108
|
return;
|
|
109
109
|
}
|
|
110
|
+
const sources = Array.isArray(payload.watchedSources) ? payload.watchedSources : [];
|
|
111
|
+
const hasInvalidEvents = sources.some((value) => {
|
|
112
|
+
const source = record(value);
|
|
113
|
+
const config = record(source?.config);
|
|
114
|
+
return source?.enabled !== false && Array.isArray(config?.events) &&
|
|
115
|
+
!hasStructurallyValidExplicitEvent(config);
|
|
116
|
+
});
|
|
117
|
+
if (hasInvalidEvents) {
|
|
118
|
+
throw new Error('Event-driven cadence requires at least one event-capable watched source. ' +
|
|
119
|
+
'An explicit config.events list must contain event objects with non-empty entityType and eventType, ' +
|
|
120
|
+
'for example {"entityType":"github","eventType":"check_run_failed"}. ' +
|
|
121
|
+
'Supply valid bindings, or remove config.events to use the source defaults (which may include more events).');
|
|
122
|
+
}
|
|
110
123
|
throw new Error('Event-driven cadence (--event-driven or --cadence-mode event_plus_interval) requires at least one event-capable watched source. ' +
|
|
111
124
|
'Add --watch github (or another event-capable source), or choose an interval-only cadence.');
|
|
112
125
|
}
|
|
@@ -56,6 +56,15 @@ class DaemonInstall extends core_1.Command {
|
|
|
56
56
|
// own comment records `skrr code run -m <model>` dying the same way. Same
|
|
57
57
|
// mistake, one directory over.
|
|
58
58
|
const argv = this.argv;
|
|
59
|
+
// A forwarded help request is read-only. Do not run installation preflight,
|
|
60
|
+
// bind a profile, or download software just to show the runtime's options.
|
|
61
|
+
if (argv.some((arg) => arg === '--help' || arg === '-h')) {
|
|
62
|
+
if (!(0, exec_runtime_binary_1.isCurrentRuntimeBinary)((0, exec_runtime_binary_1.findRuntimeBinary)())) {
|
|
63
|
+
this.log('The background service is not installed, so its detailed install options are unavailable. Run `skrr daemon install` to install it.');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.exit(await (0, exec_runtime_binary_1.execRuntimeBinary)(['install', '--help']));
|
|
67
|
+
}
|
|
59
68
|
// Before fetching anything: a machine still carrying the previous product
|
|
60
69
|
// identity's state is refused by the runtime's own install, so downloading a
|
|
61
70
|
// release first only delays the same refusal (OSK-6895).
|
|
@@ -28,8 +28,7 @@ class GoalsBriefingEnable extends base_command_1.BaseCommand {
|
|
|
28
28
|
this.log(JSON.stringify(result, null, 2));
|
|
29
29
|
return;
|
|
30
30
|
}
|
|
31
|
-
|
|
32
|
-
this.log(`Morning briefing enabled (cron: ${cron})`);
|
|
31
|
+
this.log(`Morning briefing enabled (cron: ${result.cron})`);
|
|
33
32
|
}
|
|
34
33
|
}
|
|
35
34
|
exports.default = GoalsBriefingEnable;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const core_1 = require("@oclif/core");
|
|
4
|
+
const data_provider_1 = require("@skrr-ai/data-provider");
|
|
5
|
+
const base_command_1 = require("../../../base-command");
|
|
6
|
+
class GoalsBriefingStatus extends base_command_1.BaseCommand {
|
|
7
|
+
static description = 'Show the morning briefing schedule and delivery preference';
|
|
8
|
+
static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
|
|
9
|
+
async run() {
|
|
10
|
+
this.requireAuth();
|
|
11
|
+
const { flags } = await this.parse(GoalsBriefingStatus);
|
|
12
|
+
let status;
|
|
13
|
+
try {
|
|
14
|
+
status = await data_provider_1.dataService.getGoalBriefingStatus();
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
this.handleApiError(err);
|
|
18
|
+
}
|
|
19
|
+
if (flags.json) {
|
|
20
|
+
this.log(JSON.stringify(status, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
this.log(`Morning briefing: ${status.enabled ? 'enabled' : 'disabled'}`);
|
|
24
|
+
if (status.cron)
|
|
25
|
+
this.log(`Schedule: ${status.cron} (${status.timezone || 'timezone unknown'})`);
|
|
26
|
+
if (status.nextRunAt)
|
|
27
|
+
this.log(`Next run: ${status.nextRunAt}`);
|
|
28
|
+
this.log(`Delivery preference: ${status.delivery.categoryLevel}`);
|
|
29
|
+
if (status.delivery.preferenceEligibleChannels.length) {
|
|
30
|
+
this.log(`Preference-eligible channels: ${status.delivery.preferenceEligibleChannels.join(', ')}`);
|
|
31
|
+
this.log('Actual delivery also depends on connected destinations and other notification settings.');
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.default = GoalsBriefingStatus;
|
|
@@ -7,7 +7,7 @@ const base_command_1 = require("../../base-command");
|
|
|
7
7
|
const format_1 = require("../../lib/format");
|
|
8
8
|
const memory_scope_1 = require("../../lib/memory-scope");
|
|
9
9
|
class MemoryList extends base_command_1.BaseCommand {
|
|
10
|
-
static description = 'List memories
|
|
10
|
+
static description = 'List compatibility-pool memories (canonical topics are listed separately)';
|
|
11
11
|
static examples = [
|
|
12
12
|
'<%= config.bin %> memory list',
|
|
13
13
|
'<%= config.bin %> memory list --json',
|
|
@@ -30,15 +30,21 @@ class MemoryList extends base_command_1.BaseCommand {
|
|
|
30
30
|
catch (err) {
|
|
31
31
|
this.handleApiError(err);
|
|
32
32
|
}
|
|
33
|
+
const canonicalHint = flags.agent
|
|
34
|
+
? 'Canonical Agent Memory topics are separate. Run skrr memory topics list with the same --agent and --scope agent:<agent-id>.'
|
|
35
|
+
: 'This is a compatibility pool. Canonical Memory topics are listed separately with skrr memory topics list.';
|
|
33
36
|
if (flags.json) {
|
|
34
|
-
this.printJsonResult((0, list_output_1.listResult)(response, 'memories'));
|
|
37
|
+
this.printJsonResult((0, list_output_1.listResult)({ ...response, memorySource: 'compatibility_pool', canonicalTopicsHint: canonicalHint }, 'memories'));
|
|
35
38
|
return;
|
|
36
39
|
}
|
|
37
40
|
const memories = response.memories ?? [];
|
|
38
41
|
if (memories.length === 0) {
|
|
39
|
-
this.log('No memories.');
|
|
42
|
+
this.log('No compatibility-pool memories.');
|
|
43
|
+
this.log(canonicalHint);
|
|
40
44
|
return;
|
|
41
45
|
}
|
|
46
|
+
this.log('Compatibility-pool memories:');
|
|
47
|
+
this.log(canonicalHint);
|
|
42
48
|
const rows = memories.map((m) => ({
|
|
43
49
|
key: m.key,
|
|
44
50
|
value: m.value,
|
|
@@ -454,7 +454,7 @@
|
|
|
454
454
|
"id": "speaking",
|
|
455
455
|
"title": "A Commitment finds; its Agent speaks",
|
|
456
456
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
457
|
-
"tokens":
|
|
457
|
+
"tokens": 301,
|
|
458
458
|
"aliases": [],
|
|
459
459
|
"explains": []
|
|
460
460
|
},
|
|
@@ -709,7 +709,7 @@
|
|
|
709
709
|
]
|
|
710
710
|
}
|
|
711
711
|
],
|
|
712
|
-
"hash": "
|
|
712
|
+
"hash": "74f6b87c1bd1f851fbfe0b1ee5da04425d1b6fdafbf55445a39e121af9c4e65b",
|
|
713
713
|
"tokens": 545
|
|
714
714
|
},
|
|
715
715
|
"routedIndex": {
|
|
@@ -1167,7 +1167,7 @@
|
|
|
1167
1167
|
"id": "speaking",
|
|
1168
1168
|
"title": "A Commitment finds; its Agent speaks",
|
|
1169
1169
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
1170
|
-
"tokens":
|
|
1170
|
+
"tokens": 301,
|
|
1171
1171
|
"aliases": [],
|
|
1172
1172
|
"explains": []
|
|
1173
1173
|
},
|
|
@@ -1353,7 +1353,7 @@
|
|
|
1353
1353
|
]
|
|
1354
1354
|
}
|
|
1355
1355
|
],
|
|
1356
|
-
"hash": "
|
|
1356
|
+
"hash": "6960740319e60127c5a1007fd32e77afada0b14442b5f6fb810a7fee1407cb63",
|
|
1357
1357
|
"tokens": 494
|
|
1358
1358
|
},
|
|
1359
1359
|
"cards": {
|
|
@@ -1747,7 +1747,7 @@
|
|
|
1747
1747
|
"routed": true,
|
|
1748
1748
|
"aliases": [],
|
|
1749
1749
|
"body": "## Concept model\n\n| Noun | Question it answers |\n| --- | --- |\n| **Compass** | What does good look like for this Agent, and what may it never trade away? |\n| **Northstar** | Which Commitments should exist, at what priority and on what budget? |\n| **Commitment** | Which outcome does this Agent keep true, and how does it keep checking? |\n| **Check** | What did one wake of a Commitment see, decide and do? |\n\nA Commitment is not a unit of work. A **Task** is one unit of work and a **Goal** is a dated\noutcome with key results; a Commitment watches both and may create Tasks, and is neither.\n\n## Decide\n\nFive mechanisms make something happen later, and the noun alone does not pick one:\n\n| Mechanism | What it is for | The signal that picks it |\n| --- | --- | --- |\n| **Follow-up** | Coming back to one thing, once or a bounded few times: a reminder, a watch, a check-in, an agreed action | The ask ends: \"look tomorrow\", \"tell me when CI is green\" |\n| **Trigger** | Running the Agent with a set prompt on a schedule, an event, once, or on demand | The person names when, and every run does the same work |\n| **Heartbeat** | Checking in on a conversation, group chat or Space at a frequency that backs off while unread | The person wants regular contact, not an outcome held |\n| **Commitment** | Keeping an outcome true: watched sources, a cadence and a loop that decides when to act | \"Keep X true\", \"watch this metric\": no end, and stale evidence matters |\n| **Workflow** | Running a durable graph of Tasks in order | Repeatable work whose steps depend on each other |\n\nA one-off \"look at it tomorrow\" is a follow-up, never a Commitment. Standing observation is a\nCommitment with a freshness contract, not a recurring trigger or a task board left running.\n\n## Workflow\n\n### Start a Commitment\n\n1. Read the Agent's Commitments first, and change one rather than add a near-duplicate.\n2. Create it and turn it on in one call: the outcome, what it watches, how often it checks, where\n its work goes. Turning it on runs Preflight; a refusal keeps the draft and reports the code,\n reason and fix.\n3. Leave it a draft only when the person asked to see it first.\n4. Read it back, and tell the person what it watches, how often, and whether it is on.\n\n## Authority\n\n| Actor | May do | Only a person may do |\n| --- | --- | --- |\n| Agent | Read the Commitments of an Agent it can view; create one, change it and turn it on; request a check; pause one; decide whether a finding is worth telling the owner | Lift a stop a person made; set or clear a concurrency limit; choose the Task review policy; approve an action proposal or a cadence change; configure a Northstar's governance or decide its intents |\n| Person | Everything in both columns | — |\n\nA Commitment grants no permission: its runs work under the Agent's own. A Check never writes an\nowner-facing message, and a Compass never creates, rewrites, pauses or archives a Commitment. The\nserver cannot always tell an Agent from the person whose login it holds, so an accepted call is\nnot permission: the right-hand column stays a person's.\n\n## Live state\n\n- `skrr commitments list --agent <agent-id>` — an Agent's Commitments, with status and assessment.\n- `skrr commitments show <commitment-id>` — the contract, its next check and the last one.\n- `skrr commitments roster --agent <agent-id>` — each loop: whether it is armed, its next wake, runs today.\n\n## Related guides\n\n- `tasks` — the Tasks a Commitment creates or watches.\n- `spaces` — Goals, and Workflows: durable graphs of Tasks.\n- `agents` — the Agent a Commitment belongs to, and where its runs execute.\n- `communication` — whether, what and how to tell the owner.",
|
|
1750
|
-
"hash": "
|
|
1750
|
+
"hash": "197d9b84d1a4367ecf4b7a51687604d13814ec9e9265fe662883278a7738d2fa",
|
|
1751
1751
|
"tokens": 1169,
|
|
1752
1752
|
"sections": [
|
|
1753
1753
|
{
|
|
@@ -1824,7 +1824,7 @@
|
|
|
1824
1824
|
"id": "speaking",
|
|
1825
1825
|
"title": "A Commitment finds; its Agent speaks",
|
|
1826
1826
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
1827
|
-
"tokens":
|
|
1827
|
+
"tokens": 301,
|
|
1828
1828
|
"aliases": [],
|
|
1829
1829
|
"explains": []
|
|
1830
1830
|
},
|
|
@@ -5850,7 +5850,7 @@
|
|
|
5850
5850
|
"id": "speaking",
|
|
5851
5851
|
"title": "A Commitment finds; its Agent speaks",
|
|
5852
5852
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
5853
|
-
"tokens":
|
|
5853
|
+
"tokens": 301,
|
|
5854
5854
|
"aliases": [],
|
|
5855
5855
|
"explains": []
|
|
5856
5856
|
},
|
|
@@ -5966,7 +5966,7 @@
|
|
|
5966
5966
|
"id": "speaking",
|
|
5967
5967
|
"title": "A Commitment finds; its Agent speaks",
|
|
5968
5968
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
5969
|
-
"tokens":
|
|
5969
|
+
"tokens": 301,
|
|
5970
5970
|
"aliases": [],
|
|
5971
5971
|
"explains": []
|
|
5972
5972
|
},
|
|
@@ -6084,7 +6084,7 @@
|
|
|
6084
6084
|
"id": "speaking",
|
|
6085
6085
|
"title": "A Commitment finds; its Agent speaks",
|
|
6086
6086
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6087
|
-
"tokens":
|
|
6087
|
+
"tokens": 301,
|
|
6088
6088
|
"aliases": [],
|
|
6089
6089
|
"explains": []
|
|
6090
6090
|
},
|
|
@@ -6206,7 +6206,7 @@
|
|
|
6206
6206
|
"id": "speaking",
|
|
6207
6207
|
"title": "A Commitment finds; its Agent speaks",
|
|
6208
6208
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6209
|
-
"tokens":
|
|
6209
|
+
"tokens": 301,
|
|
6210
6210
|
"aliases": [],
|
|
6211
6211
|
"explains": []
|
|
6212
6212
|
},
|
|
@@ -6322,7 +6322,7 @@
|
|
|
6322
6322
|
"id": "speaking",
|
|
6323
6323
|
"title": "A Commitment finds; its Agent speaks",
|
|
6324
6324
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6325
|
-
"tokens":
|
|
6325
|
+
"tokens": 301,
|
|
6326
6326
|
"aliases": [],
|
|
6327
6327
|
"explains": []
|
|
6328
6328
|
},
|
|
@@ -6438,7 +6438,7 @@
|
|
|
6438
6438
|
"id": "speaking",
|
|
6439
6439
|
"title": "A Commitment finds; its Agent speaks",
|
|
6440
6440
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6441
|
-
"tokens":
|
|
6441
|
+
"tokens": 301,
|
|
6442
6442
|
"aliases": [],
|
|
6443
6443
|
"explains": []
|
|
6444
6444
|
},
|
|
@@ -6560,7 +6560,7 @@
|
|
|
6560
6560
|
"id": "speaking",
|
|
6561
6561
|
"title": "A Commitment finds; its Agent speaks",
|
|
6562
6562
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6563
|
-
"tokens":
|
|
6563
|
+
"tokens": 301,
|
|
6564
6564
|
"aliases": [],
|
|
6565
6565
|
"explains": []
|
|
6566
6566
|
},
|
|
@@ -6598,9 +6598,9 @@
|
|
|
6598
6598
|
"version": 4,
|
|
6599
6599
|
"aliases": [],
|
|
6600
6600
|
"explains": [],
|
|
6601
|
-
"body": "Read this when a Commitment run found something and you must decide whether the owner hears of it.\n\nA Check never writes to the owner. When something it found may matter, the run is handed a brief\nunder \"Owner communication — your call.\": what was found, the evidence, why it may matter and a\nsuggested next step. Those are facts to judge, not a message to forward.\n\n- Decide with everything in view: what you already said, whether the owner replied, what another\n surface already raised. One message, a different message or nothing are all valid outcomes, and\n silence is often the right one.\n- To speak from the run,
|
|
6602
|
-
"hash": "
|
|
6603
|
-
"tokens":
|
|
6601
|
+
"body": "Read this when a Commitment run found something and you must decide whether the owner hears of it.\n\nA Check never writes to the owner. When something it found may matter, the run is handed a brief\nunder \"Owner communication — your call.\": what was found, the evidence, why it may matter and a\nsuggested next step. Those are facts to judge, not a message to forward.\n\n- Decide with everything in view: what you already said, whether the owner replied, what another\n surface already raised. One message, a different message or nothing are all valid outcomes, and\n silence is often the right one.\n- To speak from the run, send it with `skrr agent message send` where the run allows it: it is\n delivered under the owner's limits and counted for this Commitment. Otherwise return a\n `status_update` or `report` action intent with its `text` for the Commitment's report\n destinations. A `message` action intent is recorded but never delivered. Write the words yourself.\n- The owner's limits (mute, quiet hours, a daily nudge cap) decide whether a run is asked to speak\n at all, and whatever you deliver counts against them, whichever way you send it.\n\n`skrr commitments reports <commitment-id>` lists where its reports go and on what condition; the\ndefault sends only on a material change.",
|
|
6602
|
+
"hash": "45fa02a0344903cc355dff0b66eee45b14dab79a8f62fba8620106b29e11e8a9",
|
|
6603
|
+
"tokens": 301,
|
|
6604
6604
|
"sections": [
|
|
6605
6605
|
{
|
|
6606
6606
|
"id": "model",
|
|
@@ -6676,7 +6676,7 @@
|
|
|
6676
6676
|
"id": "speaking",
|
|
6677
6677
|
"title": "A Commitment finds; its Agent speaks",
|
|
6678
6678
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6679
|
-
"tokens":
|
|
6679
|
+
"tokens": 301,
|
|
6680
6680
|
"aliases": [],
|
|
6681
6681
|
"explains": []
|
|
6682
6682
|
},
|
|
@@ -6796,7 +6796,7 @@
|
|
|
6796
6796
|
"id": "speaking",
|
|
6797
6797
|
"title": "A Commitment finds; its Agent speaks",
|
|
6798
6798
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6799
|
-
"tokens":
|
|
6799
|
+
"tokens": 301,
|
|
6800
6800
|
"aliases": [],
|
|
6801
6801
|
"explains": []
|
|
6802
6802
|
},
|
|
@@ -6914,7 +6914,7 @@
|
|
|
6914
6914
|
"id": "speaking",
|
|
6915
6915
|
"title": "A Commitment finds; its Agent speaks",
|
|
6916
6916
|
"readWhen": "a Commitment run found something and you must decide whether the owner hears of it",
|
|
6917
|
-
"tokens":
|
|
6917
|
+
"tokens": 301,
|
|
6918
6918
|
"aliases": [],
|
|
6919
6919
|
"explains": []
|
|
6920
6920
|
},
|
package/dist/lib/schedule.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface Todo {
|
|
|
19
19
|
rationale?: string;
|
|
20
20
|
confidence?: TodoConfidence;
|
|
21
21
|
triggerId?: string;
|
|
22
|
+
updatedAt?: string;
|
|
22
23
|
firedAt?: string;
|
|
23
24
|
result?: {
|
|
24
25
|
conversationId?: string;
|
|
@@ -68,6 +69,11 @@ export interface SmartScheduleProposal {
|
|
|
68
69
|
truncated: boolean;
|
|
69
70
|
};
|
|
70
71
|
todos: ProposedTodo[];
|
|
72
|
+
nextRunSuggestion?: {
|
|
73
|
+
runAt: string;
|
|
74
|
+
reason?: string;
|
|
75
|
+
confidence?: number;
|
|
76
|
+
};
|
|
71
77
|
/**
|
|
72
78
|
* Agent-scope trigger budget at proposal time. Advisory — approval enforces
|
|
73
79
|
* it for real — but it is the difference between a warning the user can act
|
|
@@ -105,6 +111,12 @@ export interface ApproveSmartScheduleResponse {
|
|
|
105
111
|
}>;
|
|
106
112
|
/** Post-write budget, so a caller approving several plans sees the wall coming. */
|
|
107
113
|
capacity?: TriggerCapacity;
|
|
114
|
+
scheduledNextRun?: {
|
|
115
|
+
created?: boolean;
|
|
116
|
+
triggerId?: string;
|
|
117
|
+
skippedReason?: string;
|
|
118
|
+
message?: string;
|
|
119
|
+
} | null;
|
|
108
120
|
}
|
|
109
121
|
/**
|
|
110
122
|
* The self-schedule TRACE — why the agent's day looks the way it does.
|
|
@@ -165,14 +177,20 @@ export declare const scheduleApi: {
|
|
|
165
177
|
limit?: number;
|
|
166
178
|
cursor?: string;
|
|
167
179
|
}) => Promise<ScheduleTrace>;
|
|
168
|
-
listTodos: (agentId: string, date?: string) => Promise<ListTodosResponse>;
|
|
180
|
+
listTodos: (agentId: string, date?: string, timezone?: string) => Promise<ListTodosResponse>;
|
|
169
181
|
getTodo: (todoId: string) => Promise<Todo>;
|
|
170
182
|
createTodo: (input: CreateTodoInput) => Promise<Todo>;
|
|
183
|
+
updateTodo: (todoId: string, patch: {
|
|
184
|
+
name?: string;
|
|
185
|
+
runAt?: string;
|
|
186
|
+
expectedUpdatedAt?: string;
|
|
187
|
+
}) => Promise<Todo>;
|
|
171
188
|
/** Cancel a To-Do (DELETE soft-cancels pending; see todos.js). */
|
|
172
189
|
cancelTodo: (todoId: string) => Promise<Todo>;
|
|
173
190
|
proposeSmartSchedule: (input: {
|
|
174
191
|
agentId: string;
|
|
175
192
|
workingHoursOverride?: WorkingHoursWindow;
|
|
193
|
+
timezone?: string;
|
|
176
194
|
regenerateHint?: string;
|
|
177
195
|
}) => Promise<SmartScheduleProposal>;
|
|
178
196
|
pendingProposal: (agentId: string) => Promise<{
|
|
@@ -182,6 +200,7 @@ export declare const scheduleApi: {
|
|
|
182
200
|
agentId: string;
|
|
183
201
|
proposalId: string;
|
|
184
202
|
todos: ApproveTodoInput[];
|
|
203
|
+
approveNextRunSuggestion?: boolean;
|
|
185
204
|
saveHoursAsDefault?: boolean;
|
|
186
205
|
workingHours?: WorkingHoursWindow;
|
|
187
206
|
}) => Promise<ApproveSmartScheduleResponse>;
|
package/dist/lib/schedule.js
CHANGED
|
@@ -52,15 +52,16 @@ function scheduleTracePath(params) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
/** Build the `/api/todos` list query — agentId is required, date optional. */
|
|
55
|
-
function todoListPath(agentId, date) {
|
|
56
|
-
return (0, triggers_1.withQuery)('/api/todos', { agentId, date });
|
|
55
|
+
function todoListPath(agentId, date, timezone) {
|
|
56
|
+
return (0, triggers_1.withQuery)('/api/todos', { agentId, date, timezone });
|
|
57
57
|
}
|
|
58
58
|
exports.scheduleApi = {
|
|
59
59
|
/** The self-schedule trace for one agent-day. See `scheduleTracePath`. */
|
|
60
60
|
scheduleTrace: (params) => data_provider_1.request.get(scheduleTracePath(params)),
|
|
61
|
-
listTodos: (agentId, date) => data_provider_1.request.get(todoListPath(agentId, date)),
|
|
61
|
+
listTodos: (agentId, date, timezone) => data_provider_1.request.get(todoListPath(agentId, date, timezone)),
|
|
62
62
|
getTodo: (todoId) => data_provider_1.request.get(`/api/todos/${encodeURIComponent(todoId)}`),
|
|
63
63
|
createTodo: (input) => data_provider_1.request.post('/api/todos', input),
|
|
64
|
+
updateTodo: (todoId, patch) => data_provider_1.request.patch(`/api/todos/${encodeURIComponent(todoId)}`, patch),
|
|
64
65
|
/** Cancel a To-Do (DELETE soft-cancels pending; see todos.js). */
|
|
65
66
|
cancelTodo: (todoId) => data_provider_1.request.delete(`/api/todos/${encodeURIComponent(todoId)}`),
|
|
66
67
|
proposeSmartSchedule: (input) => data_provider_1.request.post('/api/todos/smart-schedule/propose', input),
|
|
@@ -162,6 +163,9 @@ function renderProposal(proposal, log) {
|
|
|
162
163
|
if (proposal.expiresAt)
|
|
163
164
|
log(`Expires: ${formatStamp(proposal.expiresAt)}`);
|
|
164
165
|
renderCapacityWarning(proposal.capacity, log);
|
|
166
|
+
if (proposal.nextRunSuggestion) {
|
|
167
|
+
log(`Next run: ${formatStamp(proposal.nextRunSuggestion.runAt)}${proposal.nextRunSuggestion.reason ? ` · ${proposal.nextRunSuggestion.reason}` : ''}`);
|
|
168
|
+
}
|
|
165
169
|
log('');
|
|
166
170
|
const live = proposal.todos.filter((t) => !t.dropped);
|
|
167
171
|
if (live.length === 0) {
|
|
@@ -82,6 +82,16 @@ export declare function parseMemoryApiError(error: ApiFetchError): {
|
|
|
82
82
|
retryable: boolean;
|
|
83
83
|
details?: Record<string, unknown>;
|
|
84
84
|
};
|
|
85
|
+
/** A gateway timeout can arrive after the canonical mutation committed. */
|
|
86
|
+
export declare function uncertainMemoryMutationRecovery(path: string, options: ApiFetchOptions, status: number): {
|
|
87
|
+
message: string;
|
|
88
|
+
details: {
|
|
89
|
+
spaceId?: string | undefined;
|
|
90
|
+
outcome: string;
|
|
91
|
+
targetStoreId: string;
|
|
92
|
+
idempotencyKey: string;
|
|
93
|
+
};
|
|
94
|
+
} | null;
|
|
85
95
|
export declare abstract class ScopedMemoryCommand extends BaseCommand {
|
|
86
96
|
protected endpoint(agentId: string, suffix?: string): string;
|
|
87
97
|
protected memoryApi<T>(agentId: string, suffix: string, options?: ApiFetchOptions): Promise<T>;
|
|
@@ -7,6 +7,7 @@ exports.memoryNotMountedDiagnostic = memoryNotMountedDiagnostic;
|
|
|
7
7
|
exports.parseMemoryMount = parseMemoryMount;
|
|
8
8
|
exports.parseMountedMemoryStore = parseMountedMemoryStore;
|
|
9
9
|
exports.parseMemoryApiError = parseMemoryApiError;
|
|
10
|
+
exports.uncertainMemoryMutationRecovery = uncertainMemoryMutationRecovery;
|
|
10
11
|
const core_1 = require("@oclif/core");
|
|
11
12
|
const node_crypto_1 = require("node:crypto");
|
|
12
13
|
const base_command_1 = require("../base-command");
|
|
@@ -191,6 +192,23 @@ function parseMemoryApiError(error) {
|
|
|
191
192
|
};
|
|
192
193
|
}
|
|
193
194
|
}
|
|
195
|
+
/** A gateway timeout can arrive after the canonical mutation committed. */
|
|
196
|
+
function uncertainMemoryMutationRecovery(path, options, status) {
|
|
197
|
+
if (status !== 504 || options.method !== 'POST' || !/\/mutations\/[^/?]+$/.test(path))
|
|
198
|
+
return null;
|
|
199
|
+
const body = options.body;
|
|
200
|
+
if (!body || body.dryRun === true || typeof body.idempotencyKey !== 'string' || typeof body.targetStoreId !== 'string')
|
|
201
|
+
return null;
|
|
202
|
+
return {
|
|
203
|
+
message: `The write may have been saved. Inspect the canonical Store with skrr memory topics list using the same --agent and --store ${body.targetStoreId}${body.spaceId ? ' and --space ' + body.spaceId : ''}. If retrying the identical operation, use --idempotency-key ${body.idempotencyKey}.`,
|
|
204
|
+
details: {
|
|
205
|
+
outcome: 'unknown',
|
|
206
|
+
targetStoreId: body.targetStoreId,
|
|
207
|
+
idempotencyKey: body.idempotencyKey,
|
|
208
|
+
...(typeof body.spaceId === 'string' ? { spaceId: body.spaceId } : {}),
|
|
209
|
+
},
|
|
210
|
+
};
|
|
211
|
+
}
|
|
194
212
|
class ScopedMemoryCommand extends base_command_1.BaseCommand {
|
|
195
213
|
endpoint(agentId, suffix = '') {
|
|
196
214
|
return `/api/agents/${encodeURIComponent(agentId)}/memories/workspace${suffix}`;
|
|
@@ -229,13 +247,14 @@ class ScopedMemoryCommand extends base_command_1.BaseCommand {
|
|
|
229
247
|
catch (error) {
|
|
230
248
|
if (error instanceof api_fetch_1.ApiFetchError) {
|
|
231
249
|
const parsed = parseMemoryApiError(error);
|
|
250
|
+
const recovery = uncertainMemoryMutationRecovery(path, options, error.status);
|
|
232
251
|
this.failWithCliError({
|
|
233
|
-
message: `${parsed.code}: ${parsed.message}`,
|
|
252
|
+
message: `${parsed.code}: ${parsed.message}${recovery ? ` ${recovery.message}` : ''}`,
|
|
234
253
|
code: parsed.code,
|
|
235
254
|
status: error.status,
|
|
236
255
|
exit: ERROR_EXITS[parsed.code] || (error.status === 404 ? 4 : error.status === 403 ? 5 : 2),
|
|
237
256
|
retryable: parsed.retryable,
|
|
238
|
-
details: parsed.details,
|
|
257
|
+
details: recovery ? { ...parsed.details, ...recovery.details } : parsed.details,
|
|
239
258
|
});
|
|
240
259
|
}
|
|
241
260
|
this.handleApiError(error);
|