@slock-ai/daemon 0.23.0 → 0.25.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.
@@ -229,58 +229,26 @@ Use your Read tool to view this image.` }]
229
229
  }
230
230
  );
231
231
  server.tool(
232
- "receive_message",
233
- "Receive new messages. Blocks and waits for new messages (polls internally). Returns messages formatted as [#channel], [dm:@peer], or [thread:#channel:id] followed by the sender and content.",
234
- {
235
- block: z.boolean().default(true).describe("Whether to block (wait) for new messages")
236
- },
237
- async ({ block }) => {
232
+ "check_messages",
233
+ "Check for new messages without waiting. Returns immediately with any pending messages, or 'No new messages' if none. Use this freely during work \u2014 at natural breakpoints, after notifications, or whenever you want to see if anything new came in.",
234
+ {},
235
+ async () => {
238
236
  try {
239
- const POLL_INTERVAL_MS = 25e3;
240
- const MAX_WAIT_MS = 27e4;
241
- const startTime = Date.now();
242
- while (true) {
243
- const elapsed = Date.now() - startTime;
244
- const remaining = MAX_WAIT_MS - elapsed;
245
- if (!block || remaining <= 0) {
246
- const params2 = new URLSearchParams();
247
- if (block) {
248
- params2.set("block", "true");
249
- params2.set("timeout", "1000");
250
- }
251
- const res2 = await fetch(
252
- `${serverUrl}/internal/agent/${agentId}/receive?${params2}`,
253
- { method: "GET", headers: commonHeaders }
254
- );
255
- if (!res2.ok) {
256
- const errData = await res2.json().catch(() => ({}));
257
- return { content: [{ type: "text", text: `Error: ${errData.error || res2.statusText}` }] };
258
- }
259
- const data2 = await res2.json();
260
- if (data2.messages?.length > 0) {
261
- return { content: [{ type: "text", text: formatMessages(data2.messages) }] };
262
- }
263
- return {
264
- content: [{ type: "text", text: "No new messages. Take a rest \u2014 new messages will be delivered to you directly. Do not call receive_message or take any further action until notified." }]
265
- };
266
- }
267
- const thisPoll = Math.min(POLL_INTERVAL_MS, remaining);
268
- const params = new URLSearchParams();
269
- params.set("block", "true");
270
- params.set("timeout", String(thisPoll));
271
- const res = await fetch(
272
- `${serverUrl}/internal/agent/${agentId}/receive?${params}`,
273
- { method: "GET", headers: commonHeaders }
274
- );
275
- if (!res.ok) {
276
- const errData = await res.json().catch(() => ({}));
277
- return { content: [{ type: "text", text: `Error: ${errData.error || res.statusText}` }] };
278
- }
279
- const data = await res.json();
280
- if (data.messages?.length > 0) {
281
- return { content: [{ type: "text", text: formatMessages(data.messages) }] };
282
- }
237
+ const res = await fetch(
238
+ `${serverUrl}/internal/agent/${agentId}/receive`,
239
+ { method: "GET", headers: commonHeaders }
240
+ );
241
+ if (!res.ok) {
242
+ const errData = await res.json().catch(() => ({}));
243
+ return { content: [{ type: "text", text: `Error: ${errData.error || res.statusText}` }] };
283
244
  }
245
+ const data = await res.json();
246
+ if (data.messages?.length > 0) {
247
+ return { content: [{ type: "text", text: formatMessages(data.messages) }] };
248
+ }
249
+ return {
250
+ content: [{ type: "text", text: "No new messages." }]
251
+ };
284
252
  } catch (err) {
285
253
  return {
286
254
  content: [{ type: "text", text: `Error: ${err.message}` }]
package/dist/index.js CHANGED
@@ -106,9 +106,9 @@ function buildBaseSystemPrompt(config, opts) {
106
106
  const startupSteps = [
107
107
  `1. **Read MEMORY.md** (in your cwd). This is your memory index \u2014 it tells you what you know and where to find it.`,
108
108
  `2. Follow the instructions in MEMORY.md to read any other memory files you need (e.g. channel summaries, role definitions, user preferences).`,
109
- `3. Call ${t("receive_message")}(block=true) to start listening.`,
109
+ `3. Stop and wait. New messages will be delivered to you automatically via stdin.`,
110
110
  `4. When you receive a message, process it and reply with ${t("send_message")}.`,
111
- `5. After replying, call ${t("receive_message")}(block=true) again to keep listening.`
111
+ `5. **Complete ALL your work before stopping.** If a task requires multi-step work (research, code changes, testing), finish everything, report results, then stop. New messages arrive automatically \u2014 you do not need to poll or wait for them.`
112
112
  ];
113
113
  let prompt = `You are "${config.displayName || config.name}", an AI agent in Slock \u2014 a collaborative platform for human-AI collaboration.
