@slock-ai/daemon 0.7.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 +209 -0
- package/dist/index.js +62 -0
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -244,5 +244,214 @@ ${formatted}${footer}`
|
|
|
244
244
|
}
|
|
245
245
|
}
|
|
246
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
|
+
);
|
|
247
456
|
var transport = new StdioServerTransport();
|
|
248
457
|
await server.connect(transport);
|
package/dist/index.js
CHANGED
|
@@ -122,6 +122,11 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
122
122
|
2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
|
|
123
123
|
3. **${t("list_server")}** \u2014 List all channels, agents, and humans in this server.
|
|
124
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.
|
|
125
130
|
|
|
126
131
|
CRITICAL RULES:
|
|
127
132
|
${criticalRules.join("\n")}
|
|
@@ -168,6 +173,27 @@ Each channel has a **name** and optionally a **description** that define its pur
|
|
|
168
173
|
|
|
169
174
|
\`read_history(channel="#channel-name")\` or \`read_history(channel="DM:@peer-name")\`
|
|
170
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
|
+
|
|
171
197
|
## @Mentions
|
|
172
198
|
|
|
173
199
|
In channel group chats, you can @mention people by their unique name (e.g. "@alice" or "@bob").
|
|
@@ -182,6 +208,14 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
182
208
|
- When done, summarize the result.
|
|
183
209
|
- Keep updates concise \u2014 one or two sentences. Don't flood the chat.
|
|
184
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
|
+
|
|
185
219
|
## Workspace & Memory
|
|
186
220
|
|
|
187
221
|
Your working directory (cwd) is your **persistent workspace**. Everything you write here survives across sessions.
|
|
@@ -382,6 +416,11 @@ var ClaudeDriver = class {
|
|
|
382
416
|
});
|
|
383
417
|
}
|
|
384
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";
|
|
385
424
|
if (name.startsWith("mcp__chat__")) return "";
|
|
386
425
|
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
387
426
|
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
@@ -412,6 +451,15 @@ var ClaudeDriver = class {
|
|
|
412
451
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
413
452
|
}
|
|
414
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
|
+
}
|
|
415
463
|
return "";
|
|
416
464
|
} catch {
|
|
417
465
|
return "";
|
|
@@ -584,6 +632,11 @@ var CodexDriver = class {
|
|
|
584
632
|
});
|
|
585
633
|
}
|
|
586
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";
|
|
587
640
|
if (name.startsWith(this.mcpToolPrefix)) return "";
|
|
588
641
|
if (name === "shell" || name === "command_execution") return "Running command\u2026";
|
|
589
642
|
if (name === "file_change") return "Editing file\u2026";
|
|
@@ -608,6 +661,15 @@ var CodexDriver = class {
|
|
|
608
661
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
609
662
|
}
|
|
610
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
|
+
}
|
|
611
673
|
return "";
|
|
612
674
|
} catch {
|
|
613
675
|
return "";
|