@slock-ai/daemon 0.24.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.
- package/dist/chat-bridge.js +0 -56
- package/dist/index.js +57 -62
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -256,62 +256,6 @@ server.tool(
|
|
|
256
256
|
}
|
|
257
257
|
}
|
|
258
258
|
);
|
|
259
|
-
server.tool(
|
|
260
|
-
"wait_for_message",
|
|
261
|
-
"Block and wait for new messages. ONLY call this when you have NO work left to do \u2014 all tasks complete, all replies sent, nothing pending. This blocks for up to ~4 minutes waiting for a message to arrive. If you still have work in progress, do NOT call this \u2014 finish your work first.",
|
|
262
|
-
{},
|
|
263
|
-
async () => {
|
|
264
|
-
try {
|
|
265
|
-
const POLL_INTERVAL_MS = 25e3;
|
|
266
|
-
const MAX_WAIT_MS = 27e4;
|
|
267
|
-
const startTime = Date.now();
|
|
268
|
-
while (true) {
|
|
269
|
-
const elapsed = Date.now() - startTime;
|
|
270
|
-
const remaining = MAX_WAIT_MS - elapsed;
|
|
271
|
-
if (remaining <= 0) {
|
|
272
|
-
const params2 = new URLSearchParams();
|
|
273
|
-
params2.set("block", "true");
|
|
274
|
-
params2.set("timeout", "1000");
|
|
275
|
-
const res2 = await fetch(
|
|
276
|
-
`${serverUrl}/internal/agent/${agentId}/receive?${params2}`,
|
|
277
|
-
{ method: "GET", headers: commonHeaders }
|
|
278
|
-
);
|
|
279
|
-
if (!res2.ok) {
|
|
280
|
-
const errData = await res2.json().catch(() => ({}));
|
|
281
|
-
return { content: [{ type: "text", text: `Error: ${errData.error || res2.statusText}` }] };
|
|
282
|
-
}
|
|
283
|
-
const data2 = await res2.json();
|
|
284
|
-
if (data2.messages?.length > 0) {
|
|
285
|
-
return { content: [{ type: "text", text: formatMessages(data2.messages) }] };
|
|
286
|
-
}
|
|
287
|
-
return {
|
|
288
|
-
content: [{ type: "text", text: "No new messages. Take a rest \u2014 new messages will be delivered to you directly. Do not take any further action until notified." }]
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
const thisPoll = Math.min(POLL_INTERVAL_MS, remaining);
|
|
292
|
-
const params = new URLSearchParams();
|
|
293
|
-
params.set("block", "true");
|
|
294
|
-
params.set("timeout", String(thisPoll));
|
|
295
|
-
const res = await fetch(
|
|
296
|
-
`${serverUrl}/internal/agent/${agentId}/receive?${params}`,
|
|
297
|
-
{ method: "GET", headers: commonHeaders }
|
|
298
|
-
);
|
|
299
|
-
if (!res.ok) {
|
|
300
|
-
const errData = await res.json().catch(() => ({}));
|
|
301
|
-
return { content: [{ type: "text", text: `Error: ${errData.error || res.statusText}` }] };
|
|
302
|
-
}
|
|
303
|
-
const data = await res.json();
|
|
304
|
-
if (data.messages?.length > 0) {
|
|
305
|
-
return { content: [{ type: "text", text: formatMessages(data.messages) }] };
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
} catch (err) {
|
|
309
|
-
return {
|
|
310
|
-
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
311
|
-
};
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
);
|
|
315
259
|
function formatMessages(messages) {
|
|
316
260
|
return messages.map((m) => {
|
|
317
261
|
const target = formatTarget(m);
|
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.
|
|
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. **
|
|
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,18 +120,17 @@ 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("
|
|
124
|
-
2. **${t("
|
|
125
|
-
3. **${t("
|
|
126
|
-
4. **${t("
|
|
127
|
-
5. **${t("
|
|
128
|
-
6. **${t("
|
|
129
|
-
7. **${t("
|
|
130
|
-
8. **${t("
|
|
131
|
-
9. **${t("
|
|
132
|
-
10. **${t("
|
|
133
|
-
11. **${t("
|
|
134
|
-
12. **${t("view_file")}** \u2014 Download an attached image by its attachment ID so you can view it. Use when messages contain image attachments.
|
|
123
|
+
1. **${t("check_messages")}** \u2014 Non-blocking check for new messages. Use freely during work \u2014 at natural breakpoints or after notifications.
|
|
124
|
+
2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
|
|
125
|
+
3. **${t("list_server")}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
|
|
126
|
+
4. **${t("read_history")}** \u2014 Read past messages from a channel or DM.
|
|
127
|
+
5. **${t("list_tasks")}** \u2014 View a channel's task board.
|
|
128
|
+
6. **${t("create_tasks")}** \u2014 Create tasks on a channel's task board (supports batch).
|
|
129
|
+
7. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
|
|
130
|
+
8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
131
|
+
9. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
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.
|
|
135
134
|
|
|
136
135
|
CRITICAL RULES:
|
|
137
136
|
${criticalRules.join("\n")}
|
|
@@ -340,7 +339,7 @@ While you are busy (executing tools, thinking, etc.), new messages may arrive. W
|
|
|
340
339
|
How to handle these:
|
|
341
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.
|
|
342
341
|
- If the new message is higher priority, you may pivot to it. If not, continue your current work.
|
|
343
|
-
-
|
|
342
|
+
- \`check_messages\` returns instantly with any pending messages (or "no new messages"). It is always safe to call.`;
|
|
344
343
|
}
|
|
345
344
|
if (config.description) {
|
|
346
345
|
prompt += `
|
|
@@ -718,11 +717,10 @@ var CodexDriver = class {
|
|
|
718
717
|
return buildBaseSystemPrompt(config, {
|
|
719
718
|
toolPrefix: "",
|
|
720
719
|
extraCriticalRules: [
|
|
721
|
-
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
722
|
-
"- ALWAYS call wait_for_message 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."
|
|
723
721
|
],
|
|
724
722
|
postStartupNotes: [
|
|
725
|
-
"**IMPORTANT**: Your process exits after each turn completes. You will be automatically restarted when new messages arrive.
|
|
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."
|
|
726
724
|
],
|
|
727
725
|
includeStdinNotificationSection: false
|
|
728
726
|
});
|
|
@@ -872,9 +870,9 @@ Use read_history to catch up, or respond to the message above first.`;
|
|
|
872
870
|
}
|
|
873
871
|
prompt += `
|
|
874
872
|
|
|
875
|
-
Respond as appropriate \u2014 reply using send_message, or take action as needed.
|
|
873
|
+
Respond as appropriate \u2014 reply using send_message, or take action as needed. Complete ALL your work before stopping.
|
|
876
874
|
|
|
877
|
-
IMPORTANT: If the message requires multi-step work (e.g. research, code changes, testing), complete ALL steps before
|
|
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.`;
|
|
878
876
|
if (driver.supportsStdinNotification) {
|
|
879
877
|
prompt += `
|
|
880
878
|
|
|
@@ -888,14 +886,14 @@ Note: While you are busy, you may receive [System notification: ...] messages. F
|
|
|
888
886
|
}
|
|
889
887
|
prompt += `
|
|
890
888
|
|
|
891
|
-
Use read_history to catch up on important channels, then
|
|
889
|
+
Use read_history to catch up on important channels, then stop. New messages will be delivered to you automatically.`;
|
|
892
890
|
if (driver.supportsStdinNotification) {
|
|
893
891
|
prompt += `
|
|
894
892
|
|
|
895
893
|
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
896
894
|
}
|
|
897
895
|
} else if (isResume) {
|
|
898
|
-
prompt = `No new messages while you were away.
|
|
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.`;
|
|
899
897
|
if (driver.supportsStdinNotification) {
|
|
900
898
|
prompt += `
|
|
901
899
|
|
|
@@ -916,10 +914,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
916
914
|
process: proc,
|
|
917
915
|
driver,
|
|
918
916
|
inbox: [],
|
|
919
|
-
pendingReceive: null,
|
|
920
917
|
config,
|
|
921
918
|
sessionId: config.sessionId || null,
|
|
922
|
-
|
|
919
|
+
isIdle: false,
|
|
923
920
|
notificationTimer: null,
|
|
924
921
|
pendingNotificationCount: 0,
|
|
925
922
|
activityHeartbeat: null,
|
|
@@ -952,10 +949,6 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
952
949
|
if (this.agents.has(agentId)) {
|
|
953
950
|
const ap = this.agents.get(agentId);
|
|
954
951
|
if (ap.process !== proc) return;
|
|
955
|
-
if (ap.pendingReceive) {
|
|
956
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
957
|
-
ap.pendingReceive.resolve([]);
|
|
958
|
-
}
|
|
959
952
|
if (ap.notificationTimer) {
|
|
960
953
|
clearTimeout(ap.notificationTimer);
|
|
961
954
|
}
|
|
@@ -989,10 +982,6 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
989
982
|
this.idleAgentConfigs.delete(agentId);
|
|
990
983
|
const ap = this.agents.get(agentId);
|
|
991
984
|
if (!ap) return;
|
|
992
|
-
if (ap.pendingReceive) {
|
|
993
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
994
|
-
ap.pendingReceive.resolve([]);
|
|
995
|
-
}
|
|
996
985
|
if (ap.notificationTimer) {
|
|
997
986
|
clearTimeout(ap.notificationTimer);
|
|
998
987
|
}
|
|
@@ -1038,19 +1027,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1038
1027
|
}
|
|
1039
1028
|
return;
|
|
1040
1029
|
}
|
|
1041
|
-
if (ap.
|
|
1042
|
-
ap.
|
|
1030
|
+
if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
|
|
1031
|
+
ap.isIdle = false;
|
|
1043
1032
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
1033
|
+
this.deliverMessageViaStdin(agentId, ap, message);
|
|
1034
|
+
return;
|
|
1044
1035
|
}
|
|
1045
|
-
|
|
1046
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
1047
|
-
ap.pendingReceive.resolve([message]);
|
|
1048
|
-
ap.pendingReceive = null;
|
|
1049
|
-
} else {
|
|
1050
|
-
ap.inbox.push(message);
|
|
1051
|
-
}
|
|
1036
|
+
ap.inbox.push(message);
|
|
1052
1037
|
if (!ap.driver.supportsStdinNotification) return;
|
|
1053
|
-
if (ap.isInReceiveMessage) return;
|
|
1054
1038
|
if (!ap.sessionId) return;
|
|
1055
1039
|
ap.pendingNotificationCount++;
|
|
1056
1040
|
if (!ap.notificationTimer) {
|
|
@@ -1229,40 +1213,34 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1229
1213
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1230
1214
|
const extra = text ? [{ kind: "thinking", text }] : [];
|
|
1231
1215
|
this.broadcastActivity(agentId, "thinking", "", extra);
|
|
1232
|
-
if (ap) ap.
|
|
1216
|
+
if (ap) ap.isIdle = false;
|
|
1233
1217
|
break;
|
|
1234
1218
|
}
|
|
1235
1219
|
case "text": {
|
|
1236
1220
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1237
1221
|
this.broadcastActivity(agentId, "thinking", "", [{ kind: "text", text }]);
|
|
1238
|
-
if (ap) ap.
|
|
1222
|
+
if (ap) ap.isIdle = false;
|
|
1239
1223
|
break;
|
|
1240
1224
|
}
|
|
1241
1225
|
case "tool_call": {
|
|
1242
1226
|
const toolName = event.name;
|
|
1243
1227
|
const inputSummary = driver.summarizeToolInput(toolName, event.input);
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
ap.isInReceiveMessage = true;
|
|
1248
|
-
ap.pendingNotificationCount = 0;
|
|
1249
|
-
if (ap.notificationTimer) {
|
|
1250
|
-
clearTimeout(ap.notificationTimer);
|
|
1251
|
-
ap.notificationTimer = null;
|
|
1252
|
-
}
|
|
1253
|
-
}
|
|
1254
|
-
} else {
|
|
1255
|
-
const detail = toolName === `${driver.mcpToolPrefix}check_messages` ? "Checking messages\u2026" : toolName === `${driver.mcpToolPrefix}send_message` ? "Sending message\u2026" : driver.toolDisplayName(toolName);
|
|
1256
|
-
this.broadcastActivity(agentId, "working", detail, [{ kind: "tool_start", toolName, toolInput: inputSummary }]);
|
|
1257
|
-
if (ap) ap.isInReceiveMessage = false;
|
|
1258
|
-
}
|
|
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;
|
|
1259
1231
|
break;
|
|
1260
1232
|
}
|
|
1261
1233
|
case "turn_end":
|
|
1262
|
-
this.broadcastActivity(agentId, "online", "Turn complete");
|
|
1263
1234
|
if (ap) {
|
|
1264
|
-
ap.isInReceiveMessage = false;
|
|
1265
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
|
+
}
|
|
1266
1244
|
}
|
|
1267
1245
|
if (event.sessionId) {
|
|
1268
1246
|
this.sendToServer({ type: "agent:session", agentId, sessionId: event.sessionId });
|
|
@@ -1284,7 +1262,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1284
1262
|
ap.pendingNotificationCount = 0;
|
|
1285
1263
|
ap.notificationTimer = null;
|
|
1286
1264
|
if (count === 0) return;
|
|
1287
|
-
if (ap.
|
|
1265
|
+
if (ap.isIdle) return;
|
|
1288
1266
|
if (!ap.sessionId) return;
|
|
1289
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.]`;
|
|
1290
1268
|
console.log(`[Agent ${agentId}] Sending stdin notification: ${count} message(s)`);
|
|
@@ -1293,6 +1271,23 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1293
1271
|
ap.process.stdin?.write(encoded + "\n");
|
|
1294
1272
|
}
|
|
1295
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
|
+
}
|
|
1296
1291
|
/** List ONE level of a directory — directories returned without children (lazy-loaded on demand) */
|
|
1297
1292
|
async listDirectoryChildren(dir, rootDir) {
|
|
1298
1293
|
let entries;
|