@slock-ai/daemon 0.26.0 → 0.27.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/index.js +125 -11
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -820,10 +820,11 @@ var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
|
820
820
|
function getMessageDeliveryText(supportsStdinNotification) {
|
|
821
821
|
return supportsStdinNotification ? "New messages will be delivered to you automatically via stdin." : "The daemon will automatically restart you when new messages arrive.";
|
|
822
822
|
}
|
|
823
|
-
var AgentProcessManager = class {
|
|
823
|
+
var AgentProcessManager = class _AgentProcessManager {
|
|
824
824
|
agents = /* @__PURE__ */ new Map();
|
|
825
825
|
agentsStarting = /* @__PURE__ */ new Set();
|
|
826
826
|
// Prevent concurrent starts of same agent
|
|
827
|
+
startingInboxes = /* @__PURE__ */ new Map();
|
|
827
828
|
/** Cached configs for agents whose process exited normally — enables auto-restart on next message */
|
|
828
829
|
idleAgentConfigs = /* @__PURE__ */ new Map();
|
|
829
830
|
chatBridgePath;
|
|
@@ -838,7 +839,7 @@ var AgentProcessManager = class {
|
|
|
838
839
|
this.dataDir = opts.dataDir || DATA_DIR;
|
|
839
840
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
840
841
|
}
|
|
841
|
-
async startAgent(agentId, config, wakeMessage, unreadSummary) {
|
|
842
|
+
async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt) {
|
|
842
843
|
if (this.agents.has(agentId) || this.agentsStarting.has(agentId)) return;
|
|
843
844
|
this.agentsStarting.add(agentId);
|
|
844
845
|
try {
|
|
@@ -866,7 +867,14 @@ ${config.description || "No role defined yet."}
|
|
|
866
867
|
await mkdir(path3.join(agentDataDir, "notes"), { recursive: true });
|
|
867
868
|
const isResume = !!config.sessionId;
|
|
868
869
|
let prompt;
|
|
869
|
-
if (isResume &&
|
|
870
|
+
if (isResume && resumePrompt) {
|
|
871
|
+
prompt = resumePrompt;
|
|
872
|
+
if (driver.supportsStdinNotification) {
|
|
873
|
+
prompt += `
|
|
874
|
+
|
|
875
|
+
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
876
|
+
}
|
|
877
|
+
} else if (isResume && wakeMessage) {
|
|
870
878
|
const channelLabel = wakeMessage.channel_type === "dm" ? `DM:@${wakeMessage.channel_name}` : `#${wakeMessage.channel_name}`;
|
|
871
879
|
const senderPrefix = wakeMessage.sender_type === "agent" ? "(agent) " : "";
|
|
872
880
|
const time = wakeMessage.timestamp ? ` (${toLocalTime(wakeMessage.timestamp)})` : "";
|
|
@@ -908,11 +916,6 @@ Note: While you are busy, you may receive [System notification: ...] messages. F
|
|
|
908
916
|
prompt += `
|
|
909
917
|
|
|
910
918
|
Use read_history to catch up on the channels listed above, then stop. Read each listed channel at most once unless a read fails. Do NOT call check_messages in this mode. If the history reveals a direct request, assignment, @mention, review request, or task clearly addressed to you, switch into active handling instead of stopping: reply with send_message and claim the relevant task before starting work. Otherwise, do NOT send any message in this mode. ${getMessageDeliveryText(driver.supportsStdinNotification)}`;
|
|
911
|
-
if (driver.supportsStdinNotification) {
|
|
912
|
-
prompt += `
|
|
913
|
-
|
|
914
|
-
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
915
|
-
}
|
|
916
919
|
} else if (isResume) {
|
|
917
920
|
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver.supportsStdinNotification)}`;
|
|
918
921
|
if (driver.supportsStdinNotification) {
|
|
@@ -934,7 +937,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
934
937
|
const agentProcess = {
|
|
935
938
|
process: proc,
|
|
936
939
|
driver,
|
|
937
|
-
inbox: [],
|
|
940
|
+
inbox: this.startingInboxes.get(agentId) || [],
|
|
938
941
|
config,
|
|
939
942
|
sessionId: config.sessionId || null,
|
|
940
943
|
isIdle: false,
|
|
@@ -944,6 +947,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
944
947
|
lastActivity: "",
|
|
945
948
|
lastActivityDetail: ""
|
|
946
949
|
};
|
|
950
|
+
this.startingInboxes.delete(agentId);
|
|
947
951
|
this.agents.set(agentId, agentProcess);
|
|
948
952
|
this.agentsStarting.delete(agentId);
|
|
949
953
|
let buffer = "";
|
|
@@ -1058,6 +1062,12 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1058
1062
|
deliverMessage(agentId, message) {
|
|
1059
1063
|
const ap = this.agents.get(agentId);
|
|
1060
1064
|
if (!ap) {
|
|
1065
|
+
if (this.agentsStarting.has(agentId)) {
|
|
1066
|
+
const pending = this.startingInboxes.get(agentId) || [];
|
|
1067
|
+
pending.push(message);
|
|
1068
|
+
this.startingInboxes.set(agentId, pending);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1061
1071
|
const cached = this.idleAgentConfigs.get(agentId);
|
|
1062
1072
|
if (cached) {
|
|
1063
1073
|
console.log(`[Agent ${agentId}] Auto-restarting idle process for incoming message`);
|
|
@@ -1112,7 +1122,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1112
1122
|
}
|
|
1113
1123
|
for (const entry of entries) {
|
|
1114
1124
|
if (!entry.isDirectory()) continue;
|
|
1115
|
-
const dirPath = path3.join(
|
|
1125
|
+
const dirPath = path3.join(this.dataDir, entry.name);
|
|
1116
1126
|
try {
|
|
1117
1127
|
const dirContents = await readdir(dirPath, { withFileTypes: true });
|
|
1118
1128
|
let totalSize = 0;
|
|
@@ -1212,6 +1222,102 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1212
1222
|
const content = await readFile(resolved, "utf-8");
|
|
1213
1223
|
return { content, binary: false };
|
|
1214
1224
|
}
|
|
1225
|
+
// Skill scanning
|
|
1226
|
+
// Per-runtime skill search paths (relative to home dir for global, workspace dir for workspace).
|
|
1227
|
+
// To add a new runtime, add an entry here.
|
|
1228
|
+
static SKILL_PATHS = {
|
|
1229
|
+
claude: {
|
|
1230
|
+
// Claude reads shared skills via symlinks in ~/.claude/skills/, not from ~/.agents/skills/
|
|
1231
|
+
global: [".claude/skills", ".claude/commands"],
|
|
1232
|
+
workspace: [".claude/skills", ".claude/commands"]
|
|
1233
|
+
},
|
|
1234
|
+
codex: {
|
|
1235
|
+
// Codex natively scans ~/.agents/skills/ and has built-in .system skills
|
|
1236
|
+
global: [".codex/skills", ".codex/skills/.system", ".agents/skills"],
|
|
1237
|
+
workspace: [".codex/skills", ".agents/skills"]
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
async listSkills(agentId, runtimeHint) {
|
|
1241
|
+
const agent = this.agents.get(agentId);
|
|
1242
|
+
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
1243
|
+
const home = os.homedir();
|
|
1244
|
+
const workspaceDir = path3.join(this.dataDir, agentId);
|
|
1245
|
+
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
1246
|
+
const globalResults = await Promise.all(
|
|
1247
|
+
paths.global.map((p) => this.scanSkillsDir(path3.join(home, p)))
|
|
1248
|
+
);
|
|
1249
|
+
const workspaceResults = await Promise.all(
|
|
1250
|
+
paths.workspace.map((p) => this.scanSkillsDir(path3.join(workspaceDir, p)))
|
|
1251
|
+
);
|
|
1252
|
+
const dedup = (skills) => {
|
|
1253
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1254
|
+
return skills.filter((s) => {
|
|
1255
|
+
if (seen.has(s.name)) return false;
|
|
1256
|
+
seen.add(s.name);
|
|
1257
|
+
return true;
|
|
1258
|
+
});
|
|
1259
|
+
};
|
|
1260
|
+
const shorten = (skills) => skills.map((s) => ({
|
|
1261
|
+
...s,
|
|
1262
|
+
sourcePath: s.sourcePath?.startsWith(home) ? "~" + s.sourcePath.slice(home.length) : s.sourcePath
|
|
1263
|
+
}));
|
|
1264
|
+
return {
|
|
1265
|
+
global: shorten(dedup(globalResults.flat())),
|
|
1266
|
+
workspace: shorten(dedup(workspaceResults.flat()))
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
async scanSkillsDir(dir) {
|
|
1270
|
+
let entries;
|
|
1271
|
+
try {
|
|
1272
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
1273
|
+
} catch {
|
|
1274
|
+
return [];
|
|
1275
|
+
}
|
|
1276
|
+
const skills = [];
|
|
1277
|
+
for (const entry of entries) {
|
|
1278
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
1279
|
+
const skillMd = path3.join(dir, entry.name, "SKILL.md");
|
|
1280
|
+
try {
|
|
1281
|
+
const content = await readFile(skillMd, "utf-8");
|
|
1282
|
+
const skill = this.parseSkillMd(entry.name, content);
|
|
1283
|
+
skill.sourcePath = dir;
|
|
1284
|
+
skills.push(skill);
|
|
1285
|
+
} catch {
|
|
1286
|
+
}
|
|
1287
|
+
} else if (entry.name.endsWith(".md")) {
|
|
1288
|
+
const cmdName = entry.name.replace(/\.md$/, "");
|
|
1289
|
+
try {
|
|
1290
|
+
const content = await readFile(path3.join(dir, entry.name), "utf-8");
|
|
1291
|
+
const skill = this.parseSkillMd(cmdName, content);
|
|
1292
|
+
skill.sourcePath = dir;
|
|
1293
|
+
skills.push(skill);
|
|
1294
|
+
} catch {
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
return skills;
|
|
1299
|
+
}
|
|
1300
|
+
parseSkillMd(dirName, content) {
|
|
1301
|
+
const info = {
|
|
1302
|
+
name: dirName,
|
|
1303
|
+
displayName: dirName,
|
|
1304
|
+
description: "",
|
|
1305
|
+
userInvocable: false
|
|
1306
|
+
};
|
|
1307
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1308
|
+
if (!match) return info;
|
|
1309
|
+
const frontmatter = match[1];
|
|
1310
|
+
for (const line of frontmatter.split("\n")) {
|
|
1311
|
+
const colonIdx = line.indexOf(":");
|
|
1312
|
+
if (colonIdx === -1) continue;
|
|
1313
|
+
const key = line.slice(0, colonIdx).trim();
|
|
1314
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
1315
|
+
if (key === "name") info.displayName = value;
|
|
1316
|
+
if (key === "description") info.description = value;
|
|
1317
|
+
if (key === "user-invocable") info.userInvocable = value === "true";
|
|
1318
|
+
}
|
|
1319
|
+
return info;
|
|
1320
|
+
}
|
|
1215
1321
|
// Private methods
|
|
1216
1322
|
/**
|
|
1217
1323
|
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
@@ -1460,7 +1566,7 @@ connection = new DaemonConnection({
|
|
|
1460
1566
|
switch (msg.type) {
|
|
1461
1567
|
case "agent:start":
|
|
1462
1568
|
console.log(`[Daemon] Starting agent ${msg.agentId} (model: ${msg.config.model}, session: ${msg.config.sessionId || "new"}${msg.wakeMessage ? ", with wake message" : ""})`);
|
|
1463
|
-
agentManager.startAgent(msg.agentId, msg.config, msg.wakeMessage, msg.unreadSummary).catch((err) => {
|
|
1569
|
+
agentManager.startAgent(msg.agentId, msg.config, msg.wakeMessage, msg.unreadSummary, msg.resumePrompt).catch((err) => {
|
|
1464
1570
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1465
1571
|
console.error(`[Daemon] Failed to start agent ${msg.agentId}:`, reason);
|
|
1466
1572
|
connection.send({ type: "agent:status", agentId: msg.agentId, status: "inactive" });
|
|
@@ -1504,6 +1610,14 @@ connection = new DaemonConnection({
|
|
|
1504
1610
|
});
|
|
1505
1611
|
});
|
|
1506
1612
|
break;
|
|
1613
|
+
case "agent:skills:list":
|
|
1614
|
+
agentManager.listSkills(msg.agentId, msg.runtime).then(({ global, workspace }) => {
|
|
1615
|
+
connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global, workspace });
|
|
1616
|
+
}).catch((err) => {
|
|
1617
|
+
console.error(`[Daemon] Failed to list skills for ${msg.agentId}:`, err);
|
|
1618
|
+
connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global: [], workspace: [] });
|
|
1619
|
+
});
|
|
1620
|
+
break;
|
|
1507
1621
|
case "machine:workspace:scan":
|
|
1508
1622
|
console.log("[Daemon] Scanning all workspace directories");
|
|
1509
1623
|
agentManager.scanAllWorkspaces().then((directories) => {
|