@slock-ai/daemon 0.6.0 → 0.8.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 +211 -1
- package/dist/index.js +74 -1
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -127,7 +127,8 @@ server.tool(
|
|
|
127
127
|
text += "Use `#channel-name` with send_message to post in a channel.\n";
|
|
128
128
|
if (data.channels?.length > 0) {
|
|
129
129
|
for (const t of data.channels) {
|
|
130
|
-
text += ` - #${t.name}
|
|
130
|
+
text += t.description ? ` - #${t.name} \u2014 ${t.description}
|
|
131
|
+
` : ` - #${t.name}
|
|
131
132
|
`;
|
|
132
133
|
}
|
|
133
134
|
} else {
|
|
@@ -243,5 +244,214 @@ ${formatted}${footer}`
|
|
|
243
244
|
}
|
|
244
245
|
}
|
|
245
246
|
);
|
|
247
|
+
server.tool(
|
|
248
|
+
"list_tasks",
|
|
249
|
+
"List tasks on a channel's task board. Returns tasks with their number (#t1, #t2...), title, status, and assignee.",
|
|
250
|
+
{
|
|
251
|
+
channel: z.string().describe("The channel whose task board to view \u2014 e.g. '#engineering', '#proj-slock'"),
|
|
252
|
+
status: z.enum(["all", "open", "claimed", "completed"]).default("all").describe("Filter by status (default: all)")
|
|
253
|
+
},
|
|
254
|
+
async ({ channel, status }) => {
|
|
255
|
+
try {
|
|
256
|
+
const params = new URLSearchParams();
|
|
257
|
+
params.set("channel", channel);
|
|
258
|
+
if (status !== "all") params.set("status", status);
|
|
259
|
+
const res = await fetch(
|
|
260
|
+
`${serverUrl}/internal/agent/${agentId}/tasks?${params}`,
|
|
261
|
+
{ method: "GET", headers: commonHeaders }
|
|
262
|
+
);
|
|
263
|
+
const data = await res.json();
|
|
264
|
+
if (!res.ok) {
|
|
265
|
+
return {
|
|
266
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
if (!data.tasks || data.tasks.length === 0) {
|
|
270
|
+
return {
|
|
271
|
+
content: [{ type: "text", text: `No${status !== "all" ? ` ${status}` : ""} tasks in ${channel}.` }]
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
const formatted = data.tasks.map((t) => {
|
|
275
|
+
const assignee = t.claimedByName ? ` \u2192 @${t.claimedByName}` : "";
|
|
276
|
+
const creator = t.createdByName ? ` (by @${t.createdByName})` : "";
|
|
277
|
+
return `#t${t.taskNumber} [${t.status}] "${t.title}"${assignee}${creator}`;
|
|
278
|
+
}).join("\n");
|
|
279
|
+
return {
|
|
280
|
+
content: [
|
|
281
|
+
{
|
|
282
|
+
type: "text",
|
|
283
|
+
text: `## Task Board for ${channel} (${data.tasks.length} tasks)
|
|
284
|
+
|
|
285
|
+
${formatted}`
|
|
286
|
+
}
|
|
287
|
+
]
|
|
288
|
+
};
|
|
289
|
+
} catch (err) {
|
|
290
|
+
return {
|
|
291
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
);
|
|
296
|
+
server.tool(
|
|
297
|
+
"create_tasks",
|
|
298
|
+
"Create one or more tasks on a channel's task board. Returns the created task numbers.",
|
|
299
|
+
{
|
|
300
|
+
channel: z.string().describe("The channel to create tasks in \u2014 e.g. '#engineering'"),
|
|
301
|
+
tasks: z.array(
|
|
302
|
+
z.object({
|
|
303
|
+
title: z.string().describe("Task title")
|
|
304
|
+
})
|
|
305
|
+
).describe("Array of tasks to create")
|
|
306
|
+
},
|
|
307
|
+
async ({ channel, tasks }) => {
|
|
308
|
+
try {
|
|
309
|
+
const res = await fetch(`${serverUrl}/internal/agent/${agentId}/tasks`, {
|
|
310
|
+
method: "POST",
|
|
311
|
+
headers: commonHeaders,
|
|
312
|
+
body: JSON.stringify({ channel, tasks })
|
|
313
|
+
});
|
|
314
|
+
const data = await res.json();
|
|
315
|
+
if (!res.ok) {
|
|
316
|
+
return {
|
|
317
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
const created = data.tasks.map((t) => `#t${t.taskNumber} "${t.title}"`).join("\n");
|
|
321
|
+
return {
|
|
322
|
+
content: [
|
|
323
|
+
{
|
|
324
|
+
type: "text",
|
|
325
|
+
text: `Created ${data.tasks.length} task(s) in ${channel}:
|
|
326
|
+
${created}`
|
|
327
|
+
}
|
|
328
|
+
]
|
|
329
|
+
};
|
|
330
|
+
} catch (err) {
|
|
331
|
+
return {
|
|
332
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
);
|
|
337
|
+
server.tool(
|
|
338
|
+
"claim_tasks",
|
|
339
|
+
"Claim one or more tasks by their number. Returns which claims succeeded and which failed (e.g. already claimed by someone else).",
|
|
340
|
+
{
|
|
341
|
+
channel: z.string().describe("The channel whose tasks to claim \u2014 e.g. '#engineering'"),
|
|
342
|
+
task_numbers: z.array(z.number()).describe("Task numbers to claim (e.g. [1, 3, 5])")
|
|
343
|
+
},
|
|
344
|
+
async ({ channel, task_numbers }) => {
|
|
345
|
+
try {
|
|
346
|
+
const res = await fetch(
|
|
347
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/claim`,
|
|
348
|
+
{
|
|
349
|
+
method: "POST",
|
|
350
|
+
headers: commonHeaders,
|
|
351
|
+
body: JSON.stringify({ channel, task_numbers })
|
|
352
|
+
}
|
|
353
|
+
);
|
|
354
|
+
const data = await res.json();
|
|
355
|
+
if (!res.ok) {
|
|
356
|
+
return {
|
|
357
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
const lines = data.results.map((r) => {
|
|
361
|
+
if (r.success) {
|
|
362
|
+
return `#t${r.taskNumber}: claimed`;
|
|
363
|
+
}
|
|
364
|
+
return `#t${r.taskNumber}: FAILED \u2014 ${r.reason || "already claimed"}`;
|
|
365
|
+
});
|
|
366
|
+
const succeeded = data.results.filter((r) => r.success).length;
|
|
367
|
+
const failed = data.results.length - succeeded;
|
|
368
|
+
let summary = `${succeeded} claimed`;
|
|
369
|
+
if (failed > 0) summary += `, ${failed} failed`;
|
|
370
|
+
return {
|
|
371
|
+
content: [
|
|
372
|
+
{
|
|
373
|
+
type: "text",
|
|
374
|
+
text: `Claim results (${summary}):
|
|
375
|
+
${lines.join("\n")}`
|
|
376
|
+
}
|
|
377
|
+
]
|
|
378
|
+
};
|
|
379
|
+
} catch (err) {
|
|
380
|
+
return {
|
|
381
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
);
|
|
386
|
+
server.tool(
|
|
387
|
+
"unclaim_task",
|
|
388
|
+
"Release your claim on a task, setting it back to open.",
|
|
389
|
+
{
|
|
390
|
+
channel: z.string().describe("The channel \u2014 e.g. '#engineering'"),
|
|
391
|
+
task_number: z.number().describe("The task number to unclaim (e.g. 3)")
|
|
392
|
+
},
|
|
393
|
+
async ({ channel, task_number }) => {
|
|
394
|
+
try {
|
|
395
|
+
const res = await fetch(
|
|
396
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/unclaim`,
|
|
397
|
+
{
|
|
398
|
+
method: "POST",
|
|
399
|
+
headers: commonHeaders,
|
|
400
|
+
body: JSON.stringify({ channel, task_number })
|
|
401
|
+
}
|
|
402
|
+
);
|
|
403
|
+
const data = await res.json();
|
|
404
|
+
if (!res.ok) {
|
|
405
|
+
return {
|
|
406
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
return {
|
|
410
|
+
content: [
|
|
411
|
+
{ type: "text", text: `#t${task_number} unclaimed \u2014 now open.` }
|
|
412
|
+
]
|
|
413
|
+
};
|
|
414
|
+
} catch (err) {
|
|
415
|
+
return {
|
|
416
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
);
|
|
421
|
+
server.tool(
|
|
422
|
+
"complete_task",
|
|
423
|
+
"Mark a task as completed. You must be the current claimer.",
|
|
424
|
+
{
|
|
425
|
+
channel: z.string().describe("The channel \u2014 e.g. '#engineering'"),
|
|
426
|
+
task_number: z.number().describe("The task number to complete (e.g. 3)")
|
|
427
|
+
},
|
|
428
|
+
async ({ channel, task_number }) => {
|
|
429
|
+
try {
|
|
430
|
+
const res = await fetch(
|
|
431
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/complete`,
|
|
432
|
+
{
|
|
433
|
+
method: "POST",
|
|
434
|
+
headers: commonHeaders,
|
|
435
|
+
body: JSON.stringify({ channel, task_number })
|
|
436
|
+
}
|
|
437
|
+
);
|
|
438
|
+
const data = await res.json();
|
|
439
|
+
if (!res.ok) {
|
|
440
|
+
return {
|
|
441
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
return {
|
|
445
|
+
content: [
|
|
446
|
+
{ type: "text", text: `#t${task_number} marked as completed.` }
|
|
447
|
+
]
|
|
448
|
+
};
|
|
449
|
+
} catch (err) {
|
|
450
|
+
return {
|
|
451
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
);
|
|
246
456
|
var transport = new StdioServerTransport();
|
|
247
457
|
await server.connect(transport);
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import path3 from "path";
|
|
5
5
|
import os2 from "os";
|
|
6
|
+
import { createRequire } from "module";
|
|
6
7
|
import { execSync as execSync2 } from "child_process";
|
|
7
8
|
import { accessSync } from "fs";
|
|
8
9
|
import { fileURLToPath } from "url";
|
|
@@ -121,6 +122,11 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
121
122
|
2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
|
|
122
123
|
3. **${t("list_server")}** \u2014 List all channels, agents, and humans in this server.
|
|
123
124
|
4. **${t("read_history")}** \u2014 Read past messages from a channel or DM.
|
|
125
|
+
5. **${t("list_tasks")}** \u2014 View a channel's task board.
|
|
126
|
+
6. **${t("create_tasks")}** \u2014 Create tasks on a channel's task board (supports batch).
|
|
127
|
+
7. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
|
|
128
|
+
8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
129
|
+
9. **${t("complete_task")}** \u2014 Mark a task as done.
|
|
124
130
|
|
|
125
131
|
CRITICAL RULES:
|
|
126
132
|
${criticalRules.join("\n")}
|
|
@@ -156,10 +162,38 @@ The \`[...]\` prefix identifies where the message came from. Reuse it as the \`c
|
|
|
156
162
|
|
|
157
163
|
Call \`list_server\` to see all your channels, other agents, and humans in this server.
|
|
158
164
|
|
|
165
|
+
### Channel awareness
|
|
166
|
+
|
|
167
|
+
Each channel has a **name** and optionally a **description** that define its purpose (visible via \`list_server\`). Respect them:
|
|
168
|
+
- **Reply in context** \u2014 always respond in the channel the message came from.
|
|
169
|
+
- **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
|
|
170
|
+
- If unsure where something belongs, call \`list_server\` to review channel descriptions.
|
|
171
|
+
|
|
159
172
|
### Reading history
|
|
160
173
|
|
|
161
174
|
\`read_history(channel="#channel-name")\` or \`read_history(channel="DM:@peer-name")\`
|
|
162
175
|
|
|
176
|
+
### Task boards
|
|
177
|
+
|
|
178
|
+
Each channel has a task board where humans and agents can create, claim, and complete tasks. Tasks are numbered per channel (#t1, #t2, ...) for easy reference.
|
|
179
|
+
|
|
180
|
+
- **View tasks**: \`list_tasks(channel="#channel-name")\` \u2014 see all tasks with status and assignee.
|
|
181
|
+
- **Create tasks**: \`create_tasks(channel="#channel-name", tasks=[{title: "..."}, ...])\` \u2014 create one or more tasks. Useful for breaking down a large task into subtasks.
|
|
182
|
+
- **Claim tasks**: \`claim_tasks(channel="#channel-name", task_numbers=[1, 3])\` \u2014 claim tasks you want to work on. If another agent already claimed a task, your claim for that task fails (but others in the batch may succeed). Check the result to see which succeeded.
|
|
183
|
+
- **Unclaim**: \`unclaim_task(channel="#channel-name", task_number=3)\` \u2014 release a task back to open.
|
|
184
|
+
- **Complete**: \`complete_task(channel="#channel-name", task_number=3)\` \u2014 mark a task as done.
|
|
185
|
+
|
|
186
|
+
**CRITICAL: You MUST claim a task before starting work on it.** Never begin working on a task without claiming it first. The claim mechanism prevents multiple agents from doing the same work. If your claim fails (someone else claimed it), move on to another task.
|
|
187
|
+
|
|
188
|
+
### Splitting tasks for parallel execution
|
|
189
|
+
|
|
190
|
+
When you need to break down a large task into subtasks, structure them so agents can work **in parallel**:
|
|
191
|
+
- **Group by phase** if tasks have dependencies. Label them clearly (e.g. "Phase 1: ...", "Phase 2: ...") so agents know what can run concurrently and what must wait.
|
|
192
|
+
- **Prefer independent subtasks** that don't block each other. Each subtask should be completable without waiting for another.
|
|
193
|
+
- **Avoid creating sequential chains** where each task depends on the previous one \u2014 this forces agents to work one at a time, wasting capacity.
|
|
194
|
+
|
|
195
|
+
When you receive a notification about new tasks, check the task board and claim tasks relevant to your skills.
|
|
196
|
+
|
|
163
197
|
## @Mentions
|
|
164
198
|
|
|
165
199
|
In channel group chats, you can @mention people by their unique name (e.g. "@alice" or "@bob").
|
|
@@ -174,6 +208,14 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
174
208
|
- When done, summarize the result.
|
|
175
209
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
176
210
|
|
|
211
|
+
### Formatting \u2014 URLs in non-English text
|
|
212
|
+
|
|
213
|
+
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.
|
|
214
|
+
|
|
215
|
+
- **Wrong**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1Ahttp://localhost:3000\uFF0C\u8BF7\u67E5\u770B\` (the \`\uFF0C\` gets swallowed into the link)
|
|
216
|
+
- **Correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A<http://localhost:3000>\uFF0C\u8BF7\u67E5\u770B\`
|
|
217
|
+
- **Also correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A[http://localhost:3000](http://localhost:3000)\uFF0C\u8BF7\u67E5\u770B\`
|
|
218
|
+
|
|
177
219
|
## Workspace & Memory
|
|
178
220
|
|
|
179
221
|
Your working directory (cwd) is your **persistent workspace**. Everything you write here survives across sessions.
|
|
@@ -374,6 +416,11 @@ var ClaudeDriver = class {
|
|
|
374
416
|
});
|
|
375
417
|
}
|
|
376
418
|
toolDisplayName(name) {
|
|
419
|
+
if (name === "mcp__chat__list_tasks") return "Viewing task board\u2026";
|
|
420
|
+
if (name === "mcp__chat__create_tasks") return "Creating tasks\u2026";
|
|
421
|
+
if (name === "mcp__chat__claim_tasks") return "Claiming tasks\u2026";
|
|
422
|
+
if (name === "mcp__chat__unclaim_task") return "Unclaiming task\u2026";
|
|
423
|
+
if (name === "mcp__chat__complete_task") return "Completing task\u2026";
|
|
377
424
|
if (name.startsWith("mcp__chat__")) return "";
|
|
378
425
|
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
379
426
|
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
@@ -404,6 +451,15 @@ var ClaudeDriver = class {
|
|
|
404
451
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
405
452
|
}
|
|
406
453
|
if (name === "mcp__chat__read_history") return input.channel || "";
|
|
454
|
+
if (name === "mcp__chat__list_tasks") return input.channel || "";
|
|
455
|
+
if (name === "mcp__chat__create_tasks") return input.channel || "";
|
|
456
|
+
if (name === "mcp__chat__claim_tasks") {
|
|
457
|
+
const nums = input.task_numbers;
|
|
458
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
459
|
+
}
|
|
460
|
+
if (name === "mcp__chat__unclaim_task" || name === "mcp__chat__complete_task") {
|
|
461
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
462
|
+
}
|
|
407
463
|
return "";
|
|
408
464
|
} catch {
|
|
409
465
|
return "";
|
|
@@ -576,6 +632,11 @@ var CodexDriver = class {
|
|
|
576
632
|
});
|
|
577
633
|
}
|
|
578
634
|
toolDisplayName(name) {
|
|
635
|
+
if (name === `${this.mcpToolPrefix}list_tasks`) return "Viewing task board\u2026";
|
|
636
|
+
if (name === `${this.mcpToolPrefix}create_tasks`) return "Creating tasks\u2026";
|
|
637
|
+
if (name === `${this.mcpToolPrefix}claim_tasks`) return "Claiming tasks\u2026";
|
|
638
|
+
if (name === `${this.mcpToolPrefix}unclaim_task`) return "Unclaiming task\u2026";
|
|
639
|
+
if (name === `${this.mcpToolPrefix}complete_task`) return "Completing task\u2026";
|
|
579
640
|
if (name.startsWith(this.mcpToolPrefix)) return "";
|
|
580
641
|
if (name === "shell" || name === "command_execution") return "Running command\u2026";
|
|
581
642
|
if (name === "file_change") return "Editing file\u2026";
|
|
@@ -600,6 +661,15 @@ var CodexDriver = class {
|
|
|
600
661
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
601
662
|
}
|
|
602
663
|
if (name === `${this.mcpToolPrefix}read_history`) return input.channel || "";
|
|
664
|
+
if (name === `${this.mcpToolPrefix}list_tasks`) return input.channel || "";
|
|
665
|
+
if (name === `${this.mcpToolPrefix}create_tasks`) return input.channel || "";
|
|
666
|
+
if (name === `${this.mcpToolPrefix}claim_tasks`) {
|
|
667
|
+
const nums = input.task_numbers;
|
|
668
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
669
|
+
}
|
|
670
|
+
if (name === `${this.mcpToolPrefix}unclaim_task` || name === `${this.mcpToolPrefix}complete_task`) {
|
|
671
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
672
|
+
}
|
|
603
673
|
return "";
|
|
604
674
|
} catch {
|
|
605
675
|
return "";
|
|
@@ -1105,6 +1175,8 @@ var RUNTIMES = [
|
|
|
1105
1175
|
];
|
|
1106
1176
|
|
|
1107
1177
|
// src/index.ts
|
|
1178
|
+
var require2 = createRequire(import.meta.url);
|
|
1179
|
+
var DAEMON_VERSION = require2("../package.json").version;
|
|
1108
1180
|
function detectRuntimes() {
|
|
1109
1181
|
const detected = [];
|
|
1110
1182
|
for (const rt of RUNTIMES) {
|
|
@@ -1220,7 +1292,8 @@ connection = new DaemonConnection({
|
|
|
1220
1292
|
runtimes,
|
|
1221
1293
|
runningAgents: agentManager.getRunningAgentIds(),
|
|
1222
1294
|
hostname: os2.hostname(),
|
|
1223
|
-
os: `${os2.platform()} ${os2.arch()}
|
|
1295
|
+
os: `${os2.platform()} ${os2.arch()}`,
|
|
1296
|
+
daemonVersion: DAEMON_VERSION
|
|
1224
1297
|
});
|
|
1225
1298
|
},
|
|
1226
1299
|
onDisconnect: () => {
|