@slock-ai/daemon 0.39.1-alpha.1 → 0.39.1-alpha.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.
@@ -475,6 +475,7 @@ var DISPLAY_PLAN_CONFIG = {
475
475
  };
476
476
 
477
477
  // src/agentProcessManager.ts
478
+ import { writeFileSync as writeFileSync6, renameSync, rmSync } from "fs";
478
479
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
479
480
  import path10 from "path";
480
481
  import os3 from "os";
@@ -942,6 +943,7 @@ function prepareCliTransport(ctx, extraEnv = {}, platform = process.platform) {
942
943
  mkdirSync(slockDir, { recursive: true });
943
944
  const tokenFile = path.join(slockDir, "agent-token");
944
945
  writeFileSync(tokenFile, ctx.config.authToken || ctx.daemonApiKey, { mode: 384 });
946
+ const chatContextFile = path.join(ctx.workingDirectory, "chat-context.json");
945
947
  const posixWrapper = path.join(slockDir, "slock");
946
948
  const posixBody = `#!/usr/bin/env bash
947
949
  exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)} "$@"
@@ -963,11 +965,13 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(ctx.slockCliPath)}
963
965
  SLOCK_AGENT_ID: ctx.agentId,
964
966
  SLOCK_SERVER_URL: ctx.config.serverUrl,
965
967
  SLOCK_AGENT_TOKEN_FILE: tokenFile,
968
+ SLOCK_CHAT_CONTEXT_FILE: chatContextFile,
966
969
  PATH: `${slockDir}${path.delimiter}${process.env.PATH ?? ""}`
967
970
  };
968
971
  delete spawnEnv.SLOCK_AGENT_TOKEN;
969
972
  return {
970
973
  slockDir,
974
+ chatContextFile,
971
975
  tokenFile,
972
976
  wrapperPath,
973
977
  spawnEnv
@@ -2420,6 +2424,29 @@ function formatMessageTarget(message) {
2420
2424
  function getMessageShortId(messageId) {
2421
2425
  return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
2422
2426
  }
2427
+ function writeAgentChatContextFile(agentDataDir, message) {
2428
+ const target = formatMessageTarget(message);
2429
+ const msgId = message.message_id ?? null;
2430
+ const filePath = path10.join(agentDataDir, "chat-context.json");
2431
+ const tmpPath = `${filePath}.tmp.${process.pid}.${Date.now()}`;
2432
+ const json = JSON.stringify({
2433
+ target,
2434
+ msgId,
2435
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
2436
+ });
2437
+ try {
2438
+ writeFileSync6(tmpPath, json, { encoding: "utf-8", mode: 384 });
2439
+ renameSync(tmpPath, filePath);
2440
+ } catch (error) {
2441
+ try {
2442
+ rmSync(tmpPath, { force: true });
2443
+ } catch {
2444
+ }
2445
+ logger.warn(
2446
+ `[AgentChatContext] Failed to persist current chat context for agent dir ${agentDataDir}: ${error instanceof Error ? error.message : String(error)}`
2447
+ );
2448
+ }
2449
+ }
2423
2450
  function formatSenderHandle(message) {
2424
2451
  return message.sender_description ? `@${message.sender_name} \u2014 ${message.sender_description}` : `@${message.sender_name}`;
2425
2452
  }
@@ -2655,6 +2682,9 @@ Use read_history to catch up on the channels listed above, then stop. Read each
2655
2682
  prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : standingPrompt;
2656
2683
  }
2657
2684
  const effectiveConfig = await this.buildSpawnConfig(agentId, config);
2685
+ if (wakeMessage) {
2686
+ writeAgentChatContextFile(agentDataDir, wakeMessage);
2687
+ }
2658
2688
  const { process: proc } = driver.spawn({
2659
2689
  agentId,
2660
2690
  config: effectiveConfig,
@@ -3320,6 +3350,9 @@ Use read_history to catch up on the channels listed above, then stop. Read each
3320
3350
  /** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
3321
3351
  deliverMessagesViaStdin(agentId, ap, messages, mode) {
3322
3352
  if (messages.length === 0) return true;
3353
+ const latestMessage = messages[messages.length - 1];
3354
+ const agentDataDir = path10.join(this.dataDir, agentId);
3355
+ writeAgentChatContextFile(agentDataDir, latestMessage);
3323
3356
  const prompt = messages.length === 1 ? `New message received:
3324
3357
 
3325
3358
  ${formatIncomingMessage(messages[0])}
package/dist/cli/index.js CHANGED
@@ -295,6 +295,59 @@ function registerServerInfoCommand(parent) {
295
295
  });
