@slock-ai/daemon 0.13.0 → 0.14.0

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.
@@ -68,11 +68,13 @@ server.tool(
68
68
  ]
69
69
  };
70
70
  }
71
+ const shortId = data.messageId ? data.messageId.slice(0, 8) : null;
72
+ const replyHint = shortId ? ` (to reply in this message's thread, use target "${target.includes(":") ? target : target + ":" + shortId}")` : "";
71
73
  return {
72
74
  content: [
73
75
  {
74
76
  type: "text",
75
- text: `Message sent to ${target}`
77
+ text: `Message sent to ${target}. Message ID: ${data.messageId}${replyHint}`
76
78
  }
77
79
  ]
78
80
  };
@@ -107,9 +109,10 @@ server.tool(
107
109
  }
108
110
  const formatted = data.messages.map((m) => {
109
111
  const target = formatTarget(m);
110
- const senderPrefix = m.sender_type === "agent" ? "(agent) " : "";
111
- const time = m.timestamp ? ` (${toLocalTime(m.timestamp)})` : "";
112
- return `[${target}]${time} ${senderPrefix}@${m.sender_name}: ${m.content}`;
112
+ const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
113
+ const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
114
+ const senderType = m.sender_type === "agent" ? " type=agent" : "";
115
+ return `[target=${target} msg=${msgId} time=${time}${senderType}] @${m.sender_name}: ${m.content}`;
113
116
  }).join("\n");
114
117
  return {
115
118
  content: [{ type: "text", text: formatted }]
@@ -211,9 +214,10 @@ server.tool(
211
214
  };
212
215
  }
213
216
  const formatted = data.messages.map((m) => {
214
- const senderPrefix = m.senderType === "agent" ? "(agent) " : "";
215
- const time = m.createdAt ? ` (${toLocalTime(m.createdAt)})` : "";
216
- return `[seq:${m.seq}]${time} ${senderPrefix}@${m.senderName}: ${m.content}`;
217
+ const senderType = m.senderType === "agent" ? " type=agent" : "";
218
+ const time = m.createdAt ? toLocalTime(m.createdAt) : "-";
219
+ const msgId = m.id ? m.id.slice(0, 8) : "-";
220
+ return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}] @${m.senderName}: ${m.content}`;
217
221
  }).join("\n");
218
222
  let footer = "";
219
223
  if (data.historyLimited) {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import path3 from "path";
4
+ import path4 from "path";
5
5
  import os2 from "os";
6
6
  import { createRequire } from "module";
7
7
  import { execSync as execSync2 } from "child_process";
@@ -84,11 +84,13 @@ var DaemonConnection = class {
84
84
 
85
85
  // src/agentProcessManager.ts
86
86
  import { mkdir, writeFile, access, readdir, stat, readFile, rm } from "fs/promises";
87
- import path2 from "path";
87
+ import path3 from "path";
88
88
  import os from "os";
89
89
 
90
90
  // src/drivers/claude.ts
91
91
  import { spawn } from "child_process";
92
+ import { writeFileSync } from "fs";
93
+ import path from "path";
92
94
 
93
95
  // src/drivers/systemPrompt.ts
94
96
  function toolRef(prefix, name) {
@@ -143,14 +145,21 @@ ${opts.postStartupNotes.join("\n")}`;
143
145
 
144
146
  ## Messaging
145
147
 
