@slock-ai/daemon 0.19.0 → 0.21.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 +111 -45
- 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
|
@@ -372,8 +372,14 @@ var ClaudeDriver = class {
|
|
|
372
372
|
}
|
|
373
373
|
}
|
|
374
374
|
});
|
|
375
|
-
|
|
376
|
-
|
|
375
|
+
let mcpConfigArg;
|
|
376
|
+
if (process.platform === "win32") {
|
|
377
|
+
const mcpConfigPath = path.join(ctx.workingDirectory, ".slock-claude-mcp.json");
|
|
378
|
+
writeFileSync(mcpConfigPath, mcpConfig, "utf8");
|
|
379
|
+
mcpConfigArg = mcpConfigPath;
|
|
380
|
+
} else {
|
|
381
|
+
mcpConfigArg = mcpConfig;
|
|
382
|
+
}
|
|
377
383
|
const args2 = [
|
|
378
384
|
"--allow-dangerously-skip-permissions",
|
|
379
385
|
"--dangerously-skip-permissions",
|
|
@@ -383,7 +389,7 @@ var ClaudeDriver = class {
|
|
|
383
389
|
"--input-format",
|
|
384
390
|
"stream-json",
|
|
385
391
|
"--mcp-config",
|
|
386
|
-
|
|
392
|
+
mcpConfigArg,
|
|
387
393
|
"--model",
|
|
388
394
|
ctx.config.model || "sonnet",
|
|
389
395
|
"--disallowed-tools",
|
|
@@ -564,7 +570,7 @@ var CodexDriver = class {
|
|
|
564
570
|
"-c",
|
|
565
571
|
"mcp_servers.chat.startup_timeout_sec=30",
|
|
566
572
|
"-c",
|
|
567
|
-
"mcp_servers.chat.tool_timeout_sec=
|
|
573
|
+
"mcp_servers.chat.tool_timeout_sec=300",
|
|
568
574
|
"-c",
|
|
569
575
|
"mcp_servers.chat.enabled=true",
|
|
570
576
|
"-c",
|
|
@@ -578,11 +584,36 @@ var CodexDriver = class {
|
|
|
578
584
|
}
|
|
579
585
|
args2.push(ctx.prompt);
|
|
580
586
|
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
581
|
-
|
|
587
|
+
let spawnCmd = "codex";
|
|
588
|
+
let spawnArgs = args2;
|
|
589
|
+
if (process.platform === "win32") {
|
|
590
|
+
let codexEntry = null;
|
|
591
|
+
try {
|
|
592
|
+
const globalRoot = execSync("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
593
|
+
const candidate = path2.join(globalRoot, "@openai", "codex", "bin", "codex.js");
|
|
594
|
+
if (existsSync(candidate)) codexEntry = candidate;
|
|
595
|
+
} catch {
|
|
596
|
+
}
|
|
597
|
+
if (!codexEntry) {
|
|
598
|
+
try {
|
|
599
|
+
const cmdPath = execSync("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
|
|
600
|
+
const candidate = path2.join(path2.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
601
|
+
if (existsSync(candidate)) codexEntry = candidate;
|
|
602
|
+
} catch {
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
if (!codexEntry) {
|
|
606
|
+
throw new Error(
|
|
607
|
+
"Cannot resolve Codex CLI entry point on Windows. Ensure @openai/codex is installed globally via npm (npm i -g @openai/codex)."
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
spawnCmd = process.execPath;
|
|
611
|
+
spawnArgs = [codexEntry, ...args2];
|
|
612
|
+
}
|
|
613
|
+
const proc = spawn2(spawnCmd, spawnArgs, {
|
|
582
614
|
cwd: ctx.workingDirectory,
|
|
583
615
|
stdio: ["pipe", "pipe", "pipe"],
|
|
584
|
-
env: spawnEnv
|
|
585
|
-
shell: process.platform === "win32"
|
|
616
|
+
env: spawnEnv
|
|
586
617
|
});
|
|
587
618
|
return { process: proc };
|
|
588
619
|
}
|
|
@@ -772,6 +803,8 @@ var AgentProcessManager = class {
|
|
|
772
803
|
agents = /* @__PURE__ */ new Map();
|
|
773
804
|
agentsStarting = /* @__PURE__ */ new Set();
|
|
774
805
|
// Prevent concurrent starts of same agent
|
|
806
|
+
/** Cached configs for agents whose process exited normally — enables auto-restart on next message */
|
|
807
|
+
idleAgentConfigs = /* @__PURE__ */ new Map();
|
|
775
808
|
chatBridgePath;
|
|
776
809
|
sendToServer;
|
|
777
810
|
daemonApiKey;
|
|
@@ -917,9 +950,13 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
917
950
|
}
|
|
918
951
|
this.agents.delete(agentId);
|
|
919
952
|
if (code === 0) {
|
|
920
|
-
this.
|
|
921
|
-
|
|
953
|
+
this.idleAgentConfigs.set(agentId, {
|
|
954
|
+
config: { ...ap.config, sessionId: ap.sessionId },
|
|
955
|
+
sessionId: ap.sessionId
|
|
956
|
+
});
|
|
957
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: "online", detail: "" });
|
|
922
958
|
} else {
|
|
959
|
+
this.idleAgentConfigs.delete(agentId);
|
|
923
960
|
const reason = code === null ? "killed by signal" : `exit code ${code}`;
|
|
924
961
|
console.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
925
962
|
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
@@ -934,7 +971,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
934
971
|
throw err;
|
|
935
972
|
}
|
|
936
973
|
}
|
|
937
|
-
async stopAgent(agentId) {
|
|
974
|
+
async stopAgent(agentId, { wait = false, silent = false } = {}) {
|
|
975
|
+
this.idleAgentConfigs.delete(agentId);
|
|
938
976
|
const ap = this.agents.get(agentId);
|
|
939
977
|
if (!ap) return;
|
|
940
978
|
if (ap.pendingReceive) {
|
|
@@ -946,27 +984,43 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
946
984
|
}
|
|
947
985
|
this.agents.delete(agentId);
|
|
948
986
|
ap.process.kill("SIGTERM");
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
/** Hibernate: kill process but keep status as "sleeping" (auto-wakes on next message via --resume) */
|
|
953
|
-
sleepAgent(agentId) {
|
|
954
|
-
const ap = this.agents.get(agentId);
|
|
955
|
-
if (!ap) return;
|
|
956
|
-
console.log(`[Agent ${agentId}] Hibernating (sleeping)`);
|
|
957
|
-
if (ap.pendingReceive) {
|
|
958
|
-
clearTimeout(ap.pendingReceive.timer);
|
|
959
|
-
ap.pendingReceive.resolve([]);
|
|
987
|
+
if (!silent) {
|
|
988
|
+
this.sendToServer({ type: "agent:status", agentId, status: "inactive" });
|
|
989
|
+
this.sendToServer({ type: "agent:activity", agentId, activity: "offline", detail: "" });
|
|
960
990
|
}
|
|
961
|
-
if (
|
|
962
|
-
|
|
991
|
+
if (wait) {
|
|
992
|
+
await new Promise((resolve) => {
|
|
993
|
+
const forceKillTimer = setTimeout(() => {
|
|
994
|
+
try {
|
|
995
|
+
ap.process.kill("SIGKILL");
|
|
996
|
+
} catch {
|
|
997
|
+
}
|
|
998
|
+
resolve();
|
|
999
|
+
}, 5e3);
|
|
1000
|
+
ap.process.on("exit", () => {
|
|
1001
|
+
clearTimeout(forceKillTimer);
|
|
1002
|
+
resolve();
|
|
1003
|
+
});
|
|
1004
|
+
if (ap.process.exitCode !== null || ap.process.signalCode !== null) {
|
|
1005
|
+
clearTimeout(forceKillTimer);
|
|
1006
|
+
resolve();
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
963
1009
|
}
|
|
964
|
-
this.agents.delete(agentId);
|
|
965
|
-
ap.process.kill("SIGTERM");
|
|
966
1010
|
}
|
|
967
1011
|
deliverMessage(agentId, message) {
|
|
968
1012
|
const ap = this.agents.get(agentId);
|
|
969
|
-
if (!ap)
|
|
1013
|
+
if (!ap) {
|
|
1014
|
+
const cached = this.idleAgentConfigs.get(agentId);
|
|
1015
|
+
if (cached) {
|
|
1016
|
+
console.log(`[Agent ${agentId}] Auto-restarting idle process for incoming message`);
|
|
1017
|
+
this.idleAgentConfigs.delete(agentId);
|
|
1018
|
+
this.startAgent(agentId, cached.config, message).catch((err) => {
|
|
1019
|
+
console.error(`[Agent ${agentId}] Failed to auto-restart:`, err);
|
|
1020
|
+
});
|
|
1021
|
+
}
|
|
1022
|
+
return;
|
|
1023
|
+
}
|
|
970
1024
|
if (ap.pendingReceive) {
|
|
971
1025
|
clearTimeout(ap.pendingReceive.timer);
|
|
972
1026
|
ap.pendingReceive.resolve([message]);
|
|
@@ -994,8 +1048,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
994
1048
|
}
|
|
995
1049
|
}
|
|
996
1050
|
async stopAll() {
|
|
1051
|
+
this.idleAgentConfigs.clear();
|
|
997
1052
|
const ids = [...this.agents.keys()];
|
|
998
|
-
await Promise.all(ids.map((id) => this.stopAgent(id)));
|
|
1053
|
+
await Promise.all(ids.map((id) => this.stopAgent(id, { wait: true, silent: true })));
|
|
999
1054
|
}
|
|
1000
1055
|
getRunningAgentIds() {
|
|
1001
1056
|
return [...this.agents.keys()];
|
|
@@ -1126,39 +1181,51 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1126
1181
|
case "thinking": {
|
|
1127
1182
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1128
1183
|
trajectory.push({ kind: "thinking", text });
|
|
1129
|
-
|
|
1130
|
-
|
|
1184
|
+
if (ap?.isInReceiveMessage) {
|
|
1185
|
+
ap.isInReceiveMessage = false;
|
|
1186
|
+
} else {
|
|
1187
|
+
activity = "thinking";
|
|
1188
|
+
}
|
|
1131
1189
|
break;
|
|
1132
1190
|
}
|
|
1133
1191
|
case "text": {
|
|
1134
1192
|
const text = event.text.length > MAX_TRAJECTORY_TEXT ? event.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : event.text;
|
|
1135
1193
|
trajectory.push({ kind: "text", text });
|
|
1136
|
-
|
|
1137
|
-
|
|
1194
|
+
if (ap?.isInReceiveMessage) {
|
|
1195
|
+
ap.isInReceiveMessage = false;
|
|
1196
|
+
} else {
|
|
1197
|
+
activity = "thinking";
|
|
1198
|
+
}
|
|
1138
1199
|
break;
|
|
1139
1200
|
}
|
|
1140
1201
|
case "tool_call": {
|
|
1141
1202
|
const toolName = event.name;
|
|
1142
1203
|
const inputSummary = driver.summarizeToolInput(toolName, event.input);
|
|
1143
|
-
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1144
1204
|
if (toolName === `${driver.mcpToolPrefix}receive_message`) {
|
|
1145
|
-
const isBlocking = event.input?.block
|
|
1205
|
+
const isBlocking = event.input?.block === true || event.input?.block === "true";
|
|
1146
1206
|
if (isBlocking) {
|
|
1147
1207
|
activity = "online";
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1208
|
+
if (ap) {
|
|
1209
|
+
ap.isInReceiveMessage = true;
|
|
1210
|
+
ap.pendingNotificationCount = 0;
|
|
1211
|
+
if (ap.notificationTimer) {
|
|
1212
|
+
clearTimeout(ap.notificationTimer);
|
|
1213
|
+
ap.notificationTimer = null;
|
|
1214
|
+
}
|
|
1155
1215
|
}
|
|
1216
|
+
} else {
|
|
1217
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1218
|
+
activity = "working";
|
|
1219
|
+
detail = "Checking messages\u2026";
|
|
1220
|
+
if (ap) ap.isInReceiveMessage = false;
|
|
1156
1221
|
}
|
|
1157
1222
|
} else if (toolName === `${driver.mcpToolPrefix}send_message`) {
|
|
1223
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1158
1224
|
activity = "working";
|
|
1159
1225
|
detail = "Sending message\u2026";
|
|
1160
1226
|
if (ap) ap.isInReceiveMessage = false;
|
|
1161
1227
|
} else {
|
|
1228
|
+
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1162
1229
|
activity = "working";
|
|
1163
1230
|
detail = driver.toolDisplayName(toolName);
|
|
1164
1231
|
if (ap) ap.isInReceiveMessage = false;
|
|
@@ -1181,7 +1248,10 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1181
1248
|
}
|
|
1182
1249
|
if (activity) {
|
|
1183
1250
|
this.sendToServer({ type: "agent:activity", agentId, activity, detail });
|
|
1184
|
-
trajectory.
|
|
1251
|
+
const hasToolStart = trajectory.some((e) => e.kind === "tool_start");
|
|
1252
|
+
if (!hasToolStart) {
|
|
1253
|
+
trajectory.push({ kind: "status", activity, detail });
|
|
1254
|
+
}
|
|
1185
1255
|
}
|
|
1186
1256
|
if (trajectory.length > 0) {
|
|
1187
1257
|
this.sendToServer({ type: "agent:trajectory", agentId, entries: trajectory });
|
|
@@ -1346,10 +1416,6 @@ connection = new DaemonConnection({
|
|
|
1346
1416
|
console.log(`[Daemon] Stopping agent ${msg.agentId}`);
|
|
1347
1417
|
agentManager.stopAgent(msg.agentId);
|
|
1348
1418
|
break;
|
|
1349
|
-
case "agent:sleep":
|
|
1350
|
-
console.log(`[Daemon] Sleeping agent ${msg.agentId}`);
|
|
1351
|
-
agentManager.sleepAgent(msg.agentId);
|
|
1352
|
-
break;
|
|
1353
1419
|
case "agent:reset-workspace":
|
|
1354
1420
|
console.log(`[Daemon] Resetting workspace for agent ${msg.agentId}`);
|
|
1355
1421
|
agentManager.resetWorkspace(msg.agentId);
|