296
296
  }
297
297
 
298
+ // src/chatContext.ts
299
+ import fs2 from "fs";
300
+ import os from "os";
301
+ import path from "path";
302
+ var EMPTY = { target: null, msgId: null };
303
+ function resolveChatContextPath(agentId, env = process.env) {
304
+ const override = env.SLOCK_CHAT_CONTEXT_FILE;
305
+ if (override && override.length > 0) return override;
306
+ return path.join(os.homedir(), ".slock", "agents", agentId, "chat-context.json");
307
+ }
308
+ function readChatContext(agentId, env = process.env) {
309
+ const filePath = resolveChatContextPath(agentId, env);
310
+ let raw;
311
+ try {
312
+ raw = fs2.readFileSync(filePath, "utf-8");
313
+ } catch {
314
+ return EMPTY;
315
+ }
316
+ try {
317
+ const parsed = JSON.parse(raw);
318
+ if (!parsed || typeof parsed !== "object") return EMPTY;
319
+ const target = typeof parsed.target === "string" && parsed.target.length > 0 ? parsed.target : null;
320
+ const msgId = typeof parsed.msgId === "string" && parsed.msgId.length > 0 ? parsed.msgId : null;
321
+ return { target, msgId };
322
+ } catch {
323
+ return EMPTY;
324
+ }
325
+ }
326
+ function writeChatContext(agentId, ctx, env = process.env) {
327
+ const filePath = resolveChatContextPath(agentId, env);
328
+ const current = readChatContext(agentId, env);
329
+ const next = {
330
+ target: ctx.target !== void 0 ? ctx.target : current.target,
331
+ msgId: ctx.msgId !== void 0 ? ctx.msgId : current.msgId
332
+ };
333
+ const dir = path.dirname(filePath);
334
+ try {
335
+ fs2.mkdirSync(dir, { recursive: true });
336
+ } catch {
337
+ }
338
+ const tmp = `${filePath}.tmp.${process.pid}.${Date.now()}`;
339
+ const json = JSON.stringify({ ...next, updatedAt: (/* @__PURE__ */ new Date()).toISOString() });
340
+ try {
341
+ fs2.writeFileSync(tmp, json, { encoding: "utf-8", mode: 384 });
342
+ fs2.renameSync(tmp, filePath);
343
+ } catch {
344
+ try {
345
+ fs2.unlinkSync(tmp);
346
+ } catch {
347
+ }
348
+ }
349
+ }
350
+
298
351
  // src/commands/message/_format.ts
