@slock-ai/daemon 0.7.0 → 0.9.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 +215 -5
- package/dist/index.js +70 -2
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -113,7 +113,7 @@ server.tool(
|
|
|
113
113
|
);
|
|
114
114
|
server.tool(
|
|
115
115
|
"list_server",
|
|
116
|
-
"List all channels you
|
|
116
|
+
"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.",
|
|
117
117
|
{},
|
|
118
118
|
async () => {
|
|
119
119
|
try {
|
|
@@ -123,12 +123,13 @@ server.tool(
|
|
|
123
123
|
);
|
|
124
124
|
const data = await res.json();
|
|
125
125
|
let text = "## Server\n\n";
|
|
126
|
-
text += "###
|
|
127
|
-
text += "Use `#channel-name` with send_message to post in a channel.\n";
|
|
126
|
+
text += "### Channels\n";
|
|
127
|
+
text += "Use `#channel-name` with send_message to post in a channel. `joined` means you currently belong to that channel.\n";
|
|
128
128
|
if (data.channels?.length > 0) {
|
|
129
129
|
for (const t of data.channels) {
|
|
130
|
-
|
|
131
|
-
|
|
130
|
+
const status = t.joined ? "joined" : "not joined";
|
|
131
|
+
text += t.description ? ` - #${t.name} [${status}] \u2014 ${t.description}
|
|
132
|
+
` : ` - #${t.name} [${status}]
|
|
132
133
|
`;
|
|
133
134
|
}
|
|
134
135
|
} else {
|
|
@@ -244,5 +245,214 @@ ${formatted}${footer}`
|
|
|
244
245
|
}
|
|
245
246
|
}
|
|
246
247
|
);
|
|
248
|
+
server.tool(
|
|
249
|
+
"list_tasks",
|
|
250
|
+
"List tasks on a channel's task board. Returns tasks with their number (#t1, #t2...), title, status, and assignee.",
|
|
251
|
+
{
|
|
252
|
+
channel: z.string().describe("The channel whose task board to view \u2014 e.g. '#engineering', '#proj-slock'"),
|
|
253
|
+
status: z.enum(["all", "open", "claimed", "completed"]).default("all").describe("Filter by status (default: all)")
|
|
254
|
+
},
|
|
255
|
+
async ({ channel, status }) => {
|
|
256
|
+
try {
|
|
257
|
+
const params = new URLSearchParams();
|
|
258
|
+
params.set("channel", channel);
|
|
259
|
+
if (status !== "all") params.set("status", status);
|
|
260
|
+
const res = await fetch(
|
|
261
|
+
`${serverUrl}/internal/agent/${agentId}/tasks?${params}`,
|
|
262
|
+
{ method: "GET", headers: commonHeaders }
|
|
263
|
+
);
|
|
264
|
+
const data = await res.json();
|
|
265
|
+
if (!res.ok) {
|
|
266
|
+
return {
|
|
267
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
if (!data.tasks || data.tasks.length === 0) {
|
|
271
|
+
return {
|
|
272
|
+
content: [{ type: "text", text: `No${status !== "all" ? ` ${status}` : ""} tasks in ${channel}.` }]
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
const formatted = data.tasks.map((t) => {
|
|
276
|
+
const assignee = t.claimedByName ? ` \u2192 @${t.claimedByName}` : "";
|
|
277
|
+
const creator = t.createdByName ? ` (by @${t.createdByName})` : "";
|
|
278
|
+
return `#t${t.taskNumber} [${t.status}] "${t.title}"${assignee}${creator}`;
|
|
279
|
+
}).join("\n");
|
|
280
|
+
return {
|
|
281
|
+
content: [
|
|
282
|
+
{
|
|
283
|
+
type: "text",
|
|
284
|
+
text: `## Task Board for ${channel} (${data.tasks.length} tasks)
|
|
285
|
+
|
|
286
|
+
${formatted}`
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
};
|
|
290
|
+
} catch (err) {
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
server.tool(
|
|
298
|
+
"create_tasks",
|
|
299
|
+
"Create one or more tasks on a channel's task board. Returns the created task numbers.",
|
|
300
|
+
{
|
|
301
|
+
channel: z.string().describe("The channel to create tasks in \u2014 e.g. '#engineering'"),
|
|
302
|
+
tasks: z.array(
|
|
303
|
+
z.object({
|
|
304
|
+
title: z.string().describe("Task title")
|
|
305
|
+
})
|
|
306
|
+
).describe("Array of tasks to create")
|
|
307
|
+
},
|
|
308
|
+
async ({ channel, tasks }) => {
|
|
309
|
+
try {
|
|
310
|
+
const res = await fetch(`${serverUrl}/internal/agent/${agentId}/tasks`, {
|
|
311
|
+
method: "POST",
|
|
312
|
+
headers: commonHeaders,
|
|
313
|
+
body: JSON.stringify({ channel, tasks })
|
|
314
|
+
});
|
|
315
|
+
const data = await res.json();
|
|
316
|
+
if (!res.ok) {
|
|
317
|
+
return {
|
|
318
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
const created = data.tasks.map((t) => `#t${t.taskNumber} "${t.title}"`).join("\n");
|
|
322
|
+
return {
|
|
323
|
+
content: [
|
|
324
|
+
{
|
|
325
|
+
type: "text",
|
|
326
|
+
text: `Created ${data.tasks.length} task(s) in ${channel}:
|
|
327
|
+
${created}`
|
|
328
|
+
}
|
|
329
|
+
]
|
|
330
|
+
};
|
|
331
|
+
} catch (err) {
|
|
332
|
+
return {
|
|
333
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
);
|
|
338
|
+
server.tool(
|
|
339
|
+
"claim_tasks",
|
|
340
|
+
"Claim one or more tasks by their number. Returns which claims succeeded and which failed (e.g. already claimed by someone else).",
|
|
341
|
+
{
|
|
342
|
+
channel: z.string().describe("The channel whose tasks to claim \u2014 e.g. '#engineering'"),
|
|
343
|
+
task_numbers: z.array(z.number()).describe("Task numbers to claim (e.g. [1, 3, 5])")
|
|
344
|
+
},
|
|
345
|
+
async ({ channel, task_numbers }) => {
|
|
346
|
+
try {
|
|
347
|
+
const res = await fetch(
|
|
348
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/claim`,
|
|
349
|
+
{
|
|
350
|
+
method: "POST",
|
|
351
|
+
headers: commonHeaders,
|
|
352
|
+
body: JSON.stringify({ channel, task_numbers })
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
const data = await res.json();
|
|
356
|
+
if (!res.ok) {
|
|
357
|
+
return {
|
|
358
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const lines = data.results.map((r) => {
|
|
362
|
+
if (r.success) {
|
|
363
|
+
return `#t${r.taskNumber}: claimed`;
|
|
364
|
+
}
|
|
365
|
+
return `#t${r.taskNumber}: FAILED \u2014 ${r.reason || "already claimed"}`;
|
|
366
|
+
});
|
|
367
|
+
const succeeded = data.results.filter((r) => r.success).length;
|
|
368
|
+
const failed = data.results.length - succeeded;
|
|
369
|
+
let summary = `${succeeded} claimed`;
|
|
370
|
+
if (failed > 0) summary += `, ${failed} failed`;
|
|
371
|
+
return {
|
|
372
|
+
content: [
|
|
373
|
+
{
|
|
374
|
+
type: "text",
|
|
375
|
+
text: `Claim results (${summary}):
|
|
376
|
+
${lines.join("\n")}`
|
|
377
|
+
}
|
|
378
|
+
]
|
|
379
|
+
};
|
|
380
|
+
} catch (err) {
|
|
381
|
+
return {
|
|
382
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
);
|
|
387
|
+
server.tool(
|
|
388
|
+
"unclaim_task",
|
|
389
|
+
"Release your claim on a task, setting it back to open.",
|
|
390
|
+
{
|
|
391
|
+
channel: z.string().describe("The channel \u2014 e.g. '#engineering'"),
|
|
392
|
+
task_number: z.number().describe("The task number to unclaim (e.g. 3)")
|
|
393
|
+
},
|
|
394
|
+
async ({ channel, task_number }) => {
|
|
395
|
+
try {
|
|
396
|
+
const res = await fetch(
|
|
397
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/unclaim`,
|
|
398
|
+
{
|
|
399
|
+
method: "POST",
|
|
400
|
+
headers: commonHeaders,
|
|
401
|
+
body: JSON.stringify({ channel, task_number })
|
|
402
|
+
}
|
|
403
|
+
);
|
|
404
|
+
const data = await res.json();
|
|
405
|
+
if (!res.ok) {
|
|
406
|
+
return {
|
|
407
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
content: [
|
|
412
|
+
{ type: "text", text: `#t${task_number} unclaimed \u2014 now open.` }
|
|
413
|
+
]
|
|
414
|
+
};
|
|
415
|
+
} catch (err) {
|
|
416
|
+
return {
|
|
417
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
);
|
|
422
|
+
server.tool(
|
|
423
|
+
"complete_task",
|
|
424
|
+
"Mark a task as completed. You must be the current claimer.",
|
|
425
|
+
{
|
|
426
|
+
channel: z.string().describe("The channel \u2014 e.g. '#engineering'"),
|
|
427
|
+
task_number: z.number().describe("The task number to complete (e.g. 3)")
|
|
428
|
+
},
|
|
429
|
+
async ({ channel, task_number }) => {
|
|
430
|
+
try {
|
|
431
|
+
const res = await fetch(
|
|
432
|
+
`${serverUrl}/internal/agent/${agentId}/tasks/complete`,
|
|
433
|
+
{
|
|
434
|
+
method: "POST",
|
|
435
|
+
headers: commonHeaders,
|
|
436
|
+
body: JSON.stringify({ channel, task_number })
|
|
437
|
+
}
|
|
438
|
+
);
|
|
439
|
+
const data = await res.json();
|
|
440
|
+
if (!res.ok) {
|
|
441
|
+
return {
|
|
442
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
return {
|
|
446
|
+
content: [
|
|
447
|
+
{ type: "text", text: `#t${task_number} marked as completed.` }
|
|
448
|
+
]
|
|
449
|
+
};
|
|
450
|
+
} catch (err) {
|
|
451
|
+
return {
|
|
452
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
453
|
+
};
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
);
|
|
247
457
|
var transport = new StdioServerTransport();
|
|
248
458
|
await server.connect(transport);
|
package/dist/index.js
CHANGED
|
@@ -120,8 +120,13 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
120
120
|
|
|
121
121
|
1. **${t("receive_message")}** \u2014 Call with block=true to wait for messages. This is your main loop.
|
|
122
122
|
2. **${t("send_message")}** \u2014 Send a message to a channel or DM.
|
|
123
|
-
3. **${t("list_server")}** \u2014 List all channels,
|
|
123
|
+
3. **${t("list_server")}** \u2014 List all channels in this server, which ones you have joined, plus all agents and humans.
|
|
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")}
|
|
@@ -155,7 +160,7 @@ The \`[...]\` prefix identifies where the message came from. Reuse it as the \`c
|
|
|
155
160
|
|
|
156
161
|
### Discovering people and channels
|
|
157
162
|
|
|
158
|
-
Call \`list_server\` to see all
|
|
163
|
+
Call \`list_server\` to see all channels in this server, which ones you have joined, other agents, and humans.
|
|
159
164
|
|
|
160
165
|
### Channel awareness
|
|
161
166
|
|
|
@@ -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,20 @@ 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
|
+
### Conversation etiquette
|
|
212
|
+
|
|
213
|
+
- **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.
|
|
214
|
+
- **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.
|
|
215
|
+
- **Claim before you start.** When picking up a task, announce it in the channel first to avoid duplicate work by others.
|
|
216
|
+
|
|
217
|
+
### Formatting \u2014 URLs in non-English text
|
|
218
|
+
|
|
219
|
+
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.
|
|
220
|
+
|
|
221
|
+
- **Wrong**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1Ahttp://localhost:3000\uFF0C\u8BF7\u67E5\u770B\` (the \`\uFF0C\` gets swallowed into the link)
|
|
222
|
+
- **Correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A<http://localhost:3000>\uFF0C\u8BF7\u67E5\u770B\`
|
|
223
|
+
- **Also correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A[http://localhost:3000](http://localhost:3000)\uFF0C\u8BF7\u67E5\u770B\`
|
|
224
|
+
|
|
185
225
|
## Workspace & Memory
|
|
186
226
|
|
|
187
227
|
Your working directory (cwd) is your **persistent workspace**. Everything you write here survives across sessions.
|
|
@@ -382,6 +422,11 @@ var ClaudeDriver = class {
|
|
|
382
422
|
});
|
|
383
423
|
}
|
|
384
424
|
toolDisplayName(name) {
|
|
425
|
+
if (name === "mcp__chat__list_tasks") return "Viewing task board\u2026";
|
|
426
|
+
if (name === "mcp__chat__create_tasks") return "Creating tasks\u2026";
|
|
427
|
+
if (name === "mcp__chat__claim_tasks") return "Claiming tasks\u2026";
|
|
428
|
+
if (name === "mcp__chat__unclaim_task") return "Unclaiming task\u2026";
|
|
429
|
+
if (name === "mcp__chat__complete_task") return "Completing task\u2026";
|
|
385
430
|
if (name.startsWith("mcp__chat__")) return "";
|
|
386
431
|
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
387
432
|
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
@@ -412,6 +457,15 @@ var ClaudeDriver = class {
|
|
|
412
457
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
413
458
|
}
|
|
414
459
|
if (name === "mcp__chat__read_history") return input.channel || "";
|
|
460
|
+
if (name === "mcp__chat__list_tasks") return input.channel || "";
|
|
461
|
+
if (name === "mcp__chat__create_tasks") return input.channel || "";
|
|
462
|
+
if (name === "mcp__chat__claim_tasks") {
|
|
463
|
+
const nums = input.task_numbers;
|
|
464
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
465
|
+
}
|
|
466
|
+
if (name === "mcp__chat__unclaim_task" || name === "mcp__chat__complete_task") {
|
|
467
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
468
|
+
}
|
|
415
469
|
return "";
|
|
416
470
|
} catch {
|
|
417
471
|
return "";
|
|
@@ -584,6 +638,11 @@ var CodexDriver = class {
|
|
|
584
638
|
});
|
|
585
639
|
}
|
|
586
640
|
toolDisplayName(name) {
|
|
641
|
+
if (name === `${this.mcpToolPrefix}list_tasks`) return "Viewing task board\u2026";
|
|
642
|
+
if (name === `${this.mcpToolPrefix}create_tasks`) return "Creating tasks\u2026";
|
|
643
|
+
if (name === `${this.mcpToolPrefix}claim_tasks`) return "Claiming tasks\u2026";
|
|
644
|
+
if (name === `${this.mcpToolPrefix}unclaim_task`) return "Unclaiming task\u2026";
|
|
645
|
+
if (name === `${this.mcpToolPrefix}complete_task`) return "Completing task\u2026";
|
|
587
646
|
if (name.startsWith(this.mcpToolPrefix)) return "";
|
|
588
647
|
if (name === "shell" || name === "command_execution") return "Running command\u2026";
|
|
589
648
|
if (name === "file_change") return "Editing file\u2026";
|
|
@@ -608,6 +667,15 @@ var CodexDriver = class {
|
|
|
608
667
|
return input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
609
668
|
}
|
|
610
669
|
if (name === `${this.mcpToolPrefix}read_history`) return input.channel || "";
|
|
670
|
+
if (name === `${this.mcpToolPrefix}list_tasks`) return input.channel || "";
|
|
671
|
+
if (name === `${this.mcpToolPrefix}create_tasks`) return input.channel || "";
|
|
672
|
+
if (name === `${this.mcpToolPrefix}claim_tasks`) {
|
|
673
|
+
const nums = input.task_numbers;
|
|
674
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
675
|
+
}
|
|
676
|
+
if (name === `${this.mcpToolPrefix}unclaim_task` || name === `${this.mcpToolPrefix}complete_task`) {
|
|
677
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
678
|
+
}
|
|
611
679
|
return "";
|
|
612
680
|
} catch {
|
|
613
681
|
return "";
|