@skrr-ai/cli 0.1.132 → 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.
@@ -7,10 +7,8 @@ import { BaseCommand } from '../../base-command';
7
7
  * server, and enqueues the agent turn through the canonical agentic
8
8
  * pipeline (preflight → BullMQ → AgentWorker → AgenticClient → SDK).
9
9
  *
10
- * The CLI returns the resolved `conversationId` + `sessionId` immediately
11
- * after the HTTP accept. Live streaming continues over `/ws/agentic` —
12
- * not subscribed here. Pair with `skrr convos show <conversationId>` to
13
- * inspect the resulting agenda once it persists.
10
+ * Interactive terminals follow the accepted session, including permission
11
+ * requests. JSON and non-interactive invocations remain detached by default.
14
12
  */
15
13
  export default class AgendaPlan extends BaseCommand {
16
14
  static description: string;
@@ -22,6 +20,8 @@ export default class AgendaPlan extends BaseCommand {
22
20
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
23
21
  'space-id': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
24
22
  timezone: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
23
+ follow: import("@oclif/core/interfaces").BooleanFlag<boolean>;
24
+ 'approve-tools': import("@oclif/core/interfaces").BooleanFlag<boolean>;
25
25
  };
26
26
  run(): Promise<void>;
27
27
  }
@@ -3,6 +3,22 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  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
+ const api_fetch_1 = require("../../lib/api-fetch");
7
+ const agentic_stream_1 = require("../../lib/agentic-stream");
8
+ const chat_1 = require("../agents/chat");
9
+ function describeRuntime(runtime) {
10
+ if (typeof runtime === 'string')
11
+ return runtime;
12
+ if (!runtime || typeof runtime !== 'object')
13
+ return null;
14
+ const record = runtime;
15
+ const plan = record.executionPlan && typeof record.executionPlan === 'object'
16
+ ? record.executionPlan : null;
17
+ const selected = typeof plan?.selectedKind === 'string' ? plan.selectedKind : null;
18
+ if (selected)
19
+ return `selected ${selected}${selected === 'agent-worker' && record.daemonId ? '; local daemon available as tool bridge' : ''}`;
20
+ return typeof record.mode === 'string' ? record.mode : null;
21
+ }
6
22
  /**
7
23
  * `skrr agenda plan <agent-id>` — kick off a one-shot Agenda Planning turn
8
24
  * for an agent. The server-side controller resolves the agent's persistent
@@ -11,10 +27,8 @@ const base_command_1 = require("../../base-command");
11
27
  * server, and enqueues the agent turn through the canonical agentic
12
28
  * pipeline (preflight → BullMQ → AgentWorker → AgenticClient → SDK).
13
29
  *
14
- * The CLI returns the resolved `conversationId` + `sessionId` immediately
15
- * after the HTTP accept. Live streaming continues over `/ws/agentic` —
16
- * not subscribed here. Pair with `skrr convos show <conversationId>` to
17
- * inspect the resulting agenda once it persists.
30
+ * Interactive terminals follow the accepted session, including permission
31
+ * requests. JSON and non-interactive invocations remain detached by default.
18
32
  */