146
- Messages you receive look like:
147
- - **Channel message**: \`[#general] @richard: hello everyone\`
148
- - **Channel message from an agent**: \`[#general] (agent) @Alice: hi there\`
149
- - **DM**: \`[dm:@richard] @richard: hey, can you help?\`
150
- - **Thread in a channel**: \`[#general:a1b2c3] @richard: replying in thread\`
151
- - **Thread in a DM**: \`[dm:@richard:x9y8z7] @richard: thread reply\`
148
+ Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
149
+
150
+ \`\`\`
151
+ [target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00] @richard: hello everyone
152
+ [target=#general msg=e5f6a7b8 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
153
+ [target=dm:@richard msg=c9d0e1f2 time=2026-03-15T01:00:02] @richard: hey, can you help?
154
+ [target=#general:a1b2c3d4 msg=f3a4b5c6 time=2026-03-15T01:00:03] @richard: thread reply
155
+ [target=dm:@richard:x9y8z7a0 msg=d7e8f9a0 time=2026-03-15T01:00:04] @richard: DM thread reply
156
+ \`\`\`
152
157
 
153
- The \`[...]\` prefix is the **target** \u2014 it identifies where the message came from. Reuse it exactly as the \`target\` parameter when replying.
158
+ Header fields:
159
+ - \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
160
+ - \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
161
+ - \`time=\` \u2014 timestamp.
162
+ - \`type=agent\` \u2014 present only if the sender is an agent.
154
163
 
155
164
  ### Sending messages
156
165
 
@@ -165,9 +174,11 @@ The \`[...]\` prefix is the **target** \u2014 it identifies where the message ca
165
174
 
166
175
  Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
167
176
 
168
- - **Thread targets** have a colon and short ID suffix: \`#general:a1b2c3\` (thread in #general) or \`dm:@richard:x9y8z7\` (thread in a DM).
177
+ - **Thread targets** have a colon and short ID suffix: \`#general:a1b2c3d4\` (thread in #general) or \`dm:@richard:x9y8z7a0\` (thread in a DM).
169
178
  - When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
170
- - You can read thread history: \`read_history(channel="#general:a1b2c3")\`
179
+ - **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=a1b2c3d4 ...]\`, reply with \`send_message(target="#general:a1b2c3d4", content="...")\`. The thread will be auto-created if it doesn't exist yet.
180
+ - When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
181
+ - You can read thread history: \`read_history(channel="#general:a1b2c3d4")\`
171
182
  - Threads cannot be nested \u2014 you cannot start a thread inside a thread.
172
183
 
173
184
  ### Discovering people and channels
@@ -360,6 +371,8 @@ var ClaudeDriver = class {
360
371
  }
361
372
  }
362
373
  });
374
+ const mcpConfigPath = path.join(ctx.workingDirectory, ".slock-claude-mcp.json");
375
+ writeFileSync(mcpConfigPath, mcpConfig, "utf8");
363
376
  const args2 = [
364
377
  "--allow-dangerously-skip-permissions",
365
378
  "--dangerously-skip-permissions",
@@ -369,7 +382,7 @@ var ClaudeDriver = class {
369
382
  "--input-format",
370
383
  "stream-json",
371
384
  "--mcp-config",
372
- mcpConfig,
385
+ mcpConfigPath,
373
386
  "--model",
374
387
  ctx.config.model || "sonnet"
375
388
  ];
@@ -506,13 +519,13 @@ var ClaudeDriver = class {
506
519
  // src/drivers/codex.ts
507
520
  import { spawn as spawn2, execSync } from "child_process";
508
521
  import { existsSync } from "fs";
509
- import path from "path";
522
+ import path2 from "path";
510
523
  var CodexDriver = class {
511
524
  id = "codex";
512
525
  supportsStdinNotification = false;
513
526
  mcpToolPrefix = "mcp_chat_";
514
527
  spawn(ctx) {
515
- const gitDir = path.join(ctx.workingDirectory, ".git");
528
+ const gitDir = path2.join(ctx.workingDirectory, ".git");
516
529
  if (!existsSync(gitDir)) {
517
530
  execSync("git init", { cwd: ctx.workingDirectory, stdio: "pipe" });
518
531
  execSync("git add -A && git commit --allow-empty -m 'init'", {
@@ -728,7 +741,7 @@ function getDriver(runtimeId) {
728
741
  }
729
742
 
730
743
  // src/agentProcessManager.ts
731
- var DATA_DIR = path2.join(os.homedir(), ".slock", "agents");
744
+ var DATA_DIR = path3.join(os.homedir(), ".slock", "agents");
732
745
  function toLocalTime(iso) {
733
746
  const d = new Date(iso);
734
747
  if (isNaN(d.getTime())) return iso;
@@ -753,9 +766,9 @@ var AgentProcessManager = class {
753
766
  this.agentsStarting.add(agentId);
754
767
  try {
755
768
  const driver = getDriver(config.runtime || "claude");
756
- const agentDataDir = path2.join(DATA_DIR, agentId);
769
+ const agentDataDir = path3.join(DATA_DIR, agentId);
757
770
  await mkdir(agentDataDir, { recursive: true });
758
- const memoryMdPath = path2.join(agentDataDir, "MEMORY.md");
771
+ const memoryMdPath = path3.join(agentDataDir, "MEMORY.md");
759
772
  try {
760
773
  await access(memoryMdPath);
761
774
  } catch {
@@ -773,7 +786,7 @@ ${config.description || "No role defined yet."}
773
786
  `;
774
787
  await writeFile(memoryMdPath, initialMemoryMd);
775
788
  }
776
- await mkdir(path2.join(agentDataDir, "notes"), { recursive: true });
789
+ await mkdir(path3.join(agentDataDir, "notes"), { recursive: true });
777
790
  const isResume = !!config.sessionId;
778
791
  let prompt;
779
792
  if (isResume && wakeMessage) {
@@ -953,7 +966,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
953
966
  }
954
967
  }
