@slock-ai/daemon 0.43.0 → 0.44.2

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.
@@ -1323,7 +1323,7 @@ Thread messages and system messages (e.g. task-claim / task-status announcements
1323
1323
  const msgShort = r.messageId ? r.messageId.slice(0, 8) : "";
1324
1324
  return `${label} (msg:${msgShort}): claimed`;
1325
1325
  }
1326
- return `${label}: FAILED \u2014 ${r.reason || "already claimed"}`;
1326
+ return `${label}: FAILED \u2014 ${r.reason || "already claimed"}. Do not reply.`;
1327
1327
  });
1328
1328
  const succeeded = data.results.filter((r) => r.success).length;
1329
1329
  const failed = data.results.length - succeeded;
@@ -1328,8 +1328,25 @@ function probeClaude(deps = {}) {
1328
1328
  }
1329
1329
  var ClaudeDriver = class {
1330
1330
  id = "claude";
1331
+ lifecycle = {
1332
+ kind: "persistent",
1333
+ stdin: "gated",
1334
+ inFlightWake: "queue"
1335
+ };
1336
+ communication = {
1337
+ chat: "slock_cli",
1338
+ runtimeControl: "mcp_runtime_actions"
1339
+ };
1340
+ session = {
1341
+ recovery: "resume_or_fresh"
1342
+ };
1343
+ model = {
1344
+ detectedModelsVerifiedAs: "launchable",
1345
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
1346
+ };
1331
1347
  supportsStdinNotification = true;
1332
1348
  mcpToolPrefix = "mcp__chat__";
1349
+ usesSlockCliForCommunication = true;
1333
1350
  // Claude Code supports same-turn steering, but raw stdin injection at an
1334
1351
  // arbitrary busy instant can collide with active signed thinking blocks. The
1335
1352
  // daemon therefore gates busy delivery on Claude stream-json boundaries.
@@ -1631,8 +1648,25 @@ function joinReasoningText(item) {
1631
1648
  }
1632
1649
  var CodexDriver = class {
1633
1650
  id = "codex";
1651
+ lifecycle = {
1652
+ kind: "persistent",
1653
+ stdin: "direct",
1654
+ inFlightWake: "steer"
1655
+ };
1656
+ communication = {
1657
+ chat: "slock_cli",
1658
+ runtimeControl: "mcp_runtime_actions"
1659
+ };
1660
+ session = {
1661
+ recovery: "resume_or_fresh"
1662
+ };
1663
+ model = {
1664
+ detectedModelsVerifiedAs: "launchable",
1665
+ toLaunchSpec: (modelId) => ({ params: { model: modelId } })
1666
+ };
1634
1667
  supportsStdinNotification = true;
1635
1668
  mcpToolPrefix = "mcp_chat_";
1669
+ usesSlockCliForCommunication = true;
1636
1670
  busyDeliveryMode = "direct";
1637
1671
  supportsNativeStandingPrompt = true;
1638
1672
  probe() {
@@ -2027,7 +2061,7 @@ function detectCodexModels(home = os.homedir()) {
2027
2061
  if (entry?.visibility && entry.visibility !== "public") continue;
2028
2062
  if (entry?.supported_in_api === false) continue;
2029
2063
  const label = typeof entry?.display_name === "string" && entry.display_name.length > 0 ? entry.display_name : slug;
2030
- models.push({ id: slug, label });
2064
+ models.push({ id: slug, label, verified: "launchable" });
2031
2065
  }
2032
2066
  } catch {
2033
2067
  return null;
@@ -2049,6 +2083,23 @@ import path5 from "path";
2049
2083
  import { writeFileSync as writeFileSync3 } from "fs";
2050
2084
  var CopilotDriver = class {
2051
2085
  id = "copilot";
2086
+ lifecycle = {
2087
+ kind: "per_turn",
2088
+ start: "immediate",
2089
+ exit: "natural",
2090
+ inFlightWake: "spawn_new"
2091
+ };
2092
+ communication = {
2093
+ chat: "mcp_chat_bridge",
2094
+ runtimeControl: "mcp_runtime_actions"
2095
+ };
2096
+ session = {
2097
+ recovery: "resume_or_fresh"
2098
+ };
2099
+ model = {
2100
+ detectedModelsVerifiedAs: "launchable",
2101
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
2102
+ };
2052
2103
  supportsStdinNotification = false;
2053
2104
  mcpToolPrefix = "";
2054
2105
  busyDeliveryMode = "none";
@@ -2182,11 +2233,28 @@ var CopilotDriver = class {
2182
2233
  };
2183
2234
 
2184
2235
  // src/drivers/cursor.ts
2185
- import { spawn as spawn4 } from "child_process";
2236
+ import { spawn as spawn4, spawnSync } from "child_process";
2186
2237
  import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
2187
2238
  import path6 from "path";
2188
2239
  var CursorDriver = class {
2189
2240
  id = "cursor";
2241
+ lifecycle = {
2242
+ kind: "per_turn",
2243
+ start: "immediate",
2244
+ exit: "natural",
2245
+ inFlightWake: "spawn_new"
2246
+ };
2247
+ communication = {
2248
+ chat: "mcp_chat_bridge",
2249
+ runtimeControl: "mcp_runtime_actions"
2250
+ };
2251
+ session = {
2252
+ recovery: "resume_or_fresh"
2253
+ };
2254
+ model = {
2255
+ detectedModelsVerifiedAs: "launchable",
2256
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
2257
+ };
2190
2258
  supportsStdinNotification = false;
2191
2259
  mcpToolPrefix = "mcp__chat__";
2192
2260
  busyDeliveryMode = "none";
@@ -2299,7 +2367,48 @@ var CursorDriver = class {
2299
2367
  messageNotificationStyle: "poll"
2300
2368
  });
2301
2369
  }
2370
+ async detectModels() {
2371
+ return detectCursorModels();
2372
+ }
2302
2373
  };
2374
+ function parseCursorModelsOutput(output) {
2375
+ const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
2376
+ const models = [];
2377
+ let defaultModel;
2378
+ for (const rawLine of stripAnsi(output).split(/\r?\n/)) {
2379
+ const line = rawLine.trim();
2380
+ if (!line || /^available models$/i.test(line) || /^tip:/i.test(line)) continue;
2381
+ if (/^no models available/i.test(line) || /^failed to load models:/i.test(line)) continue;
2382
+ let modelLine = line;
2383
+ const markerMatch = modelLine.match(/\s+\(([^)]+)\)$/);
2384
+ const markers = markerMatch?.[1]?.split(",").map((part) => part.trim().toLowerCase()) ?? [];
2385
+ if (markers.length > 0 && markers.every((part) => part === "current" || part === "default")) {
2386
+ const markerStart = markerMatch?.index ?? modelLine.length;
2387
+ modelLine = modelLine.slice(0, markerStart).trim();
2388
+ }
2389
+ const match = modelLine.match(/^(\S+)(?:\s+-\s+(.+))?$/);
2390
+ if (!match) continue;
2391
+ const id = match[1]?.trim();
2392
+ if (!id || id.startsWith("-")) continue;
2393
+ const label = match[2]?.trim() || id;
2394
+ models.push({ id, label, verified: "launchable" });
2395
+ if (markers.includes("default")) defaultModel = id;
2396
+ }
2397
+ if (models.length === 0) return null;
2398
+ return { models, default: defaultModel };
2399
+ }
2400
+ function detectCursorModels(runCommand = runCursorModelsCommand) {
2401
+ const result = runCommand();
2402
+ if (result.error || result.status !== 0) return null;
2403
+ return parseCursorModelsOutput(String(result.stdout || ""));
2404
+ }
2405
+ function runCursorModelsCommand() {
2406
+ return spawnSync("cursor-agent", ["models"], {
2407
+ env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
2408
+ encoding: "utf8",
2409
+ timeout: 5e3
2410
+ });
2411
+ }
2303
2412
 
2304
2413
  // src/drivers/gemini.ts
2305
2414
  import { spawn as spawn5 } from "child_process";
@@ -2318,6 +2427,23 @@ function buildGeminiSpawnEnv(ctx) {
2318
2427
  }
2319
2428
  var GeminiDriver = class {
2320
2429
  id = "gemini";
2430
+ lifecycle = {
2431
+ kind: "per_turn",
2432
+ start: "immediate",
2433
+ exit: "natural",
2434
+ inFlightWake: "spawn_new"
2435
+ };
2436
+ communication = {
2437
+ chat: "mcp_chat_bridge",
2438
+ runtimeControl: "mcp_runtime_actions"
2439
+ };
2440
+ session = {
2441
+ recovery: "resume_or_fresh"
2442
+ };
2443
+ model = {
2444
+ detectedModelsVerifiedAs: "launchable",
2445
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
2446
+ };
2321
2447
  supportsStdinNotification = false;
2322
2448
  mcpToolPrefix = "";
2323
2449
  busyDeliveryMode = "none";
@@ -2447,6 +2573,22 @@ function parseToolArguments(raw) {
2447
2573
  }
2448
2574
  var KimiDriver = class {
2449
2575
  id = "kimi";
2576
+ lifecycle = {
2577
+ kind: "persistent",
2578
+ stdin: "direct",
2579
+ inFlightWake: "steer"
2580
+ };
2581
+ communication = {
2582
+ chat: "mcp_chat_bridge",
2583
+ runtimeControl: "mcp_runtime_actions"
2584
+ };
2585
+ session = {
2586
+ recovery: "resume_or_fresh"
2587
+ };
2588
+ model = {
2589
+ detectedModelsVerifiedAs: "launchable",
2590
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
2591
+ };
2450
2592
  supportsStdinNotification = true;
2451
2593
  mcpToolPrefix = "";
2452
2594
  busyDeliveryMode = "direct";
@@ -2647,7 +2789,7 @@ function detectKimiModels(home = os2.homedir()) {
2647
2789
  let key = match[1].trim();
2648
2790
  if (key.startsWith('"') && key.endsWith('"')) key = key.slice(1, -1);
2649
2791
  if (!key) continue;
2650
- models.push({ id: key, label: key });
2792
+ models.push({ id: key, label: key, verified: "launchable" });
2651
2793
  }
2652
2794
  void sectionRe;
2653
2795
  if (models.length === 0) return null;
@@ -2802,7 +2944,10 @@ function buildOpenCodeLaunchOptions(ctx, home = os3.homedir()) {
2802
2944
  return { args, env, config };
2803
2945
  }
2804
2946
  function detectOpenCodeModels(home = os3.homedir()) {
2805
- const models = [...RUNTIME_MODELS.opencode || []];
2947
+ const models = (RUNTIME_MODELS.opencode || []).map((model) => ({
2948
+ ...model,
2949
+ verified: "suggestion_only"
2950
+ }));
2806
2951
  const providers = recordField(readLocalOpenCodeConfig(home).provider);
2807
2952
  for (const [providerId, providerConfig] of Object.entries(providers)) {
2808
2953
  const providerModels = recordField(recordField(providerConfig).models);
@@ -2811,7 +2956,7 @@ function detectOpenCodeModels(home = os3.homedir()) {
2811
2956
  if (models.some((model2) => model2.id === fullId)) continue;
2812
2957
  const model = recordField(modelConfig);
2813
2958
  const name = typeof model.name === "string" && model.name.length > 0 ? model.name : fullId;
2814
- models.push({ id: fullId, label: name });
2959
+ models.push({ id: fullId, label: name, verified: "launchable" });
2815
2960
  }
2816
2961
  }
2817
2962
  return models.length > 0 ? { models } : null;
@@ -2834,6 +2979,38 @@ function buildOpenCodeSystemPrompt(config) {
2834
2979
  }
2835
2980
  var OpenCodeDriver = class {
2836
2981
  id = "opencode";
2982
+ lifecycle = {
2983
+ kind: "per_turn",
2984
+ start: "defer_until_concrete_message",
2985
+ exit: "terminate_on_turn_end",
2986
+ inFlightWake: "coalesce_into_pending"
2987
+ };
2988
+ communication = {
2989
+ chat: "slock_cli",
2990
+ runtimeControl: "mcp_runtime_actions"
2991
+ };
2992
+ session = {
2993
+ recovery: "resume_or_fresh"
2994
+ };
2995
+ model = {
2996
+ detectedModelsVerifiedAs: "launchable",
2997
+ toLaunchSpec: (modelId, ctx, opts) => {
2998
+ if (!ctx) return { args: ["--model", modelId] };
2999
+ const launchCtx = {
3000
+ ...ctx,
3001
+ config: {
3002
+ ...ctx.config,
3003
+ model: modelId
3004
+ }
3005
+ };
3006
+ const launch = buildOpenCodeLaunchOptions(launchCtx, opts?.home);
3007
+ return {
3008
+ args: launch.args,
3009
+ env: launch.env,
3010
+ config: launch.config
3011
+ };
3012
+ }
3013
+ };
2837
3014
  supportsStdinNotification = false;
2838
3015
  mcpToolPrefix = CHAT_MCP_TOOL_PREFIX;
2839
3016
  busyDeliveryMode = "none";
@@ -3060,6 +3237,22 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
3060
3237
 
3061
3238
  // src/agentProcessManager.ts
3062
3239
  var DATA_DIR = path11.join(os4.homedir(), ".slock", "agents");
3240
+ var DEFAULT_MAX_CONCURRENT_AGENT_STARTS = 1;
3241
+ var DEFAULT_AGENT_START_INTERVAL_MS = 500;
3242
+ function readPositiveIntegerEnv(name, fallback) {
3243
+ const raw = process.env[name];
3244
+ if (!raw) return fallback;
3245
+ const parsed = Number(raw);
3246
+ if (!Number.isFinite(parsed) || parsed < 1) return fallback;
3247
+ return Math.floor(parsed);
3248
+ }
3249
+ function readNonNegativeIntegerEnv(name, fallback) {
3250
+ const raw = process.env[name];
3251
+ if (!raw) return fallback;
3252
+ const parsed = Number(raw);
3253
+ if (!Number.isFinite(parsed) || parsed < 0) return fallback;
3254
+ return Math.floor(parsed);
3255
+ }
3063
3256
  function toLocalTime(iso) {
3064
3257
  const d = new Date(iso);
3065
3258
  if (isNaN(d.getTime())) return iso;
@@ -3783,6 +3976,15 @@ var AgentProcessManager = class _AgentProcessManager {
3783
3976
  agents = /* @__PURE__ */ new Map();
3784
3977
  agentsStarting = /* @__PURE__ */ new Set();
3785
3978
  // Prevent concurrent starts of same agent
3979
+ queuedAgentStarts = /* @__PURE__ */ new Map();
3980
+ agentStartQueue = [];
3981
+ activeAgentStartPermits = /* @__PURE__ */ new Set();
3982
+ activeAgentStartCount = 0;
3983
+ agentStartPumpTimer = null;
3984
+ lastAgentStartAt = 0;
3985
+ lastAgentStartAgentId = null;
3986
+ maxConcurrentAgentStarts;
3987
+ agentStartIntervalMs;
3786
3988
  startingInboxes = /* @__PURE__ */ new Map();
3787
3989
  /** Cached configs for agents whose process exited normally — enables auto-restart on next message */
3788
3990
  idleAgentConfigs = /* @__PURE__ */ new Map();
@@ -3807,8 +4009,137 @@ var AgentProcessManager = class _AgentProcessManager {
3807
4009
  this.driverResolver = opts.driverResolver || getDriver;
3808
4010
  this.defaultAgentEnvVarsProvider = opts.defaultAgentEnvVarsProvider || null;
3809
4011
  this.tracer = opts.tracer ?? noopTracer;
4012
+ this.maxConcurrentAgentStarts = Math.max(
4013
+ 1,
4014
+ Math.floor(
4015
+ opts.runtimeStartScheduler?.maxConcurrentStarts ?? readPositiveIntegerEnv("SLOCK_DAEMON_MAX_CONCURRENT_AGENT_STARTS", DEFAULT_MAX_CONCURRENT_AGENT_STARTS)
4016
+ )
4017
+ );
4018
+ this.agentStartIntervalMs = Math.max(
4019
+ 0,
4020
+ Math.floor(
4021
+ opts.runtimeStartScheduler?.minStartIntervalMs ?? readNonNegativeIntegerEnv("SLOCK_DAEMON_AGENT_START_INTERVAL_MS", DEFAULT_AGENT_START_INTERVAL_MS)
4022
+ )
4023
+ );
3810
4024
  }
3811
4025
  async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
4026
+ if (this.agents.has(agentId)) {
4027
+ logger.info(`[Agent ${agentId}] Start ignored (already running)`);
4028
+ return;
4029
+ }
4030
+ if (this.agentsStarting.has(agentId)) {
4031
+ logger.info(`[Agent ${agentId}] Start ignored (startup in progress)`);
4032
+ return;
4033
+ }
4034
+ if (this.queuedAgentStarts.has(agentId)) {
4035
+ logger.info(`[Agent ${agentId}] Start ignored (startup already queued)`);
4036
+ return;
4037
+ }
4038
+ return new Promise((resolve, reject) => {
4039
+ const item = {
4040
+ agentId,
4041
+ config,
4042
+ wakeMessage,
4043
+ unreadSummary,
4044
+ resumePrompt,
4045
+ launchId,
4046
+ resolve,
4047
+ reject
4048
+ };
4049
+ this.agentStartQueue.push(item);
4050
+ this.queuedAgentStarts.set(agentId, item);
4051
+ logger.info(
4052
+ `[Agent ${agentId}] Start queued (queue=${this.agentStartQueue.length}, active=${this.activeAgentStartCount}, max=${this.maxConcurrentAgentStarts}, interval=${this.agentStartIntervalMs}ms)`
4053
+ );
4054
+ this.pumpAgentStartQueue();
4055
+ });
4056
+ }
4057
+ pumpAgentStartQueue() {
4058
+ if (this.agentStartPumpTimer) return;
4059
+ if (this.agentStartQueue.length === 0) return;
4060
+ if (this.activeAgentStartCount >= this.maxConcurrentAgentStarts) return;
4061
+ const next = this.agentStartQueue[0];
4062
+ const shouldRateLimit = next ? next.agentId !== this.lastAgentStartAgentId : true;
4063
+ const elapsed = Date.now() - this.lastAgentStartAt;
4064
+ const waitMs = shouldRateLimit ? Math.max(0, this.agentStartIntervalMs - elapsed) : 0;
4065
+ if (waitMs > 0) {
4066
+ this.agentStartPumpTimer = setTimeout(() => {
4067
+ this.agentStartPumpTimer = null;
4068
+ this.pumpAgentStartQueue();
4069
+ }, waitMs);
4070
+ return;
4071
+ }
4072
+ const item = this.agentStartQueue.shift();
4073
+ if (!item) return;
4074
+ if (this.queuedAgentStarts.get(item.agentId) !== item) {
4075
+ this.pumpAgentStartQueue();
4076
+ return;
4077
+ }
4078
+ this.queuedAgentStarts.delete(item.agentId);
4079
+ if (this.agents.has(item.agentId) || this.agentsStarting.has(item.agentId)) {
4080
+ logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
4081
+ item.resolve();
4082
+ this.pumpAgentStartQueue();
4083
+ return;
4084
+ }
4085
+ this.activeAgentStartCount++;
4086
+ this.activeAgentStartPermits.add(item.agentId);
4087
+ this.lastAgentStartAt = Date.now();
4088
+ this.lastAgentStartAgentId = item.agentId;
4089
+ logger.info(
4090
+ `[Agent ${item.agentId}] Dequeued start (remaining=${this.agentStartQueue.length}, active=${this.activeAgentStartCount})`
4091
+ );
4092
+ this.startAgentNow(
4093
+ item.agentId,
4094
+ item.config,
4095
+ item.wakeMessage,
4096
+ item.unreadSummary,
4097
+ item.resumePrompt,
4098
+ item.launchId
4099
+ ).then(item.resolve, (err) => {
4100
+ this.releaseAgentStartPermit(item.agentId, "start failed");
4101
+ item.reject(err);
4102
+ });
4103
+ }
4104
+ releaseAgentStartPermit(agentId, reason) {
4105
+ if (!this.activeAgentStartPermits.delete(agentId)) return false;
4106
+ this.activeAgentStartCount = Math.max(0, this.activeAgentStartCount - 1);
4107
+ logger.info(
4108
+ `[Agent ${agentId}] Start permit released (${reason}) (active=${this.activeAgentStartCount}, queue=${this.agentStartQueue.length})`
4109
+ );
4110
+ this.pumpAgentStartQueue();
4111
+ return true;
4112
+ }
4113
+ cancelQueuedAgentStart(agentId, reason) {
4114
+ const item = this.queuedAgentStarts.get(agentId);
4115
+ if (!item) return false;
4116
+ this.queuedAgentStarts.delete(agentId);
4117
+ this.agentStartQueue = this.agentStartQueue.filter((candidate) => candidate !== item);
4118
+ this.startingInboxes.delete(agentId);
4119
+ if (this.agentStartQueue.length === 0 && this.agentStartPumpTimer) {
4120
+ clearTimeout(this.agentStartPumpTimer);
4121
+ this.agentStartPumpTimer = null;
4122
+ }
4123
+ logger.info(`[Agent ${agentId}] Queued start cancelled (${reason})`);
4124
+ item.resolve();
4125
+ return true;
4126
+ }
4127
+ cancelAllQueuedAgentStarts(reason) {
4128
+ for (const item of this.agentStartQueue) {
4129
+ if (this.queuedAgentStarts.get(item.agentId) === item) {
4130
+ logger.info(`[Agent ${item.agentId}] Queued start cancelled (${reason})`);
4131
+ item.resolve();
4132
+ }
4133
+ }
4134
+ this.agentStartQueue = [];
4135
+ this.queuedAgentStarts.clear();
4136
+ this.startingInboxes.clear();
4137
+ if (this.agentStartPumpTimer) {
4138
+ clearTimeout(this.agentStartPumpTimer);
4139
+ this.agentStartPumpTimer = null;
4140
+ }
4141
+ }
4142
+ async startAgentNow(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId) {
3812
4143
  if (this.agents.has(agentId)) {
3813
4144
  logger.info(`[Agent ${agentId}] Start ignored (already running)`);
3814
4145
  return;
@@ -3910,6 +4241,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
3910
4241
  this.sendAgentStatus(agentId, "active", launchId || null);
3911
4242
  this.broadcastActivity(agentId, "online", "Process idle");
3912
4243
  logger.info(`[Agent ${agentId}] Deferred ${driver.id} spawn until first concrete message`);
4244
+ this.releaseAgentStartPermit(agentId, "spawn deferred");
3913
4245
  for (const message of pendingMessages) {
3914
4246
  this.deliverMessage(agentId, message);
3915
4247
  }
@@ -3947,6 +4279,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
3947
4279
  runtimeTraceSpan: null,
3948
4280
  lastActivity: "",
3949
4281
  lastActivityDetail: "",
4282
+ activityClientSeq: 0,
3950
4283
  recentStdout: [],
3951
4284
  recentStderr: [],
3952
4285
  lastRuntimeError: null,
@@ -4012,6 +4345,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4012
4345
  if (this.agents.has(agentId)) {
4013
4346
  const ap = this.agents.get(agentId);
4014
4347
  if (ap.process !== proc) return;
4348
+ this.releaseAgentStartPermit(agentId, "process closed");
4015
4349
  if (ap.notificationTimer) {
4016
4350
  clearTimeout(ap.notificationTimer);
4017
4351
  }
@@ -4176,6 +4510,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4176
4510
  return leftKeys.every((key) => left?.[key] === right?.[key]);
4177
4511
  }
4178
4512
  async stopAgent(agentId, { wait = false, silent = false } = {}) {
4513
+ this.cancelQueuedAgentStart(agentId, "stop requested");
4179
4514
  this.idleAgentConfigs.delete(agentId);
4180
4515
  const ap = this.agents.get(agentId);
4181
4516
  if (!ap) {
@@ -4184,6 +4519,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4184
4519
  }
4185
4520
  return;
4186
4521
  }
4522
+ this.releaseAgentStartPermit(agentId, "stop requested");
4187
4523
  if (ap.notificationTimer) {
4188
4524
  clearTimeout(ap.notificationTimer);
4189
4525
  }
@@ -4225,7 +4561,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4225
4561
  deliverMessage(agentId, message) {
4226
4562
  const ap = this.agents.get(agentId);
4227
4563
  if (!ap) {
4228
- if (this.agentsStarting.has(agentId)) {
4564
+ if (this.agentsStarting.has(agentId) || this.queuedAgentStarts.has(agentId)) {
4229
4565
  const pending = this.startingInboxes.get(agentId) || [];
4230
4566
  pending.push(message);
4231
4567
  this.startingInboxes.set(agentId, pending);
@@ -4285,6 +4621,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4285
4621
  }
4286
4622
  }
4287
4623
  async stopAll() {
4624
+ this.cancelAllQueuedAgentStarts("daemon shutdown");
4288
4625
  this.idleAgentConfigs.clear();
4289
4626
  const ids = [...this.agents.keys()];
4290
4627
  await Promise.all(ids.map((id) => this.stopAgent(id, { wait: true, silent: true })));
@@ -4619,13 +4956,15 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4619
4956
  if (!hasToolStart) {
4620
4957
  entries.push({ kind: "status", activity, detail });
4621
4958
  }
4959
+ if (ap) ap.activityClientSeq += 1;
4622
4960
  this.sendToServer({
4623
4961
  type: "agent:activity",
4624
4962
  agentId,
4625
4963
  activity,
4626
4964
  detail,
4627
4965
  entries,
4628
- launchId: launchIdOverride || ap?.launchId || void 0
4966
+ launchId: launchIdOverride || ap?.launchId || void 0,
4967
+ clientSeq: ap?.activityClientSeq
4629
4968
  });
4630
4969
  if (ap) {
4631
4970
  ap.lastActivity = activity;
@@ -4637,12 +4976,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4637
4976
  this.recordRuntimeTraceEvent(agentId, ap, "activity.heartbeat.sent", {
4638
4977
  activity: ap.lastActivity
4639
4978
  });
4979
+ ap.activityClientSeq += 1;
4640
4980
  this.sendToServer({
4641
4981
  type: "agent:activity",
4642
4982
  agentId,
4643
4983
  activity: ap.lastActivity,
4644
4984
  detail: ap.lastActivityDetail,
4645
- launchId: launchIdOverride || ap.launchId || void 0
4985
+ launchId: launchIdOverride || ap.launchId || void 0,
4986
+ clientSeq: ap.activityClientSeq
4646
4987
  });
4647
4988
  }, ACTIVITY_HEARTBEAT_MS);
4648
4989
  }
@@ -4654,6 +4995,42 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4654
4995
  }
4655
4996
  }
4656
4997
  }
4998
+ /**
4999
+ * Respond to a server-issued `agent:activity_probe`. Echoes the
5000
+ * agent's current `lastActivity` back through the existing
5001
+ * `agent:activity` upstream channel with the matching `probeId`.
5002
+ *
5003
+ * Why this exists: the server's stale-activity sweep used to
5004
+ * synthesize `online` whenever a transient state went 90s without
5005
+ * an update. That invented state without consulting ground truth
5006
+ * and produced "agent shows green/idle but is actually working" UI
5007
+ * staleness (#engineering:72283cf7 task #340 RCA).
5008
+ *
5009
+ * The new flow: server sends `agent:activity_probe` for stale
5010
+ * agents, daemon replies here with the *real* current activity, and
5011
+ * the server only falls back to synth-online if the probe times out
5012
+ * (5s). The body is intentionally minimal — no entries, no
5013
+ * heartbeat side-effects, no state mutation. We just echo what we
5014
+ * already know.
5015
+ *
5016
+ * If the agent is no longer running locally (`ap` undefined), we
5017
+ * report `offline` so the server stops believing the agent is busy.
5018
+ */
5019
+ respondToActivityProbe(agentId, probeId) {
5020
+ const ap = this.agents.get(agentId);
5021
+ const activity = ap?.lastActivity || "offline";
5022
+ const detail = ap?.lastActivityDetail || (ap ? "" : "Agent not running");
5023
+ if (ap) ap.activityClientSeq += 1;
5024
+ this.sendToServer({
5025
+ type: "agent:activity",
5026
+ agentId,
5027
+ activity,
5028
+ detail,
5029
+ launchId: ap?.launchId || void 0,
5030
+ probeId,
5031
+ clientSeq: ap?.activityClientSeq
5032
+ });
5033
+ }
4657
5034
  flushPendingTrajectory(agentId) {
4658
5035
  const ap = this.agents.get(agentId);
4659
5036
  const pending = ap?.pendingTrajectory;
@@ -4945,6 +5322,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
4945
5322
  this.finishCompactionIfActive(agentId, "Context compaction finished (inferred from turn end)");
4946
5323
  this.flushPendingTrajectory(agentId);
4947
5324
  if (ap) {
5325
+ this.releaseAgentStartPermit(agentId, "initial turn ended");
4948
5326
  this.clearGatedInFlightBatch(agentId, ap, "turn_end");
4949
5327
  if (event.sessionId) ap.sessionId = event.sessionId;
4950
5328
  ap.gatedSteering.outstandingToolUses = 0;
@@ -5534,6 +5912,8 @@ function summarizeIncomingMessage(msg) {
5534
5912
  return `(agent=${msg.agentId}, path=${msg.path})`;
5535
5913
  case "agent:skills:list":
5536
5914
  return `(agent=${msg.agentId}, runtime=${msg.runtime || "auto"})`;
5915
+ case "agent:activity_probe":
5916
+ return `(agent=${msg.agentId}, probe=${msg.probeId.slice(0, 8)}, purpose=${msg.purpose})`;
5537
5917
  case "machine:workspace:delete":
5538
5918
  return `(directory=${msg.directoryName})`;
5539
5919
  case "machine:runtime_models:detect":
@@ -5721,6 +6101,9 @@ var DaemonCore = class {
5721
6101
  this.connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global: [], workspace: [] });
5722
6102
  });
5723
6103
  break;
6104
+ case "agent:activity_probe":
6105
+ this.agentManager.respondToActivityProbe(msg.agentId, msg.probeId);
6106
+ break;
5724
6107
  case "machine:workspace:scan":
5725
6108
  logger.info("[Daemon] Scanning all workspace directories");
5726
6109
  this.agentManager.scanAllWorkspaces().then((directories) => {
@@ -5738,7 +6121,12 @@ var DaemonCore = class {
5738
6121
  const detect = typeof driver?.detectModels === "function" ? driver.detectModels() : Promise.resolve(null);
5739
6122
  Promise.resolve(detect).then((result) => {
5740
6123
  if (result) {
5741
- this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, models: result.models, default: result.default });
6124
+ const verified = driver.model.detectedModelsVerifiedAs;
6125
+ const models = result.models.map((model) => ({
6126
+ ...model,
6127
+ verified: model.verified ?? verified
6128
+ }));
6129
+ this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, models, default: result.default });
5742
6130
  } else {
5743
6131
  this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: "unsupported" });
5744
6132
  }
package/dist/cli/index.js CHANGED
@@ -1049,7 +1049,7 @@ function formatClaimResults(channel, data) {
1049
1049
  const msgShort = r.messageId ? r.messageId.slice(0, 8) : "";
1050
1050
  return `${label} (msg:${msgShort}): claimed`;
1051
1051
  }
1052
- return `${label}: FAILED \u2014 ${r.reason || "already claimed"}`;
1052
+ return `${label}: FAILED \u2014 ${r.reason || "already claimed"}. Do not reply.`;
1053
1053
  });
1054
1054
  const succeeded = data.results.filter((r) => r.success).length;
1055
1055
  const failed = data.results.length - succeeded;
@@ -1681,20 +1681,18 @@ function registerProfileUpdateCommand(parent) {
1681
1681
  }
1682
1682
 
1683
1683
  // src/commands/reminder/_format.ts
1684
- function toLocalTime2(iso) {
1685
- const d = new Date(iso);
1686
- if (isNaN(d.getTime())) return iso;
1687
- const pad = (n) => String(n).padStart(2, "0");
1688
- return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
1689
- }
1684
+ var MODIFY_HINT = "(to modify: snooze/update/cancel; slock reminder --help)";
1690
1685
  function formatReminder(r) {
1691
- const fireLocal = toLocalTime2(r.fireAt);
1692
1686
  const ref = r.msgRef ? ` ref=${r.msgRef}` : "";
1693
- const repeat = r.recurrence ? ` repeat=${r.recurrence.description}` : "";
1694
- return `#${r.reminderId.slice(0, 8)} [${r.status}] fires=${fireLocal} "${r.title}"${ref}${repeat}`;
1687
+ const timeField = r.status === "fired" ? `fired_at=${r.firedAt ?? r.fireAt}` : `next=${r.fireAt}`;
1688
+ return `#${r.reminderId.slice(0, 8)} [${r.status}] ${formatReminderType(r)} ${timeField} "${r.title}"${ref}`;
1695
1689
  }
1696
1690
  function formatReminderScheduled(r, warning) {
1697
- const lines = [`Reminder scheduled: ${formatReminder(r)}`];
1691
+ const lines = [
1692
+ `Reminder scheduled: #${r.reminderId.slice(0, 8)} ${formatReminderType(r)} "${r.title}"`,
1693
+ `Next: ${r.fireAt}`,
1694
+ MODIFY_HINT
1695
+ ];
1698
1696
  if (warning) lines.push(`Warning: ${warning}`);
1699
1697
  return lines.join("\n");
1700
1698
  }
@@ -1703,7 +1701,34 @@ function formatReminderList(reminders) {
1703
1701
  return reminders.map(formatReminder).join("\n");
1704
1702
  }
1705
1703
  function formatReminderCanceled(r) {
1706
- return `Reminder canceled: ${formatReminder(r)}`;
1704
+ return `Reminder canceled: #${r.reminderId.slice(0, 8)} [${r.status}] "${r.title}"`;
1705
+ }
1706
+ function formatReminderSnoozed(r) {
1707
+ return [
1708
+ `Reminder snoozed: #${r.reminderId.slice(0, 8)} ${formatReminderType(r)} "${r.title}"`,
1709
+ `Next: ${r.fireAt}`,
1710
+ MODIFY_HINT
1711
+ ].join("\n");
1712
+ }
1713
+ function formatReminderUpdated(r, warning) {
1714
+ const lines = [
1715
+ `Reminder updated: #${r.reminderId.slice(0, 8)} ${formatReminderType(r)} "${r.title}"`,
1716
+ `Next: ${r.fireAt}`,
1717
+ MODIFY_HINT
1718
+ ];
1719
+ if (warning) lines.push(`Warning: ${warning}`);
1720
+ return lines.join("\n");
1721
+ }
1722
+ function formatReminderLog(events) {
1723
+ if (events.length === 0) return "No reminder events.";
1724
+ return events.map((event) => {
1725
+ const next = event.nextFireAt ? ` next=${event.nextFireAt}` : " next=none";
1726
+ return `${event.occurredAt} ${event.eventType.toUpperCase()} by ${event.actorType}${event.actorId ? `:${event.actorId}` : ""}${next}`;
1727
+ }).join("\n");
1728
+ }
1729
+ function formatReminderType(r) {
1730
+ if (!r.recurrence) return "(one-time)";
1731
+ return `(recurring \xB7 ${r.recurrence.description})`;
1707
1732
  }
1708
1733
 
1709
1734
  // src/commands/reminder/schedule.ts
@@ -1799,9 +1824,9 @@ function registerReminderScheduleCommand(parent) {
1799
1824
  // src/commands/reminder/list.ts
1800
1825
  var VALID_STATUSES2 = /* @__PURE__ */ new Set(["scheduled", "fired", "canceled"]);
1801
1826
  function registerReminderListCommand(parent) {
1802
- parent.command("list").description("List your own reminders (defaults to scheduled)").option(
1827
+ parent.command("list").description("List your own reminders (defaults to scheduled and fired)").option("--all", "Include canceled reminders").option(
1803
1828
  "--status <s>",
1804
- "Comma-separated statuses (scheduled,fired,canceled). Default: scheduled"
1829
+ "Comma-separated statuses (scheduled,fired,canceled). Default: scheduled,fired"
1805
1830
  ).action(async (opts) => {
1806
1831
  let ctx;
1807
1832
  try {
@@ -1810,14 +1835,18 @@ function registerReminderListCommand(parent) {
1810
1835
  if (err instanceof AgentBootstrapError) fail(err.code, err.message);
1811
1836
  throw err;
1812
1837
  }
1813
- const statusRaw = opts.status && opts.status.trim().length > 0 ? opts.status.trim() : "scheduled";
1838
+ const statusRaw = opts.status && opts.status.trim().length > 0 ? opts.status.trim() : "scheduled,fired";
1814
1839
  for (const s of statusRaw.split(",").map((x) => x.trim()).filter(Boolean)) {
1815
1840
  if (!VALID_STATUSES2.has(s)) {
1816
1841
  fail("INVALID_ARG", `--status entries must be one of ${Array.from(VALID_STATUSES2).join("|")}; got ${s}`);
1817
1842
  }
1818
1843
  }
1819
1844
  const params = new URLSearchParams();
1820
- params.set("status", statusRaw);
1845
+ if (opts.all && !opts.status) {
1846
+ params.set("all", "true");
1847
+ } else {
1848
+ params.set("status", statusRaw);
1849
+ }
1821
1850
  const client = new ApiClient(ctx);
1822
1851
  const res = await client.request(
1823
1852
  "GET",
@@ -1831,6 +1860,37 @@ function registerReminderListCommand(parent) {
1831
1860
  });
1832
1861
  }
1833
1862
 
1863
+ // src/commands/reminder/_resolve.ts
1864
+ async function resolveReminderId(client, agentId, id, opts) {
1865
+ const trimmed = id.trim();
1866
+ if (!trimmed) fail("INVALID_ARG", "--id is required");
1867
+ if (trimmed.length >= 32) return trimmed;
1868
+ const params = new URLSearchParams();
1869
+ if (opts.all) {
1870
+ params.set("all", "true");
1871
+ } else if (opts.statuses && opts.statuses.length > 0) {
1872
+ params.set("status", opts.statuses.join(","));
1873
+ }
1874
+ const query = params.toString();
1875
+ const res = await client.request(
1876
+ "GET",
1877
+ `/internal/agent/${encodeURIComponent(agentId)}/reminders${query ? `?${query}` : ""}`
1878
+ );
1879
+ if (!res.ok) {
1880
+ const code = res.status >= 500 ? "SERVER_5XX" : opts.failureCode;
1881
+ fail(code, res.error ?? `HTTP ${res.status}`);
1882
+ }
1883
+ const matches = (res.data?.reminders ?? []).filter((r) => r.reminderId.startsWith(trimmed));
1884
+ if (matches.length === 0) {
1885
+ const scope = opts.all ? "reminder" : `${opts.statuses?.join("/") ?? "active"} reminder`;
1886
+ fail("NOT_FOUND", `No ${scope} matches id prefix '${trimmed}'.`);
1887
+ }
1888
+ if (matches.length > 1) {
1889
+ fail("AMBIGUOUS", `Ambiguous id prefix '${trimmed}' matches ${matches.length} reminders; pass a longer id.`);
1890
+ }
1891
+ return matches[0].reminderId;
1892
+ }
1893
+
1834
1894
  // src/commands/reminder/cancel.ts
1835
1895
  function registerReminderCancelCommand(parent) {
1836
1896
  parent.command("cancel").description("Cancel a scheduled reminder by id (full uuid or 8-char prefix)").requiredOption("--id <id>", "Reminder id (full uuid or short prefix)").action(async (opts) => {
@@ -1845,25 +1905,10 @@ function registerReminderCancelCommand(parent) {
1845
1905
  fail("INVALID_ARG", "--id is required");
1846
1906
  }
1847
1907
  const client = new ApiClient(ctx);
1848
- let fullId = opts.id;
1849
- if (opts.id.length < 32) {
1850
- const listRes = await client.request(
1851
- "GET",
1852
- `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders?status=scheduled`
1853
- );
1854
- if (!listRes.ok) {
1855
- const code = listRes.status >= 500 ? "SERVER_5XX" : "CANCEL_FAILED";
1856
- fail(code, listRes.error ?? `HTTP ${listRes.status}`);
1857
- }
1858
- const matches = (listRes.data?.reminders ?? []).filter((r) => r.reminderId.startsWith(opts.id));
1859
- if (matches.length === 0) {
1860
- fail("NOT_FOUND", `No scheduled reminder matches id prefix '${opts.id}'.`);
1861
- }
1862
- if (matches.length > 1) {
1863
- fail("AMBIGUOUS", `Ambiguous id prefix '${opts.id}' matches ${matches.length} reminders; pass a longer id.`);
1864
- }
1865
- fullId = matches[0].reminderId;
1866
- }
1908
+ const fullId = await resolveReminderId(client, ctx.agentId, opts.id, {
1909
+ statuses: ["scheduled", "fired"],
1910
+ failureCode: "CANCEL_FAILED"
1911
+ });
1867
1912
  const res = await client.request(
1868
1913
  "DELETE",
1869
1914
  `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders/${encodeURIComponent(fullId)}`
@@ -1876,6 +1921,124 @@ function registerReminderCancelCommand(parent) {
1876
1921
  });
1877
1922
  }
1878
1923
 
1924
+ // src/commands/reminder/_duration.ts
1925
+ function parseDurationSeconds(input) {
1926
+ const raw = input.trim();
1927
+ const match = /^(\d+)(s|m|h|d)?$/.exec(raw);
1928
+ if (!match) return null;
1929
+ const value = Number(match[1]);
1930
+ const unit = match[2] ?? "s";
1931
+ const multiplier = unit === "s" ? 1 : unit === "m" ? 60 : unit === "h" ? 3600 : 86400;
1932
+ const seconds = value * multiplier;
1933
+ if (!Number.isSafeInteger(seconds) || seconds <= 0) return null;
1934
+ return seconds;
1935
+ }
1936
+
1937
+ // src/commands/reminder/snooze.ts
1938
+ function registerReminderSnoozeCommand(parent) {
1939
+ parent.command("snooze").description("Snooze a scheduled or fired reminder").requiredOption("--id <id>", "Reminder id (full uuid or short prefix)").requiredOption("--by <duration>", "Snooze duration, e.g. 30m, 2h, 1d").action(async (opts) => {
1940
+ let ctx;
1941
+ try {
1942
+ ctx = loadAgentContext();
1943
+ } catch (err) {
1944
+ if (err instanceof AgentBootstrapError) fail(err.code, err.message);
1945
+ throw err;
1946
+ }
1947
+ const delaySeconds = parseDurationSeconds(opts.by);
1948
+ if (delaySeconds == null) {
1949
+ fail("INVALID_ARG", "--by must be a positive duration like 30m, 2h, or 1d");
1950
+ }
1951
+ const client = new ApiClient(ctx);
1952
+ const fullId = await resolveReminderId(client, ctx.agentId, opts.id, {
1953
+ statuses: ["scheduled", "fired"],
1954
+ failureCode: "SNOOZE_FAILED"
1955
+ });
1956
+ const res = await client.request(
1957
+ "POST",
1958
+ `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders/${encodeURIComponent(fullId)}/snooze`,
1959
+ { delaySeconds }
1960
+ );
1961
+ if (!res.ok || !res.data?.reminder) {
1962
+ const code = res.status >= 500 ? "SERVER_5XX" : "SNOOZE_FAILED";
1963
+ fail(code, res.error ?? `HTTP ${res.status}`);
1964
+ }
1965
+ process.stdout.write(formatReminderSnoozed(res.data.reminder) + "\n");
1966
+ });
1967
+ }
1968
+
1969
+ // src/commands/reminder/update.ts
1970
+ function registerReminderUpdateCommand(parent) {
1971
+ parent.command("update").description("Update one field on a scheduled reminder").requiredOption("--id <id>", "Reminder id (full uuid or short prefix)").option("--fire-at <iso>", "New absolute next fire time").option("--in <duration>", "New relative next fire time, e.g. 30m, 2h").option("--cadence <rule>", "New recurrence rule: every:15m | daily@09:00 | weekly:mon,fri@09:00").option("--title <text>", "New reminder title").action(async (opts) => {
1972
+ let ctx;
1973
+ try {
1974
+ ctx = loadAgentContext();
1975
+ } catch (err) {
1976
+ if (err instanceof AgentBootstrapError) fail(err.code, err.message);
1977
+ throw err;
1978
+ }
1979
+ const mutationCount = [opts.fireAt, opts.in, opts.cadence, opts.title].filter((x) => x !== void 0 && x !== null).length;
1980
+ if (mutationCount !== 1) {
1981
+ fail("INVALID_ARG", "Pass exactly one of --fire-at, --in, --cadence, or --title");
1982
+ }
1983
+ const body = {};
1984
+ if (opts.fireAt !== void 0) body.fireAt = opts.fireAt;
1985
+ if (opts.in !== void 0) {
1986
+ const delaySeconds = parseDurationSeconds(opts.in);
1987
+ if (delaySeconds == null) {
1988
+ fail("INVALID_ARG", "--in must be a positive duration like 30m, 2h, or 1d");
1989
+ }
1990
+ body.delaySeconds = delaySeconds;
1991
+ }
1992
+ if (opts.cadence !== void 0) {
1993
+ body.repeat = opts.cadence;
1994
+ body.tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
1995
+ }
1996
+ if (opts.title !== void 0) body.title = opts.title;
1997
+ const client = new ApiClient(ctx);
1998
+ const fullId = await resolveReminderId(client, ctx.agentId, opts.id, {
1999
+ all: true,
2000
+ failureCode: "UPDATE_FAILED"
2001
+ });
2002
+ const res = await client.request(
2003
+ "PATCH",
2004
+ `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders/${encodeURIComponent(fullId)}`,
2005
+ body
2006
+ );
2007
+ if (!res.ok || !res.data?.reminder) {
2008
+ const code = res.status >= 500 ? "SERVER_5XX" : "UPDATE_FAILED";
2009
+ fail(code, res.error ?? `HTTP ${res.status}`);
2010
+ }
2011
+ process.stdout.write(formatReminderUpdated(res.data.reminder, res.data.warning ?? null) + "\n");
2012
+ });
2013
+ }
2014
+
2015
+ // src/commands/reminder/log.ts
2016
+ function registerReminderLogCommand(parent) {
2017
+ parent.command("log").description("Show lifecycle events for one reminder").requiredOption("--id <id>", "Reminder id (full uuid or short prefix)").action(async (opts) => {
2018
+ let ctx;
2019
+ try {
2020
+ ctx = loadAgentContext();
2021
+ } catch (err) {
2022
+ if (err instanceof AgentBootstrapError) fail(err.code, err.message);
2023
+ throw err;
2024
+ }
2025
+ const client = new ApiClient(ctx);
2026
+ const fullId = await resolveReminderId(client, ctx.agentId, opts.id, {
2027
+ all: true,
2028
+ failureCode: "LOG_FAILED"
2029
+ });
2030
+ const res = await client.request(
2031
+ "GET",
2032
+ `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders/${encodeURIComponent(fullId)}/log`
2033
+ );
2034
+ if (!res.ok || !res.data?.events) {
2035
+ const code = res.status >= 500 ? "SERVER_5XX" : "LOG_FAILED";
2036
+ fail(code, res.error ?? `HTTP ${res.status}`);
2037
+ }
2038
+ process.stdout.write(formatReminderLog(res.data.events) + "\n");
2039
+ });
2040
+ }
2041
+
1879
2042
  // src/index.ts
1880
2043
  var program = new Command();
1881
2044
  program.name("slock").description(
@@ -1911,6 +2074,9 @@ var reminderCmd = program.command("reminder").description("Reminder operations")
1911
2074
  registerReminderScheduleCommand(reminderCmd);
1912
2075
  registerReminderListCommand(reminderCmd);
1913
2076
  registerReminderCancelCommand(reminderCmd);
2077
+ registerReminderSnoozeCommand(reminderCmd);
2078
+ registerReminderUpdateCommand(reminderCmd);
2079
+ registerReminderLogCommand(reminderCmd);
1914
2080
  program.parseAsync().catch((err) => {
1915
2081
  if (err instanceof CliExit) {
1916
2082
  process.exitCode = err.exitCode;
package/dist/core.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSlockCliPath,
10
10
  resolveWorkspaceDirectoryPath,
11
11
  scanWorkspaceDirectories
12
- } from "./chunk-37O7EHYE.js";
12
+ } from "./chunk-NM2MFLQ7.js";
13
13
  import {
14
14
  subscribeDaemonLogs
15
15
  } from "./chunk-JG7ONJZ6.js";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
5
  parseDaemonCliArgs
6
- } from "./chunk-37O7EHYE.js";
6
+ } from "./chunk-NM2MFLQ7.js";
7
7
  import "./chunk-JG7ONJZ6.js";
8
8
 
9
9
  // src/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.43.0",
3
+ "version": "0.44.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"