114
114
 
@@ -120,7 +120,7 @@ You are a **long-running, persistent agent**. You are NOT a one-shot assistant \
120
120
 
121
121
  You have MCP tools from the "chat" server. Use ONLY these for communication:
122
122
 
123
- 1. **${t("receive_message")}** \u2014 Call with block=true to wait for messages. This is your main loop.
123
+ 1. **${t("check_messages")}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
124
124
  2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
125
125
  3. **${t("list_server")}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
126
126
  4. **${t("read_history")}** \u2014 Read past messages from a channel or DM.
@@ -130,6 +130,7 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
130
130
  8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
131
131
  9. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
132
132
  10. **${t("upload_file")}** \u2014 Upload an image file to attach to a message. Returns an attachment ID to pass to send_message.
133
+ 11. **${t("view_file")}** \u2014 Download an attached image by its attachment ID so you can view it. Use when messages contain image attachments.
133
134
 
134
135
  CRITICAL RULES:
135
136
  ${criticalRules.join("\n")}
@@ -333,13 +334,12 @@ You may develop a specialized role over time through your interactions. Embrace
333
334
 
334
335
  While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens, you will receive a system notification like:
335
336
 
336
- \`[System notification: You have N new message(s) waiting. Call receive_message to read them when you're ready.]\`
337
+ \`[System notification: You have N new message(s) waiting. Call check_messages to read them when you're ready.]\`
337
338
 
338
339
  How to handle these:
339
- - **Do NOT interrupt your current work.** Finish what you're doing first.
340
- - After completing your current step, call \`${t("receive_message")}(block=false)\` to check for messages.
341
- - Do not ignore notifications for too long \u2014 acknowledge new messages in a timely manner.
342
- - These notifications are batched (you won't get one per message), so the count tells you how many are waiting.`;
340
+ - Call \`${t("check_messages")}()\` to check for new messages. You are encouraged to do this frequently \u2014 at natural breakpoints in your work, or whenever you see a notification.
341
+ - If the new message is higher priority, you may pivot to it. If not, continue your current work.
342
+ - \`check_messages\` returns instantly with any pending messages (or "no new messages"). It is always safe to call.`;
343
343
  }
344
344
  if (config.description) {
345
345
  prompt += `
@@ -485,6 +485,7 @@ var ClaudeDriver = class {
485
485
  if (name === "mcp__chat__update_task_status") return "Updating task status\u2026";
486
486
  if (name === "mcp__chat__list_server") return "Listing server\u2026";
487
487
  if (name === "mcp__chat__read_history") return "Reading history\u2026";
488
+ if (name === "mcp__chat__check_messages") return "Checking messages\u2026";
488
489
  if (name.startsWith("mcp__chat__")) return "";
489
490
  if (name === "Read" || name === "read_file") return "Reading file\u2026";
490
491
  if (name === "Write" || name === "write_file") return "Writing file\u2026";
@@ -716,11 +717,10 @@ var CodexDriver = class {
716
717
  return buildBaseSystemPrompt(config, {
717
718
  toolPrefix: "",
718
719
  extraCriticalRules: [
719
- "- Do NOT use shell commands to send or receive messages. The MCP tools handle everything.",
720
- "- ALWAYS call receive_message(block=true) after completing any task \u2014 this keeps you listening for new messages."
720
+ "- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
721
721
  ],
722
722
  postStartupNotes: [
723
- "**IMPORTANT**: Your process exits after each turn completes. You will be automatically restarted when new messages arrive. Always call receive_message(block=true) as your last action \u2014 if no messages are pending, you'll sleep and be woken when one arrives."
723
+ "**IMPORTANT**: Your process exits after each turn completes. You will be automatically restarted when new messages arrive. Complete all your work, then stop \u2014 new messages will wake you up."
724
724
  ],
725
725
  includeStdinNotificationSection: false
726
726
  });
@@ -735,6 +735,7 @@ var CodexDriver = class {
735
735
  if (name === `${this.mcpToolPrefix}update_task_status`) return "Updating task status\u2026";
736
736
  if (name === `${this.mcpToolPrefix}list_server`) return "Listing server\u2026";
737
737
  if (name === `${this.mcpToolPrefix}read_history`) return "Reading history\u2026";
738
+ if (name === `${this.mcpToolPrefix}check_messages`) return "Checking messages\u2026";
738
739
  if (name.startsWith(this.mcpToolPrefix)) return "";
739
740
  if (name === "shell" || name === "command_execution") return "Running command\u2026";
740
741
  if (name === "file_change") return "Editing file\u2026";
@@ -801,6 +802,7 @@ function toLocalTime(iso) {
801
802
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
802
803
  }
803
804
  var MAX_TRAJECTORY_TEXT = 2e3;
805
+ var ACTIVITY_HEARTBEAT_MS = 6e4;
804
806
  var AgentProcessManager = class {
805
807
  agents = /* @__PURE__ */ new Map();
806
808
  agentsStarting = /* @__PURE__ */ new Set();
@@ -868,11 +870,13 @@ Use read_history to catch up, or respond to the message above first.`;
868
870
  }
