@slock-ai/daemon 0.26.0 → 0.27.1-alpha.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 +127 -11
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -253,6 +253,8 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
253
253
|
- **Don't interrupt ongoing conversations.** If a human is having a back-and-forth with another person (human or agent) on a topic, their follow-up messages are directed at that person \u2014 not at you. Do NOT jump in unless you are explicitly @mentioned or clearly addressed.
|
|
254
254
|
- **Only the person doing the work should report on it.** If someone else completed a task or submitted a PR, don't echo or summarize their work \u2014 let them respond to questions about it.
|
|
255
255
|
- **Claim before you start.** Always call \`${t("claim_tasks")}\` before doing any work on a task. If the claim fails, stop immediately and pick a different task.
|
|
256
|
+
- **Before stopping, check for concrete blockers you own.** If you still owe a specific handoff, review, decision, or reply that is currently blocking a specific person, send one minimal actionable message to that person or channel before stopping.
|
|
257
|
+
- **Do not narrate idling.** Do NOT send generic messages just to say you are going idle, sleeping, waiting, or staying silent. Do NOT broadcast speculative blockers.
|
|
256
258
|
|
|
257
259
|
### Formatting \u2014 No HTML
|
|
258
260
|
|
|
@@ -820,10 +822,11 @@ var ACTIVITY_HEARTBEAT_MS = 6e4;
|
|
|
820
822
|
function getMessageDeliveryText(supportsStdinNotification) {
|
|
821
823
|
return supportsStdinNotification ? "New messages will be delivered to you automatically via stdin." : "The daemon will automatically restart you when new messages arrive.";
|
|
822
824
|
}
|
|
823
|
-
var AgentProcessManager = class {
|
|
825
|
+
var AgentProcessManager = class _AgentProcessManager {
|
|
824
826
|
agents = /* @__PURE__ */ new Map();
|
|
825
827
|
agentsStarting = /* @__PURE__ */ new Set();
|
|
826
828
|
// Prevent concurrent starts of same agent
|
|
829
|
+
startingInboxes = /* @__PURE__ */ new Map();
|
|
827
830
|
/** Cached configs for agents whose process exited normally — enables auto-restart on next message */
|
|
828
831
|
idleAgentConfigs = /* @__PURE__ */ new Map();
|
|
829
832
|
chatBridgePath;
|
|
@@ -838,7 +841,7 @@ var AgentProcessManager = class {
|
|
|
838
841
|
this.dataDir = opts.dataDir || DATA_DIR;
|
|
839
842
|
this.driverResolver = opts.driverResolver || getDriver;
|
|
840
843
|
}
|
|
841
|
-
async startAgent(agentId, config, wakeMessage, unreadSummary) {
|
|
844
|
+
async startAgent(agentId, config, wakeMessage, unreadSummary, resumePrompt) {
|
|
842
845
|
if (this.agents.has(agentId) || this.agentsStarting.has(agentId)) return;
|
|
843
846
|
this.agentsStarting.add(agentId);
|
|
844
847
|
try {
|
|
@@ -866,7 +869,14 @@ ${config.description || "No role defined yet."}
|
|
|
866
869
|
await mkdir(path3.join(agentDataDir, "notes"), { recursive: true });
|
|
867
870
|
const isResume = !!config.sessionId;
|
|
868
871
|
let prompt;
|
|
869
|
-
if (isResume &&
|
|
872
|
+
if (isResume && resumePrompt) {
|
|
873
|
+
prompt = resumePrompt;
|
|
874
|
+
if (driver.supportsStdinNotification) {
|
|
875
|
+
prompt += `
|
|
876
|
+
|
|
877
|
+
Note: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.`;
|
|
878
|
+
}
|
|
879
|
+
} else if (isResume && wakeMessage) {
|
|
870
880
|
const channelLabel = wakeMessage.channel_type === "dm" ? `DM:@${wakeMessage.channel_name}` : `#${wakeMessage.channel_name}`;
|
|
871
881
|
const senderPrefix = wakeMessage.sender_type === "agent" ? "(agent) " : "";
|
|
872
882
|
const time = wakeMessage.timestamp ? ` (${toLocalTime(wakeMessage.timestamp)})` : "";
|
|
@@ -908,11 +918,6 @@ Note: While you are busy, you may receive [System notification: ...] messages. F
|
|
|
908
918
|
prompt += `
|
|
909
919
|
|
|
910
920
|
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
921
|
} else if (isResume) {
|
|
917
922
|
prompt = `No new messages while you were away. Nothing to do \u2014 just stop. ${getMessageDeliveryText(driver.supportsStdinNotification)}`;
|
|
918
923
|
if (driver.supportsStdinNotification) {
|
|
@@ -934,7 +939,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
934
939
|
const agentProcess = {
|
|
935
940
|
process: proc,
|
|
936
941
|
driver,
|
|
937
|
-
inbox: [],
|
|
942
|
+
inbox: this.startingInboxes.get(agentId) || [],
|
|
938
943
|
config,
|
|
939
944
|
sessionId: config.sessionId || null,
|
|
940
945
|
isIdle: false,
|
|
@@ -944,6 +949,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
944
949
|
lastActivity: "",
|
|
945
950
|
lastActivityDetail: ""
|
|
946
951
|
};
|
|
952
|
+
this.startingInboxes.delete(agentId);
|
|
947
953
|
this.agents.set(agentId, agentProcess);
|
|
948
954
|
this.agentsStarting.delete(agentId);
|
|
949
955
|
let buffer = "";
|
|
@@ -1058,6 +1064,12 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1058
1064
|
deliverMessage(agentId, message) {
|
|
1059
1065
|
const ap = this.agents.get(agentId);
|
|
1060
1066
|
if (!ap) {
|
|
1067
|
+
if (this.agentsStarting.has(agentId)) {
|
|
1068
|
+
const pending = this.startingInboxes.get(agentId) || [];
|
|
1069
|
+
pending.push(message);
|
|
1070
|
+
this.startingInboxes.set(agentId, pending);
|
|
1071
|
+
return;
|
|
1072
|
+
}
|
|
1061
1073
|
const cached = this.idleAgentConfigs.get(agentId);
|
|
1062
1074
|
if (cached) {
|
|
1063
1075
|
console.log(`[Agent ${agentId}] Auto-restarting idle process for incoming message`);
|
|
@@ -1112,7 +1124,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1112
1124
|
}
|
|
1113
1125
|
for (const entry of entries) {
|
|
1114
1126
|
if (!entry.isDirectory()) continue;
|
|
1115
|
-
const dirPath = path3.join(
|
|
1127
|
+
const dirPath = path3.join(this.dataDir, entry.name);
|
|
1116
1128
|
try {
|
|
1117
1129
|
const dirContents = await readdir(dirPath, { withFileTypes: true });
|
|
1118
1130
|
let totalSize = 0;
|
|
@@ -1212,6 +1224,102 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1212
1224
|
const content = await readFile(resolved, "utf-8");
|
|
1213
1225
|
return { content, binary: false };
|
|
1214
1226
|
}
|
|
1227
|
+
// Skill scanning
|
|
1228
|
+
// Per-runtime skill search paths (relative to home dir for global, workspace dir for workspace).
|
|
1229
|
+
// To add a new runtime, add an entry here.
|
|
1230
|
+
static SKILL_PATHS = {
|
|
1231
|
+
claude: {
|
|
1232
|
+
// Claude reads shared skills via symlinks in ~/.claude/skills/, not from ~/.agents/skills/
|
|
1233
|
+
global: [".claude/skills", ".claude/commands"],
|
|
1234
|
+
workspace: [".claude/skills", ".claude/commands"]
|
|
1235
|
+
},
|
|
1236
|
+
codex: {
|
|
1237
|
+
// Codex natively scans ~/.agents/skills/ and has built-in .system skills
|
|
1238
|
+
global: [".codex/skills", ".codex/skills/.system", ".agents/skills"],
|
|
1239
|
+
workspace: [".codex/skills", ".agents/skills"]
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
async listSkills(agentId, runtimeHint) {
|
|
1243
|
+
const agent = this.agents.get(agentId);
|
|
1244
|
+
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
1245
|
+
const home = os.homedir();
|
|
1246
|
+
const workspaceDir = path3.join(this.dataDir, agentId);
|
|
1247
|
+
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
1248
|
+
const globalResults = await Promise.all(
|
|
1249
|
+
paths.global.map((p) => this.scanSkillsDir(path3.join(home, p)))
|
|
1250
|
+
);
|
|
1251
|
+
const workspaceResults = await Promise.all(
|
|
1252
|
+
paths.workspace.map((p) => this.scanSkillsDir(path3.join(workspaceDir, p)))
|
|
1253
|
+
);
|
|
1254
|
+
const dedup = (skills) => {
|
|
1255
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1256
|
+
return skills.filter((s) => {
|
|
1257
|
+
if (seen.has(s.name)) return false;
|
|
1258
|
+
seen.add(s.name);
|
|
1259
|
+
return true;
|
|
1260
|
+
});
|
|
1261
|
+
};
|
|
1262
|
+
const shorten = (skills) => skills.map((s) => ({
|
|
1263
|
+
...s,
|
|
1264
|
+
sourcePath: s.sourcePath?.startsWith(home) ? "~" + s.sourcePath.slice(home.length) : s.sourcePath
|
|
1265
|
+
}));
|
|
1266
|
+
return {
|
|
1267
|
+
global: shorten(dedup(globalResults.flat())),
|
|
1268
|
+
workspace: shorten(dedup(workspaceResults.flat()))
|
|
1269
|
+
};
|
|
1270
|
+
}
|
|
1271
|
+
async scanSkillsDir(dir) {
|
|
1272
|
+
let entries;
|
|
1273
|
+
try {
|
|
1274
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
1275
|
+
} catch {
|
|
1276
|
+
return [];
|
|
1277
|
+
}
|
|
1278
|
+
const skills = [];
|
|
1279
|
+
for (const entry of entries) {
|
|
1280
|
+
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
1281
|
+
const skillMd = path3.join(dir, entry.name, "SKILL.md");
|
|
1282
|
+
try {
|
|
1283
|
+
const content = await readFile(skillMd, "utf-8");
|
|
1284
|
+
const skill = this.parseSkillMd(entry.name, content);
|
|
1285
|
+
skill.sourcePath = dir;
|
|
1286
|
+
skills.push(skill);
|
|
1287
|
+
} catch {
|
|
1288
|
+
}
|
|
1289
|
+
} else if (entry.name.endsWith(".md")) {
|
|
1290
|
+
const cmdName = entry.name.replace(/\.md$/, "");
|
|
1291
|
+
try {
|
|
1292
|
+
const content = await readFile(path3.join(dir, entry.name), "utf-8");
|
|
1293
|
+
const skill = this.parseSkillMd(cmdName, content);
|
|
1294
|
+
skill.sourcePath = dir;
|
|
1295
|
+
skills.push(skill);
|
|
1296
|
+
} catch {
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1300
|
+
return skills;
|
|
1301
|
+
}
|
|
1302
|
+
parseSkillMd(dirName, content) {
|
|
1303
|
+
const info = {
|
|
1304
|
+
name: dirName,
|
|
1305
|
+
displayName: dirName,
|
|
1306
|
+
description: "",
|
|
1307
|
+
userInvocable: false
|
|
1308
|
+
};
|
|
1309
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1310
|
+
if (!match) return info;
|
|
1311
|
+
const frontmatter = match[1];
|
|
1312
|
+
for (const line of frontmatter.split("\n")) {
|
|
1313
|
+
const colonIdx = line.indexOf(":");
|
|
1314
|
+
if (colonIdx === -1) continue;
|
|
1315
|
+
const key = line.slice(0, colonIdx).trim();
|
|
1316
|
+
const value = line.slice(colonIdx + 1).trim();
|
|
1317
|
+
if (key === "name") info.displayName = value;
|
|
1318
|
+
if (key === "description") info.description = value;
|
|
1319
|
+
if (key === "user-invocable") info.userInvocable = value === "true";
|
|
1320
|
+
}
|
|
1321
|
+
return info;
|
|
1322
|
+
}
|
|
1215
1323
|
// Private methods
|
|
1216
1324
|
/**
|
|
1217
1325
|
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
@@ -1460,7 +1568,7 @@ connection = new DaemonConnection({
|
|
|
1460
1568
|
switch (msg.type) {
|
|
1461
1569
|
case "agent:start":
|
|
1462
1570
|
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) => {
|
|
1571
|
+
agentManager.startAgent(msg.agentId, msg.config, msg.wakeMessage, msg.unreadSummary, msg.resumePrompt).catch((err) => {
|
|
1464
1572
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1465
1573
|
console.error(`[Daemon] Failed to start agent ${msg.agentId}:`, reason);
|
|
1466
1574
|
connection.send({ type: "agent:status", agentId: msg.agentId, status: "inactive" });
|
|
@@ -1504,6 +1612,14 @@ connection = new DaemonConnection({
|
|
|
1504
1612
|
});
|
|
1505
1613
|
});
|
|
1506
1614
|
break;
|
|
1615
|
+
case "agent:skills:list":
|
|
1616
|
+
agentManager.listSkills(msg.agentId, msg.runtime).then(({ global, workspace }) => {
|
|
1617
|
+
connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global, workspace });
|
|
1618
|
+
}).catch((err) => {
|
|
1619
|
+
console.error(`[Daemon] Failed to list skills for ${msg.agentId}:`, err);
|
|
1620
|
+
connection.send({ type: "agent:skills:list_result", agentId: msg.agentId, global: [], workspace: [] });
|
|
1621
|
+
});
|
|
1622
|
+
break;
|
|
1507
1623
|
case "machine:workspace:scan":
|
|
1508
1624
|
console.log("[Daemon] Scanning all workspace directories");
|
|
1509
1625
|
agentManager.scanAllWorkspaces().then((directories) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@slock-ai/daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.27.1-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"bin": {
|
|
6
6
|
"slock-daemon": "dist/index.js"
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"typecheck": "tsc --noEmit",
|
|
37
37
|
"release:patch": "npm version patch --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
38
38
|
"release:minor": "npm version minor --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
39
|
-
"release:major": "npm version major --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags"
|
|
39
|
+
"release:major": "npm version major --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags",
|
|
40
|
+
"release:alpha": "npm version prerelease --preid=alpha --no-git-tag-version && cd ../.. && pnpm install --lockfile-only && git add packages/daemon/package.json pnpm-lock.yaml && git commit -m \"chore: bump @slock-ai/daemon to v$(node -p \"require('./packages/daemon/package.json').version\")\" && git tag daemon-v$(node -p \"require('./packages/daemon/package.json').version\") && git push && git push --tags"
|
|
40
41
|
}
|
|
41
42
|
}
|