19
33
  class AgendaPlan extends base_command_1.BaseCommand {
20
34
  static description = 'Trigger an Agenda Planning turn for an agent. Returns the conversation id where the streaming response will land.';
@@ -34,10 +48,17 @@ class AgendaPlan extends base_command_1.BaseCommand {
34
48
  timezone: core_1.Flags.string({
35
49
  description: 'IANA timezone hint for the agenda turn (defaults to UTC server-side)',
36
50
  }),
51
+ follow: core_1.Flags.boolean({ allowNo: true, description: 'Follow the session (default on an interactive terminal)' }),
52
+ 'approve-tools': core_1.Flags.boolean({
53
+ description: 'Allow tool prompts; on a non-interactive follow this grants requested tools for the turn',
54
+ }),
37
55
  };
38
56
  async run() {
39
57
  this.requireAuth();
40
58
  const { args, flags } = await this.parse(AgendaPlan);
59
+ if (flags.json && flags.follow)
60
+ this.error('--json is detached; use --follow without --json to stream.', { exit: 1 });
61
+ const follow = flags.follow ?? (Boolean(process.stdin.isTTY) && !flags.json);
41
62
  let response;
42
63
  try {
43
64
  response = await data_provider_1.dataService.planAgenda({
@@ -57,12 +78,103 @@ class AgendaPlan extends base_command_1.BaseCommand {
57
78
  this.log(`Conversation ID: ${response.conversationId}`);
58
79
  this.log(`Session ID: ${response.sessionId}`);
59
80
  this.log(`New conversation: ${response.isNewConversation ? 'yes' : 'no'}`);
60
- if (response.runtime)
61
- this.log(`Runtime: ${response.runtime}`);
81
+ const runtimeDescription = describeRuntime(response.runtime);
82
+ if (runtimeDescription)
83
+ this.log(`Runtime: ${runtimeDescription}`);
62
84
  this.log('');
63
- this.log('Streaming continues over /ws/agentic. Use `skrr convos show ' +
64
- response.conversationId +
65
- '` to inspect the agenda once it completes.');
85
+ if (!follow) {
86
+ this.log('The turn is running detached. It may wait for permission; inspect it in the app or conversation ' + response.conversationId + '.');
87
+ return;
88
+ }
89
+ const readMessages = async () => {
90
+ const messages = await data_provider_1.dataService.getMessagesByConvoId(response.conversationId);
91
+ return Array.isArray(messages) ? messages : [];
92
+ };
93
+ let detach;
94
+ const detached = new Promise((resolve) => { detach = () => resolve('detached'); });
95
+ let disconnectTimer;
96
+ const client = new agentic_stream_1.AgenticStreamClient({
97
+ baseURL: this.cliConfig.baseURL,
98
+ token: this.resolvedCredential.token,
99
+ workspaceId: process.env.OVERSKY_WORKSPACE_ID,
100
+ sessionId: response.sessionId,
101
+ conversationId: response.conversationId,
102
+ approveTools: Boolean(process.stdin.isTTY || flags['approve-tools']),
103
+ onEvent: (event) => {
104
+ if (event.type === 'transport.connected' && disconnectTimer) {
105
+ clearTimeout(disconnectTimer);
106
+ disconnectTimer = undefined;
107
+ }
108
+ else if (event.type === 'transport.disconnected' && !disconnectTimer) {
109
+ // An established Socket.IO connection can be disconnected without an
110
+ // automatic retry. Bound the lost view and reconcile its durable row.
111
+ disconnectTimer = setTimeout(detach, 30_000);
112
+ }
113
+ },
114
+ reconcileTerminal: async () => (0, chat_1.reconcileTerminalFromMessages)(await readMessages(), response.responseMessageId),
115
+ awaitHandover: {
116
+ read: async () => (0, chat_1.handoverStateFromMessages)(await readMessages(), response.responseMessageId),
117
+ responseMessageId: response.responseMessageId,
118
+ conversationId: response.conversationId,
119
+ maxWaitMs: 5 * 60_000,
120
+ },
121
+ replayEnvelopes: (from) => (0, api_fetch_1.apiFetch)(`/api/agentic/sessions/${encodeURIComponent(response.sessionId)}/envelopes?from=${from}&limit=500`, { credential: this.resolvedCredential, signal: AbortSignal.timeout(5000) }),
122
+ });
123
+ let interruptedBy = null;
124
+ let timer;
125
+ const onInterrupt = (signal) => {
126
+ if (interruptedBy) {
127
+ detach();
128
+ return;
129
+ }
130
+ interruptedBy = signal;
131
+ client.cancel();
132
+ if (signal !== 'SIGHUP')
133
+ process.stderr.write(`\nCancellation requested for ${response.sessionId}; waiting up to 10 seconds. The turn may still be running if it does not stop.\n`);
134
+ timer = setTimeout(detach, 10_000);
135
+ };
136
+ const onSigint = () => onInterrupt('SIGINT');
137
+ const onSigterm = () => onInterrupt('SIGTERM');
138
+ const onSighup = () => onInterrupt('SIGHUP');
139
+ process.on('SIGINT', onSigint);
140
+ process.on('SIGTERM', onSigterm);
141
+ process.on('SIGHUP', onSighup);
142
+ try {
143
+ const outcome = await Promise.race([
144
+ client.run().catch((error) => {
145
+ if (error?.code === 'STREAM_ATTACH_TIMEOUT')
146
+ return 'detached';
147
+ throw error;
148
+ }),
149
+ detached,
150
+ ]);
151
+ const result = outcome === 'detached' && await client.probeTerminal('detach')
152
+ ? client.settledResult() : outcome === 'detached' ? null : outcome;
153
+ if (!result) {
154
+ process.stderr.write(`Detached from ${response.sessionId}; the turn may still be running. Check conversation ${response.conversationId}.\n`);
155
+ process.exitCode = interruptedBy === 'SIGTERM' ? 143 : interruptedBy === 'SIGHUP' ? 129 : interruptedBy ? 130 : 75;
156
+ }
157
+ else if (result.status === 'failed') {
158
+ process.exitCode = 1;
159
+ }
160
+ else if (result.status === 'aborted' && interruptedBy !== 'SIGHUP') {
161
+ this.log(`\nCancellation confirmed for ${response.sessionId}.`);
162
+ }
163
+ if (interruptedBy === 'SIGTERM')
164
+ process.exitCode = 143;
165
+ if (interruptedBy === 'SIGHUP')
166
+ process.exitCode = 129;
167
+ }
168
+ finally {
169
+ if (timer)
170
+ clearTimeout(timer);
171
+ if (disconnectTimer)
172
+ clearTimeout(disconnectTimer);
173
+ process.removeListener('SIGINT', onSigint);
174
+ process.removeListener('SIGTERM', onSigterm);
175
+ process.removeListener('SIGHUP', onSighup);
176
+ client.close();
177
+ }
66
178
  }
67
179
  }
68
180
  exports.default = AgendaPlan;
@@ -763,6 +763,10 @@ function normalizeIntentPayload({ agentId, jsonBody, merged, }) {
763
763
  const overlayChannel = optionalString(merged.channelId);
764
764
  const overlayThreadTs = optionalString(merged.threadTs);
765
765
  const destinationKind = pickDestinationKind({ bodyDestination, overlayKind });
766
+ if (destinationKind !== 'slack_channel' &&
767
+ (merged.channelId !== undefined || bodyDestination?.channelId !== undefined)) {
768
+ throw invalidIntent('--channel / destination.channelId only applies to slack_channel.');
769
+ }
766
770
  if (destinationKind !== 'slack_channel' && (overlayThreadTs || bodyDestination?.threadTs)) {
767
771
  throw invalidIntent('--thread-ts / destination.threadTs only applies to slack_channel.');
768
772
  }
@@ -13,12 +13,31 @@ class AgentsIntuitionsGenerate extends base_command_1.BaseCommand {
13
13
  async run() {
14
14
  this.requireAuth();
15
15
  const { args, flags } = await this.parse(AgentsIntuitionsGenerate);
16
- const result = await data_provider_1.dataService.generateAgentIntuitions(args.agentId, flags.force);
17
- this.log(flags.json
18
- ? this.agentJson(args.agentId, result)
19
- : `Generated intuitions for agent ${args.agentId}`);
20
- if (!flags.json)
21
- this.printAgentUrl(args.agentId);
16
+ let result;
17
+ try {
18
+ result = await data_provider_1.dataService.generateAgentIntuitions(args.agentId, flags.force);
19
+ }
20
+ catch (err) {
21
+ this.handleApiError(err);
22
+ }
23
+ if (flags.json) {
24
+ this.log(this.agentJson(args.agentId, result));
25
+ return;
26
+ }
27
+ const response = result;
28
+ if (response.outcome === 'insufficient_context') {
29
+ this.log('No intuitions yet: add relevant agent memories or activity, then try again.');
30
+ }
31
+ else if (response.outcome === 'no_suggestions') {
32
+ this.log('No intuitions were suggested from the available context. Try again after more activity.');
33
+ }
34
+ else if (response.intuitions?.length === 0) {
35
+ this.log('No intuitions were returned. Add relevant memories or activity, then try again.');
36
+ }
37
+ else {
38
+ this.log(`Generated intuitions for agent ${args.agentId}`);
39
+ }
40
+ this.printAgentUrl(args.agentId);
22
41
  }
23
42
  }
24
43
  exports.default = AgentsIntuitionsGenerate;
@@ -16,8 +16,13 @@ class AgentsIntuitionsList extends base_command_1.BaseCommand {
16
16
  this.printJsonResult((0, list_output_1.listResult)(this.withAgentContext(args.agentId, result), 'intuitions'));
17
17
  return;
18
18
  }
19
- const text = JSON.stringify(result.intuitions ?? result, null, 2);
20
- this.log(text);
19
+ const intuitions = result.intuitions ?? result;
20
+ if (Array.isArray(intuitions) && intuitions.length === 0) {
21
+ this.log(`No active intuitions. Run skrr agents intuitions generate ${args.agentId} to look for suggestions.`);
22
+ }
23
+ else {
24
+ this.log(JSON.stringify(intuitions, null, 2));
25
+ }
21
26
  this.printAgentUrl(args.agentId);
22
27
  }
23
28
  }
@@ -17,6 +17,12 @@ function proactiveInstallReadiness(result, bin) {
17
17
  if (result.warnings.length === 0) {
18
18
  return { status: 'ready', message: 'All requested setup steps completed.' };
19
19
  }
20
+ if (result.warnings.some((warning) => warning.code === 'always_on_off')) {
21
+ return {
22
+ status: 'degraded',
23
+ message: 'The Agent is off. Review the setup warnings and enable Always-On explicitly if you want it to run.',
24
+ };
25
+ }
20
26
  // A full trigger budget must not make a new persona-only Agent look broken
21
27
  // when its core system commitments, local execution policy, and Always-On
22
28
  // state installed successfully. The optional backlog watcher is deferred;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class AgentsTodosEdit extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ at: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../../base-command");
5
+ const schedule_1 = require("../../../lib/schedule");
6
+ class AgentsTodosEdit extends base_command_1.BaseCommand {
7
+ static description = 'Change the name or time of a pending to-do';
8
+ static examples = [
9
+ '<%= config.bin %> agents todos edit <todo-id> --name "Review launch"',
10
+ '<%= config.bin %> agents todos edit <todo-id> --at 2026-11-05T18:00:00Z',
11
+ ];
12
+ static args = { id: core_1.Args.string({ description: 'To-Do ID', required: true, ignoreStdin: true }) };
13
+ static flags = {
14
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
15
+ name: core_1.Flags.string({ description: 'New name' }),
16
+ at: core_1.Flags.string({ description: 'New ISO-8601 run time' }),
17
+ };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { args, flags } = await this.parse(AgentsTodosEdit);
21
+ if (flags.name === undefined && flags.at === undefined)
22
+ this.error('Supply --name or --at.', { exit: 1 });
23
+ let todo;
24
+ try {
25
+ const current = await schedule_1.scheduleApi.getTodo(args.id);
26
+ if (!current.updatedAt)
27
+ this.error('The to-do has no version; refresh before editing.', { exit: 1 });
28
+ todo = await schedule_1.scheduleApi.updateTodo(args.id, {
29
+ ...(flags.name !== undefined ? { name: flags.name } : {}),
30
+ ...(flags.at !== undefined ? { runAt: flags.at } : {}),
31
+ expectedUpdatedAt: current.updatedAt,
32
+ });
33
+ }
34
+ catch (err) {
35
+ this.handleApiError(err);
36
+ }
37
+ if (flags.json)
38
+ this.log(JSON.stringify(todo, null, 2));
39
+ else
40
+ this.log(`Updated to-do ${todo.id} — "${todo.name}" at ${todo.runAt}`);
41
+ }
42
+ }
43
+ exports.default = AgentsTodosEdit;
@@ -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 fire respects the same gates a scheduled fire would (quiet hours, caps,
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 fire respects the same gates a scheduled fire would (quiet hours, caps,
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
  }
@@ -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
- const cron = flags.cron ?? '0 8 * * *';
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,8 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class GoalsBriefingStatus extends BaseCommand {
3
+ static description: string;
4
+ static flags: {
5
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ };
7
+ run(): Promise<void>;
8
+ }
@@ -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 in a pool (owner, an agent, user, or workspace)';
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,
@@ -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;
@@ -179,6 +180,11 @@ export declare const scheduleApi: {
179
180
  listTodos: (agentId: string, date?: string, timezone?: string) => Promise<ListTodosResponse>;
180
181
  getTodo: (todoId: string) => Promise<Todo>;
181
182
  createTodo: (input: CreateTodoInput) => Promise<Todo>;
183
+ updateTodo: (todoId: string, patch: {
184
+ name?: string;
185
+ runAt?: string;
186
+ expectedUpdatedAt?: string;
187
+ }) => Promise<Todo>;
182
188
  /** Cancel a To-Do (DELETE soft-cancels pending; see todos.js). */
183
189
  cancelTodo: (todoId: string) => Promise<Todo>;
184
190
  proposeSmartSchedule: (input: {
@@ -61,6 +61,7 @@ exports.scheduleApi = {
61
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),
@@ -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>;