299
352
  function toLocalTime(iso) {
300
353
  const d = new Date(iso);
@@ -527,6 +580,10 @@ function registerSendCommand(parent) {
527
580
  fail(code, res.error ?? `HTTP ${res.status}`);
528
581
  }
529
582
  const data = res.data;
583
+ writeChatContext(ctx.agentId, {
584
+ target: opts.target,
585
+ msgId: typeof data.messageId === "string" ? data.messageId : null
586
+ });
530
587
  const shortId = data.messageId ? data.messageId.slice(0, 8) : null;
531
588
  const replyHint = shortId ? ` (to reply in this message's thread, use target "${opts.target.includes(":") ? opts.target : opts.target + ":" + shortId}")` : "";
532
589
  let unreadSection = "";
@@ -549,8 +606,8 @@ async function drainInbox(ctx, opts) {
549
606
  const query = [];
550
607
  if (opts.block) query.push("block=true");
551
608
  if (opts.block && opts.timeoutMs !== void 0) query.push(`timeout=${opts.timeoutMs}`);
552
- const path = query.length > 0 ? `${agentPath}/receive?${query.join("&")}` : `${agentPath}/receive`;
553
- const res = await client.request("GET", path);
609
+ const path2 = query.length > 0 ? `${agentPath}/receive?${query.join("&")}` : `${agentPath}/receive`;
610
+ const res = await client.request("GET", path2);
554
611
  if (!res.ok) {
555
612
  const code = res.status >= 500 ? "SERVER_5XX" : failCode;
556
613
  fail(code, res.error ?? `HTTP ${res.status}`);
@@ -576,6 +633,13 @@ function registerCheckCommand(parent) {
576
633
  throw err;
577
634
  }
578
635
  const result = await drainInbox(ctx, { block: false });
636
+ if (result.messages.length > 0) {
637
+ const latest = result.messages[result.messages.length - 1];
638
+ const target = formatTarget(latest);
639
+ const rawMsgId = latest.message_id;
640
+ const msgId = typeof rawMsgId === "string" ? rawMsgId : null;
641
+ writeChatContext(ctx.agentId, { target, msgId });
642
+ }
579
643
  process.stdout.write(formatMessages(result.messages) + "\n");
580
644
  });
581
645
  }
@@ -1008,6 +1072,48 @@ function formatReminderCanceled(r) {
1008
1072
  }
1009
1073
 
1010
1074
  // src/commands/reminder/schedule.ts