955
968
  async resetWorkspace(agentId) {
956
- const agentDataDir = path2.join(DATA_DIR, agentId);
969
+ const agentDataDir = path3.join(DATA_DIR, agentId);
957
970
  try {
958
971
  await rm(agentDataDir, { recursive: true, force: true });
959
972
  console.log(`[Agent ${agentId}] Workspace deleted: ${agentDataDir}`);
@@ -979,14 +992,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
979
992
  }
980
993
  for (const entry of entries) {
981
994
  if (!entry.isDirectory()) continue;
982
- const dirPath = path2.join(DATA_DIR, entry.name);
995
+ const dirPath = path3.join(DATA_DIR, entry.name);
983
996
  try {
984
997
  const dirContents = await readdir(dirPath, { withFileTypes: true });
985
998
  let totalSize = 0;
986
999
  let latestMtime = /* @__PURE__ */ new Date(0);
987
1000
  let fileCount = 0;
988
1001
  for (const item of dirContents) {
989
- const itemPath = path2.join(dirPath, item.name);
1002
+ const itemPath = path3.join(dirPath, item.name);
990
1003
  try {
991
1004
  const info = await stat(itemPath);
992
1005
  if (item.isFile()) {
@@ -1016,7 +1029,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1016
1029
  if (directoryName.includes("/") || directoryName.includes("..") || directoryName.includes("\\")) {
1017
1030
  return false;
1018
1031
  }
1019
- const targetDir = path2.join(DATA_DIR, directoryName);
1032
+ const targetDir = path3.join(DATA_DIR, directoryName);
1020
1033
  try {
1021
1034
  await rm(targetDir, { recursive: true, force: true });
1022
1035
  console.log(`[Workspace] Deleted directory: ${targetDir}`);
@@ -1028,7 +1041,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1028
1041
  }
1029
1042
  // Workspace file browsing
1030
1043
  async getFileTree(agentId, dirPath) {
1031
- const agentDir = path2.join(DATA_DIR, agentId);
1044
+ const agentDir = path3.join(DATA_DIR, agentId);
1032
1045
  try {
1033
1046
  await stat(agentDir);
1034
1047
  } catch {
@@ -1036,8 +1049,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1036
1049
  }
1037
1050
  let targetDir = agentDir;
1038
1051
  if (dirPath) {
1039
- const resolved = path2.resolve(agentDir, dirPath);
1040
- if (!resolved.startsWith(agentDir + path2.sep) && resolved !== agentDir) {
1052
+ const resolved = path3.resolve(agentDir, dirPath);
1053
+ if (!resolved.startsWith(agentDir + path3.sep) && resolved !== agentDir) {
1041
1054
  return [];
1042
1055
  }
1043
1056
  targetDir = resolved;
@@ -1045,9 +1058,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1045
1058
  return this.listDirectoryChildren(targetDir, agentDir);
1046
1059
  }
1047
1060
  async readFile(agentId, filePath) {
1048
- const agentDir = path2.join(DATA_DIR, agentId);
1049
- const resolved = path2.resolve(agentDir, filePath);
1050
- if (!resolved.startsWith(agentDir + path2.sep) && resolved !== agentDir) {
1061
+ const agentDir = path3.join(DATA_DIR, agentId);
1062
+ const resolved = path3.resolve(agentDir, filePath);
1063
+ if (!resolved.startsWith(agentDir + path3.sep) && resolved !== agentDir) {
1051
1064
  throw new Error("Access denied");
1052
1065
  }
1053
1066
  const info = await stat(resolved);
@@ -1071,7 +1084,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1071
1084
  ".sh",
1072
1085
  ".py"
1073
1086
  ]);
1074
- const ext = path2.extname(resolved).toLowerCase();
1087
+ const ext = path3.extname(resolved).toLowerCase();
1075
1088
  if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
1076
1089
  return { content: null, binary: true };
1077
1090
  }
@@ -1110,7 +1123,10 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1110
1123
  const inputSummary = driver.summarizeToolInput(toolName, event.input);
1111
1124
  trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
1112
1125
  if (toolName === `${driver.mcpToolPrefix}receive_message`) {
1113
- activity = "online";
1126
+ const isBlocking = event.input?.block !== false;
1127
+ if (isBlocking) {
1128
+ activity = "online";
1129
+ }
1114
1130
  if (ap) {
1115
1131
  ap.isInReceiveMessage = true;
1116
1132
  ap.pendingNotificationCount = 0;
@@ -1185,8 +1201,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1185
1201
  const nodes = [];
1186
1202
  for (const entry of entries) {
1187
1203
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
1188
- const fullPath = path2.join(dir, entry.name);
1189
- const relativePath = path2.relative(rootDir, fullPath);
1204
+ const fullPath = path3.join(dir, entry.name);
1205
+ const relativePath = path3.relative(rootDir, fullPath);
1190
1206
  let info;
1191
1207
  try {
1192
1208
  info = await stat(fullPath);
@@ -1281,12 +1297,12 @@ if (!serverUrl || !apiKey) {
1281
1297
  console.error("Usage: slock-daemon --server-url <url> --api-key <key>");
1282
1298
  process.exit(1);
1283
1299
  }
1284
- var __dirname = path3.dirname(fileURLToPath(import.meta.url));
1285
- var chatBridgePath = path3.resolve(__dirname, "chat-bridge.js");
1300
+ var __dirname = path4.dirname(fileURLToPath(import.meta.url));
1301
+ var chatBridgePath = path4.resolve(__dirname, "chat-bridge.js");
1286
1302
  try {
1287
1303
  accessSync(chatBridgePath);
1288
1304
  } catch {
1289
- chatBridgePath = path3.resolve(__dirname, "chat-bridge.ts");
1305
+ chatBridgePath = path4.resolve(__dirname, "chat-bridge.ts");
1290
1306
  }
1291
1307
  var connection;
1292
1308
  var agentManager = new AgentProcessManager(chatBridgePath, (msg) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"