869
871
  prompt += `
870
872
 
871
- Respond as appropriate \u2014 reply using send_message, or take action as needed. Then call receive_message(block=true) to keep listening.`;
873
+ Respond as appropriate \u2014 reply using send_message, or take action as needed. Complete ALL your work before stopping.
874
+
875
+ IMPORTANT: If the message requires multi-step work (e.g. research, code changes, testing), complete ALL steps before stopping. Sending a progress update does NOT mean your task is done \u2014 only stop when you have NO more work to do. New messages will be delivered to you automatically via stdin.`;
872
876
  if (driver.supportsStdinNotification) {
873
877
  prompt += `
874
878
 
875
- Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call receive_message to check.`;
879
+ Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
876
880
  }
877
881
  } else if (isResume && unreadSummary && Object.keys(unreadSummary).length > 0) {
878
882
  prompt = `You have unread messages from while you were offline:`;
@@ -882,18 +886,18 @@ Note: While you are busy, you may receive [System notification: ...] messages. F
882
886
  }
883
887
  prompt += `
884
888
 
885
- Use read_history to catch up on important channels, then call receive_message(block=true) to listen for new messages.`;
889
+ Use read_history to catch up on important channels, then stop. New messages will be delivered to you automatically.`;
886
890
  if (driver.supportsStdinNotification) {
887
891
  prompt += `
888
892
 
889
- Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call receive_message to check.`;
893
+ Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
890
894
  }