1075
+ function buildScheduleBody(opts, cached, now = () => Intl.DateTimeFormat().resolvedOptions().timeZone) {
1076
+ if (!opts.delaySeconds && !opts.fireAt && !opts.repeat) {
1077
+ return {
1078
+ body: {},
1079
+ error: { code: "INVALID_ARG", message: "Provide --delay-seconds, --fire-at, or --repeat" }
1080
+ };
1081
+ }
1082
+ if (opts.delaySeconds && opts.fireAt) {
1083
+ return {
1084
+ body: {},
1085
+ error: {
1086
+ code: "INVALID_ARG",
1087
+ message: "Pass either --delay-seconds or --fire-at, not both"
1088
+ }
1089
+ };
1090
+ }
1091
+ const body = { title: opts.title, msgId: opts.msgId ?? null };
1092
+ if (opts.delaySeconds !== void 0) {
1093
+ const n = Number(opts.delaySeconds);
1094
+ if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
1095
+ return {
1096
+ body: {},
1097
+ error: {
1098
+ code: "INVALID_ARG",
1099
+ message: `--delay-seconds must be a positive integer; got ${opts.delaySeconds}`
1100
+ }
1101
+ };
1102
+ }
1103
+ body.delaySeconds = n;
1104
+ }
1105
+ if (opts.fireAt !== void 0) body.fireAt = opts.fireAt;
1106
+ if (opts.repeat !== void 0) {
1107
+ body.repeat = opts.repeat;
1108
+ body.tz = now();
1109
+ }
1110
+ if (opts.channel !== void 0) body.channel = opts.channel;
1111
+ if (opts.channel === void 0 && opts.msgId === void 0) {
1112
+ if (cached.target) body.channel = cached.target;
1113
+ if (cached.msgId) body.msgId = cached.msgId;
1114
+ }
1115
+ return { body };
1116
+ }
1011
1117
  function registerReminderScheduleCommand(parent) {
1012
1118
  parent.command("schedule").description("Schedule a reminder that fires at a future time").requiredOption("--title <t>", "Short description of what the reminder is about").option(
1013
1119
  "--delay-seconds <n>",
@@ -1020,7 +1126,7 @@ function registerReminderScheduleCommand(parent) {
1020
1126
  "Recurrence rule: every:15m | every:2h | every:1d | daily@09:00 | weekly:mon,fri@09:00"
1021
1127
  ).option(
1022
1128
  "--channel <ref>",
1023
- "Optional channel to post a receipt message in (e.g. #general, dm:@alice). Without this, receipt is only posted when --msg-id is given."
1129
+ "Optional channel to post a receipt message in (e.g. #general, dm:@alice). Without --channel or --msg-id, receipt inherits the agent's current chat context (last message received/sent)."
1024
1130
  ).option("--msg-id <id>", "Optional message id this reminder is anchored to").action(async (opts) => {
1025
1131
  let ctx;
1026
1132
  try {
@@ -1029,37 +1135,22 @@ function registerReminderScheduleCommand(parent) {
1029
1135
  if (err instanceof AgentBootstrapError) fail(err.code, err.message);
1030
1136
  throw err;
1031
1137
  }
1032
- if (!opts.delaySeconds && !opts.fireAt && !opts.repeat) {
1033
- fail("INVALID_ARG", "Provide --delay-seconds, --fire-at, or --repeat");
1034
- }
1035
- if (opts.delaySeconds && opts.fireAt) {
1036
- fail("INVALID_ARG", "Pass either --delay-seconds or --fire-at, not both");
1037
- }
1038
- const body = { title: opts.title, msgId: opts.msgId ?? null };
1039
- if (opts.delaySeconds !== void 0) {
1040
- const n = Number(opts.delaySeconds);
1041
- if (!Number.isFinite(n) || !Number.isInteger(n) || n <= 0) {
1042
- fail("INVALID_ARG", `--delay-seconds must be a positive integer; got ${opts.delaySeconds}`);
1043
- }
1044
- body.delaySeconds = n;
1045
- }
1046
- if (opts.fireAt !== void 0) body.fireAt = opts.fireAt;
1047
- if (opts.repeat !== void 0) {
1048
- body.repeat = opts.repeat;
1049
- body.tz = Intl.DateTimeFormat().resolvedOptions().timeZone;
1050
- }
1051
- if (opts.channel !== void 0) body.channel = opts.channel;
1138
+ const cached = readChatContext(ctx.agentId);
1139
+ const built = buildScheduleBody(opts, cached);
1140
+ if (built.error) fail(built.error.code, built.error.message);
1052
1141
  const client = new ApiClient(ctx);
1053
1142
  const res = await client.request(
1054
1143
  "POST",
1055
1144
  `/internal/agent/${encodeURIComponent(ctx.agentId)}/reminders`,
1056
- body
1145
+ built.body
1057
1146
  );
1058
1147
  if (!res.ok || !res.data?.reminder) {
1059
1148
  const code = res.status >= 500 ? "SERVER_5XX" : "SCHEDULE_FAILED";
1060
1149
  fail(code, res.error ?? `HTTP ${res.status}`);
1061
1150
  }
1062
- process.stdout.write(formatReminderScheduled(res.data.reminder, res.data.warning ?? null) + "\n");
1151
+ process.stdout.write(
1152
+ formatReminderScheduled(res.data.reminder, res.data.warning ?? null) + "\n"
1153
+ );
1063
1154
  });
1064
1155
  }
1065
1156
 
package/dist/core.js CHANGED
@@ -9,7 +9,7 @@ import {
9
9
  resolveSlockCliPath,
10
10
  resolveWorkspaceDirectoryPath,
11
11
  scanWorkspaceDirectories
12
- } from "./chunk-7JPDXVD5.js";
12
+ } from "./chunk-D6DQHMCD.js";
13
13
  import {
14
14
  subscribeDaemonLogs
15
15
  } from "./chunk-E6OOH3IC.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-7JPDXVD5.js";
6
+ } from "./chunk-D6DQHMCD.js";
7
7
  import "./chunk-E6OOH3IC.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.39.1-alpha.1",
3
+ "version": "0.39.1-alpha.2",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"