@slock-ai/daemon 0.20.0 → 0.22.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 +57 -27
- package/dist/index.js +76 -39
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -230,37 +230,57 @@ Use your Read tool to view this image.` }]
|
|
|
230
230
|
);
|
|
231
231
|
server.tool(
|
|
232
232
|
"receive_message",
|
|
233
|
-
"Receive new messages.
|
|
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
234
|
{
|
|
235
|
-
block: z.boolean().default(true).describe("Whether to block (wait) for new messages")
|
|
236
|
-
timeout_ms: z.number().default(59e3).describe("How long to wait in ms when blocking (default 59s, just under MCP tool call timeout)")
|
|
235
|
+
block: z.boolean().default(true).describe("Whether to block (wait) for new messages")
|
|
237
236
|
},
|
|
238
|
-
async ({ block
|
|
237
|
+
async ({ block }) => {
|
|
239
238
|
try {
|
|
240
|
-
const
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
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
|
+
}
|
|
252
283
|
}
|
|
253
|
-
const formatted = data.messages.map((m) => {
|
|
254
|
-
const target = formatTarget(m);
|
|
255
|
-
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
256
|
-
const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
|
|
257
|
-
const senderType = m.sender_type === "agent" ? " type=agent" : "";
|
|
258
|
-
const attachSuffix = m.attachments?.length ? ` [${m.attachments.length} image${m.attachments.length > 1 ? "s" : ""}: ${m.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to see]` : "";
|
|
259
|
-
return `[target=${target} msg=${msgId} time=${time}${senderType}] @${m.sender_name}: ${m.content}${attachSuffix}`;
|
|
260
|
-
}).join("\n");
|
|
261
|
-
return {
|
|
262
|
-
content: [{ type: "text", text: formatted }]
|
|
263
|
-
};
|
|
264
284
|
} catch (err) {
|
|
265
285
|
return {
|
|
266
286
|
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
@@ -268,6 +288,16 @@ server.tool(
|
|
|
268
288
|
}
|
|
269
289
|
}
|
|
270
290
|
);
|
|
291
|
+
function formatMessages(messages) {
|
|
292
|
+
return messages.map((m) => {
|
|
293
|
+
const target = formatTarget(m);
|
|
294
|
+
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
295
|
+
const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
|
|
296
|
+
const senderType = m.sender_type === "agent" ? " type=agent" : "";
|
|
297
|
+
const attachSuffix = m.attachments?.length ? ` [${m.attachments.length} image${m.attachments.length > 1 ? "s" : ""}: ${m.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to see]` : "";
|
|
298
|
+
return `[target=${target} msg=${msgId} time=${time}${senderType}] @${m.sender_name}: ${m.content}${attachSuffix}`;
|
|
299
|
+
}).join("\n");
|
|
300
|
+
}
|
|
271
301
|
server.tool(
|
|
272
302
|
"list_server",
|
|
273
303
|
"List all channels in this server, including which ones you have joined, plus all agents and humans. Use this to discover who and where you can message.",
|
package/dist/index.js
CHANGED
|
@@ -255,6 +255,8 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
255
255
|
|
|
256
256
|
Never output raw HTML tags in your messages. Use plain-text @mentions (e.g. \`@alice\`) and #channel references (e.g. \`#general\`, \`#t1\`). Do NOT wrap them in \`<a>\` tags or any other HTML.
|
|
257
257
|
|
|
258
|
+
When you intend to reference a channel or mention someone, write them as plain text \u2014 do NOT wrap them in backticks (inline code). Backtick-wrapped mentions render as code instead of interactive links.
|
|
259
|
+
|
|
258
260
|
### Formatting \u2014 URLs in non-English text
|
|
259
261
|
|
|
260
262
|
When writing a URL next to non-ASCII punctuation (Chinese, Japanese, etc.), always wrap the URL in angle brackets or use markdown link syntax. Otherwise the punctuation may be rendered as part of the URL.
|
|
@@ -570,7 +572,7 @@ var CodexDriver = class {
|
|
|
570
572
|
"-c",
|
|
571
573
|
"mcp_servers.chat.startup_timeout_sec=30",
|
|
572
574
|
"-c",
|
|
573
|
-
"mcp_servers.chat.tool_timeout_sec=
|
|
575
|
+
"mcp_servers.chat.tool_timeout_sec=300",
|
|
574
576
|
"-c",
|
|
575
577
|
"mcp_servers.chat.enabled=true",
|
|
576
578
|
"-c",
|
|
@@ -803,6 +805,8 @@ var AgentProcessManager = class {
|
|
|
803
805
|
agents = /* @__PURE__ */ new Map();
|
|
804
806
|
agentsStarting = /* @__PURE__ */ new Set();
|
|
805
807
|
// Prevent concurrent starts of same agent
|
|
808
|
+
/** Cached configs for agents whose process exited normally — enables auto-restart on next message */
|
|
809
|
+
idleAgentConfigs = /* @__PURE__ */ new Map();
|
|
806
810
|
chatBridgePath;
|
|
807
811
|
sendToServer;
|
|
808
812
|
daemonApiKey;
|
|
@@ -948,9 +952,13 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
948
952
|
}
|
|
949
953
|
this.agents.delete(agentId);
|
|
950
954
|
if (code === 0) {
|
|
951
|
-
this.
|
|
952
|
-
|
|
955
|
+
this.idleAgentConfigs.set(agentId, {
|
|
956
|
+
config: { ...ap.config, sessionId: ap.sessionId },
|
|
957
|
+
sessionId: ap.sessionId
|
|
958
|
+
});
|
|
959
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: "online", detail: "" });
|
|
953
960
|
} else {
|
|
961
|
+
this.idleAgentConfigs.delete(agentId);
|
|
954
962
|
const reason = code === null ? "killed by signal" : `exit code ${code}`;
|
|
955
963
|
console.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
956
964
|
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
@@ -965,7 +973,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
965
973
|
throw err;
|
|
966
974
|
}
|
|
967
975
|
}
|
|
968
|
-
async stopAgent(agentId) {
|
|
976
|
+
async stopAgent(agentId, { wait = false, silent = false } = {}) {
|
|
977
|
+
this.idleAgentConfigs.delete(agentId);
|
|
969
978
|
const ap = this.agents.get(agentId);
|
|
970
979
|
if (!ap) return;
|
|
971
980
|
if (ap.pendingReceive) {
|
|
@@ -977,27 +986,43 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
977
986
|
}
|
|
978
987
|
this.agents.delete(agentId);
|
|
979
988
|
ap.process.kill("SIGTERM");
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
/** Hibernate: kill process but keep status as "sleeping" (auto-wakes on next message via --resume) */
|
|
984
|
-
sleepAgent(agentId) {
|
|
985
|
-
const ap = this.agents.get(agentId);
|
|
986
|
-
if (!ap) return;
|
|
987
|
-
console.log(`[Agent ${agentId}] Hibernating (sleeping)`);
|
|
988
|
-
if (ap.pendingReceive) {
|
|
989
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
990
|
-
ap.pendingReceive.resolve([]);
|
|
989
|
+
if (!silent) {
|
|
990
|
+
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
991
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: "offline", detail: "" });
|
|
991
992
|
}
|
|
992
|
-
if (
|
|
993
|
-
|
|
993
|
+
if (wait) {
|
|
994
|
+
await new Promise((resolve) => {
|
|
995
|
+
const forceKillTimer = setTimeout(() => {
|
|
996
|
+
try {
|
|
997
|
+
ap.process.kill("SIGKILL");
|
|
998
|
+
} catch {
|
|
999
|
+
}
|
|
1000
|
+
resolve();
|
|
1001
|
+
}, 5e3);
|
|
1002
|
+
ap.process.on("exit", () => {
|
|
1003
|
+
clearTimeout(forceKillTimer);
|
|
1004
|
+
resolve();
|
|
1005
|
+
});
|
|
1006
|
+
if (ap.process.exitCode !== null || ap.process.signalCode !== null) {
|
|
1007
|
+
clearTimeout(forceKillTimer);
|
|
1008
|
+
resolve();
|
|
1009
|
+
}
|
|
1010
|
+
});
|
|
994
1011
|
}
|
|
995
|
-
this.agents.delete(agentId);
|
|
996
|
-
ap.process.kill("SIGTERM");
|
|
997
1012
|
}
|
|
998
1013
|
deliverMessage(agentId, message) {
|
|
999
1014
|
const ap = this.agents.get(agentId);
|
|
1000
|
-
if (!ap)
|
|
1015
|
+
if (!ap) {
|
|
1016
|
+
const cached = this.idleAgentConfigs.get(agentId);
|
|
1017
|
+
if (cached) {
|
|
1018
|
+
console.log(`[Agent ${agentId}] Auto-restarting idle process for incoming message`);
|
|
1019
|
+
this.idleAgentConfigs.delete(agentId);
|
|
1020
|
+
this.startAgent(agentId, cached.config, message).catch((err) => {
|
|
1021
|
+
console.error(`[Agent ${agentId}] Failed to auto-restart:`, err);
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
return;
|
|
1025
|
+
}
|
|
1001
1026
|
if (ap.pendingReceive) {
|
|
1002
1027
|
clearTimeout(ap.pendingReceive.timer);
|
|
1003
1028
|
ap.pendingReceive.resolve([message]);
|
|
@@ -1025,8 +1050,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1025
1050
|
}
|
|
1026
1051
|
}
|
|
1027
1052
|
async stopAll() {
|
|
1053
|
+
this.idleAgentConfigs.clear();
|
|
1028
1054
|
const ids = [...this.agents.keys()];
|
|
1029
|
-
await Promise.all(ids.map((id) => this.stopAgent(id)));
|
|
1055
|
+
await Promise.all(ids.map((id) => this.stopAgent(id, { wait: true, silent: true })));
|
|
1030
1056
|
}
|
|
1031
1057
|
getRunningAgentIds() {
|
|
1032
1058
|
return [...this.agents.keys()];
|
|
@@ -1157,39 +1183,51 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1157
1183
|
case "thinking": {
|
|
1158
1184
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1159
1185
|
trajectory.push({ kind: "thinking", text });
|
|
1160
|
-
|
|
1161
|
-
|
|
1186
|
+
if (ap?.isInReceiveMessage) {
|
|
1187
|
+
ap.isInReceiveMessage = false;
|
|
1188
|
+
} else {
|
|
1189
|
+
activity = "thinking";
|
|
1190
|
+
}
|
|
1162
1191
|
break;
|
|
1163
1192
|
}
|
|
1164
1193
|
case "text": {
|
|
1165
1194
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1166
1195
|
trajectory.push({ kind: "text", text });
|
|
1167
|
-
|
|
1168
|
-
|
|
1196
|
+
if (ap?.isInReceiveMessage) {
|
|
1197
|
+
ap.isInReceiveMessage = false;
|
|
1198
|
+
} else {
|
|
1199
|
+
activity = "thinking";
|
|
1200
|
+
}
|
|
1169
1201
|
break;
|
|
1170
1202
|
}
|
|
1171
1203
|
case "tool_call": {
|
|
1172
1204
|
const toolName = event.name;
|
|
1173
1205
|
const inputSummary = driver.summarizeToolInput(toolName, event.input);
|
|
1174
|
-
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1175
1206
|
if (toolName === `${driver.mcpToolPrefix}receive_message`) {
|
|
1176
|
-
const isBlocking = event.input?.block
|
|
1207
|
+
const isBlocking = event.input?.block === true || event.input?.block === "true";
|
|
1177
1208
|
if (isBlocking) {
|
|
1178
1209
|
activity = "online";
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1210
|
+
if (ap) {
|
|
1211
|
+
ap.isInReceiveMessage = true;
|
|
1212
|
+
ap.pendingNotificationCount = 0;
|
|
1213
|
+
if (ap.notificationTimer) {
|
|
1214
|
+
clearTimeout(ap.notificationTimer);
|
|
1215
|
+
ap.notificationTimer = null;
|
|
1216
|
+
}
|
|
1186
1217
|
}
|
|
1218
|
+
} else {
|
|
1219
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1220
|
+
activity = "working";
|
|
1221
|
+
detail = "Checking messages\u2026";
|
|
1222
|
+
if (ap) ap.isInReceiveMessage = false;
|
|
1187
1223
|
}
|
|
1188
1224
|
} else if (toolName === `${driver.mcpToolPrefix}send_message`) {
|
|
1225
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1189
1226
|
activity = "working";
|
|
1190
1227
|
detail = "Sending message\u2026";
|
|
1191
1228
|
if (ap) ap.isInReceiveMessage = false;
|
|
1192
1229
|
} else {
|
|
1230
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1193
1231
|
activity = "working";
|
|
1194
1232
|
detail = driver.toolDisplayName(toolName);
|
|
1195
1233
|
if (ap) ap.isInReceiveMessage = false;
|
|
@@ -1212,7 +1250,10 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1212
1250
|
}
|
|
1213
1251
|
if (activity) {
|
|
1214
1252
|
this.sendToServer({ type: "agent:activity", agentId, activity, detail });
|
|
1215
|
-
trajectory.
|
|
1253
|
+
const hasToolStart = trajectory.some((e) => e.kind === "tool_start");
|
|
1254
|
+
if (!hasToolStart) {
|
|
1255
|
+
trajectory.push({ kind: "status", activity, detail });
|
|
1256
|
+
}
|
|
1216
1257
|
}
|
|
1217
1258
|
if (trajectory.length > 0) {
|
|
1218
1259
|
this.sendToServer({ type: "agent:trajectory", agentId, entries: trajectory });
|
|
@@ -1377,10 +1418,6 @@ connection = new DaemonConnection({
|
|
|
1377
1418
|
console.log(`[Daemon] Stopping agent ${msg.agentId}`);
|
|
1378
1419
|
agentManager.stopAgent(msg.agentId);
|
|
1379
1420
|
break;
|
|
1380
|
-
case "agent:sleep":
|
|
1381
|
-
console.log(`[Daemon] Sleeping agent ${msg.agentId}`);
|
|
1382
|
-
agentManager.sleepAgent(msg.agentId);
|
|
1383
|
-
break;
|
|
1384
1421
|
case "agent:reset-workspace":
|
|
1385
1422
|
console.log(`[Daemon] Resetting workspace for agent ${msg.agentId}`);
|
|
1386
1423
|
agentManager.resetWorkspace(msg.agentId);
|