891
895
  } else if (isResume) {
892
- prompt = `No new messages while you were away. Call ${driver.mcpToolPrefix}receive_message(block=true) to listen for new messages.`;
896
+ prompt = `No new messages while you were away. Nothing to do \u2014 just stop. New messages will be delivered to you automatically via stdin.`;
893
897
  if (driver.supportsStdinNotification) {
894
898
  prompt += `
895
899
 
896
- Note: While you are busy, you may receive [System notification: ...] messages about new messages. Finish your current step, then call receive_message to check.`;
900
+ Note: While you are busy, you may receive [System notification: ...] messages about new messages. Finish your current step, then call check_messages to check for messages.`;
897
901
  }
898
902
  } else {
899
903
  prompt = driver.buildSystemPrompt(config, agentId);
@@ -910,12 +914,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
910
914
  process: proc,
911
915
  driver,
912
916
  inbox: [],
913
- pendingReceive: null,
914
917
  config,
915
918
  sessionId: config.sessionId || null,
916
- isInReceiveMessage: false,
919
+ isIdle: false,
917
920
  notificationTimer: null,
918
- pendingNotificationCount: 0
921
+ pendingNotificationCount: 0,
922
+ activityHeartbeat: null,
923
+ lastActivity: "",
924
+ lastActivityDetail: ""
919
925
  };
920
926
  this.agents.set(agentId, agentProcess);
921
927
  this.agentsStarting.delete(agentId);
@@ -943,31 +949,30 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
943
949
  if (this.agents.has(agentId)) {
944
950
  const ap = this.agents.get(agentId);
945
951
  if (ap.process !== proc) return;
946
- if (ap.pendingReceive) {
947
- clearTimeout(ap.pendingReceive.timer);
948
- ap.pendingReceive.resolve([]);
949
- }
950
952
  if (ap.notificationTimer) {
951
953
  clearTimeout(ap.notificationTimer);
952
954
  }
955
+ if (ap.activityHeartbeat) {
956
+ clearInterval(ap.activityHeartbeat);
957
+ }
953
958
  this.agents.delete(agentId);
954
959
  if (code === 0) {
955
960
  this.idleAgentConfigs.set(agentId, {
956
961
  config: { ...ap.config, sessionId: ap.sessionId },
957
962
  sessionId: ap.sessionId
958
963
  });
959
- this.sendToServer({ type: "agent:activity", agentId, activity: "online", detail: "" });
964
+ this.broadcastActivity(agentId, "online", "Process idle");
960
965
  } else {
961
966
  this.idleAgentConfigs.delete(agentId);
962
967
  const reason = code === null ? "killed by signal" : `exit code ${code}`;
963
968
  console.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
964
969
  this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
965
- this.sendToServer({ type: "agent:activity", agentId, activity: "offline", detail: `Crashed (${reason})` });
970
+ this.broadcastActivity(agentId, "offline", `Crashed (${reason})`);
966
971
  }
967
972
  }
968
973
  });
969
974
  this.sendToServer({ type: "agent:status", agentId, status: "active" });
970
- this.sendToServer({ type: "agent:activity", agentId, activity: "working", detail: "Starting\u2026" });
975
+ this.broadcastActivity(agentId, "working", "Starting\u2026");
971
976
  } catch (err) {
972
977
  this.agentsStarting.delete(agentId);
973
978
  throw err;
@@ -977,18 +982,17 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
977
982
  this.idleAgentConfigs.delete(agentId);
978
983
  const ap = this.agents.get(agentId);
979
984
  if (!ap) return;
980
- if (ap.pendingReceive) {
981
- clearTimeout(ap.pendingReceive.timer);
982
- ap.pendingReceive.resolve([]);
983
- }
984
985
  if (ap.notificationTimer) {
985
986
  clearTimeout(ap.notificationTimer);
986
987
  }
988
+ if (ap.activityHeartbeat) {
989
+ clearInterval(ap.activityHeartbeat);
990
+ }
987
991
  this.agents.delete(agentId);
988
992
  ap.process.kill("SIGTERM");
989
993
  if (!silent) {
990
994
  this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
991
- this.sendToServer({ type: "agent:activity", agentId, activity: "offline", detail: "" });
995
+ this.broadcastActivity(agentId, "offline", "Stopped");
992
996
  }
993
997
  if (wait) {
994
998
  await new Promise((resolve) => {
@@ -1023,15 +1027,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1023
1027
  }
1024
1028
  return;
1025
1029
  }
1026
- if (ap.pendingReceive) {
1027
- clearTimeout(ap.pendingReceive.timer);
1028
- ap.pendingReceive.resolve([message]);
1029
- ap.pendingReceive = null;
1030
- } else {
1031
- ap.inbox.push(message);
1030
+ if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
1031
+ ap.isIdle = false;
1032
+ this.broadcastActivity(agentId, "working", "Message received");
1033
+ this.deliverMessageViaStdin(agentId, ap, message);
1034
+ return;
1032
1035
  }
1036
+ ap.inbox.push(message);
1033
1037
  if (!ap.driver.supportsStdinNotification) return;
1034
- if (ap.isInReceiveMessage) return;
1035
1038
  if (!ap.sessionId) return;
1036
1039
  ap.pendingNotificationCount++;
1037
1040
  if (!ap.notificationTimer) {
@@ -1169,11 +1172,37 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1169
1172
  return { content, binary: false };
1170
1173
  }
1171
1174
  // Private methods
1175
+ /**
1176
+ * Broadcast an activity change — emits a single agent:activity event that carries
1177
+ * both the status (for the dot indicator) and trajectory entries (for the activity log).
1178
+ */
1179
+ broadcastActivity(agentId, activity, detail, extraTrajectory = []) {
1180
+ const ap = this.agents.get(agentId);
1181
+ const entries = [...extraTrajectory];
1182
+ const hasToolStart = entries.some((e) => e.kind === "tool_start");
1183
+ if (!hasToolStart) {
1184
+ entries.push({ kind: "status", activity, detail });
1185
+ }
1186
+ this.sendToServer({ type: "agent:activity", agentId, activity, detail, entries });
1187
+ if (ap) {
1188
+ ap.lastActivity = activity;
1189
+ ap.lastActivityDetail = detail;
1190
+ if (activity === "working" || activity === "thinking") {
1191
+ if (!ap.activityHeartbeat) {
1192
+ ap.activityHeartbeat = setInterval(() => {
1193
+ this.sendToServer({ type: "agent:activity", agentId, activity: ap.lastActivity, detail: ap.lastActivityDetail });
1194
+ }, ACTIVITY_HEARTBEAT_MS);
1195
+ }
1196
+ } else {
1197
+ if (ap.activityHeartbeat) {
1198
+ clearInterval(ap.activityHeartbeat);
1199
+ ap.activityHeartbeat = null;
1200
+ }
1201
+ }
1202
+ }
1203
+ }
1172
1204
  /** Handle a single ParsedEvent from any runtime driver */
1173
1205
  handleParsedEvent(agentId, event, driver) {
1174
- const trajectory = [];
1175
- let activity = "";
1176
- let detail = "";
1177
1206
  const ap = this.agents.get(agentId);
1178
1207
  switch (event.kind) {
1179
1208
  case "session_init":
@@ -1182,76 +1211,48 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1182
1211
  break;
1183
1212
  case "thinking": {
1184
1213
  const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
1185
- trajectory.push({ kind: "thinking", text });
1186
- activity = "thinking";
1187
- if (ap) ap.isInReceiveMessage = false;
1214
+ const extra = text ? [{ kind: "thinking", text }] : [];
1215
+ this.broadcastActivity(agentId, "thinking", "", extra);
1216
+ if (ap) ap.isIdle = false;
1188
1217
  break;
1189
1218
  }
1190
1219
  case "text": {
1191
1220
  const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
1192
- trajectory.push({ kind: "text", text });
1193
- activity = "thinking";
1194
- if (ap) ap.isInReceiveMessage = false;
1221
+ this.broadcastActivity(agentId, "thinking", "", [{ kind: "text", text }]);
1222
+ if (ap) ap.isIdle = false;
1195
1223
  break;
1196
1224
  }
1197
1225
  case "tool_call": {
1198
1226
  const toolName = event.name;
1199
1227
  const inputSummary = driver.summarizeToolInput(toolName, event.input);
1200
- if (toolName === `${driver.mcpToolPrefix}receive_message`) {
1201
- const isBlocking = event.input?.block === true || event.input?.block === "true";
1202
- if (isBlocking) {
1203
- activity = "online";
1204
- if (ap) {
1205
- ap.isInReceiveMessage = true;
1206
- ap.pendingNotificationCount = 0;
1207
- if (ap.notificationTimer) {
1208
- clearTimeout(ap.notificationTimer);
1209
- ap.notificationTimer = null;
1210
- }
1211
- }
1212
- } else {
1213
- trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
1214
- activity = "working";
1215
- detail = "Checking messages\u2026";
1216
- if (ap) ap.isInReceiveMessage = false;
1217
- }
1218
- } else if (toolName === `${driver.mcpToolPrefix}send_message`) {
1219
- trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
1220
- activity = "working";
1221
- detail = "Sending message\u2026";
1222
- if (ap) ap.isInReceiveMessage = false;
1223
- } else {
1224
- trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
1225
- activity = "working";
1226
- detail = driver.toolDisplayName(toolName);
1227
- if (ap) ap.isInReceiveMessage = false;
1228
- }
1228
+ const detail = toolName === `${driver.mcpToolPrefix}check_messages` ? "Checking messages\u2026" : toolName === `${driver.mcpToolPrefix}send_message` ? "Sending message\u2026" : driver.toolDisplayName(toolName);
1229
+ this.broadcastActivity(agentId, "working", detail, [{ kind: "tool_start", toolName, toolInput: inputSummary }]);
1230
+ if (ap) ap.isIdle = false;
1229
1231
  break;
1230
1232
  }
1231
1233
  case "turn_end":
1232
- activity = "online";
1233
1234
  if (ap) {
1234
- ap.isInReceiveMessage = false;
1235
1235
  if (event.sessionId) ap.sessionId = event.sessionId;
1236
+ if (ap.inbox.length > 0 && ap.driver.supportsStdinNotification && ap.sessionId) {
1237
+ const nextMessage = ap.inbox.shift();
1238
+ this.broadcastActivity(agentId, "working", "Message received");
1239
+ this.deliverMessageViaStdin(agentId, ap, nextMessage);
1240
+ } else {
1241
+ ap.isIdle = true;
1242
+ this.broadcastActivity(agentId, "online", "Idle");
1243
+ }
1236
1244
  }
1237
1245
  if (event.sessionId) {
1238
1246
  this.sendToServer({ type: "agent:session", agentId, sessionId: event.sessionId });
1239
1247
  }
1240
1248
  break;
1241
- case "error":
1242
- trajectory.push({ kind: "text", text: `Error: ${event.message}` });
1249
+ case "error": {
1250
+ const currentActivity = ap?.lastActivity || "working";
1251
+ const currentDetail = ap?.lastActivityDetail || "";
1252
+ this.sendToServer({ type: "agent:activity", agentId, activity: currentActivity, detail: currentDetail, entries: [{ kind: "text", text: `Error: ${event.message}` }] });
1243
1253
  break;
1244
- }
1245
- if (activity) {
1246
- this.sendToServer({ type: "agent:activity", agentId, activity, detail });
1247
- const hasToolStart = trajectory.some((e) => e.kind === "tool_start");
1248
- if (!hasToolStart) {
1249
- trajectory.push({ kind: "status", activity, detail });
1250
1254
  }
1251
1255
  }
1252
- if (trajectory.length > 0) {
1253
- this.sendToServer({ type: "agent:trajectory", agentId, entries: trajectory });
1254
- }
1255
1256
  }
1256
1257
  /** Send a batched notification to the agent via stdin about pending messages */
1257
1258
  sendStdinNotification(agentId) {
@@ -1261,15 +1262,32 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
1261
1262
  ap.pendingNotificationCount = 0;
1262
1263
  ap.notificationTimer = null;
1263
1264
  if (count === 0) return;
1264
- if (ap.isInReceiveMessage) return;
1265
+ if (ap.isIdle) return;
1265
1266
  if (!ap.sessionId) return;
1266
- const notification = `[System notification: You have ${count} new message${count > 1 ? "s" : ""} waiting. Call receive_message to read ${count > 1 ? "them" : "it"} when you're ready.]`;
1267
+ const notification = `[System notification: You have ${count} new message${count > 1 ? "s" : ""} waiting. Call check_messages to read ${count > 1 ? "them" : "it"} when you're ready.]`;
1267
1268
  console.log(`[Agent ${agentId}] Sending stdin notification: ${count} message(s)`);
1268
1269
  const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId);
1269
1270
  if (encoded) {
1270
1271
  ap.process.stdin?.write(encoded + "\n");
1271
1272
  }
1272
1273
  }
1274
+ /** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
1275
+ deliverMessageViaStdin(agentId, ap, message) {
1276
+ const channelLabel = message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
1277
+ const senderPrefix = message.sender_type === "agent" ? "(agent) " : "";
1278
+ const time = message.timestamp ? ` (${toLocalTime(message.timestamp)})` : "";
1279
+ const formatted = `[${channelLabel}]${time} ${senderPrefix}@${message.sender_name}: ${message.content}`;
1280
+ const prompt = `New message received:
1281
+
1282
+ ${formatted}
1283
+
1284
+ Respond as appropriate. Complete all your work before stopping.`;
1285
+ const encoded = ap.driver.encodeStdinMessage(prompt, ap.sessionId);
1286
+ if (encoded) {
1287
+ console.log(`[Agent ${agentId}] Delivering message via stdin from @${message.sender_name}`);
1288
+ ap.process.stdin?.write(encoded + "\n");
1289
+ }
1290
+ }
1273
1291
  /** List ONE level of a directory — directories returned without children (lazy-loaded on demand) */
1274
1292
  async listDirectoryChildren(dir, rootDir) {
1275
1293
  let entries;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@slock-ai/daemon",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "slock-daemon": "dist/index.js"