@slock-ai/daemon 0.34.0 → 0.35.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 +87 -35
- package/dist/{chunk-ZXIMDHR7.js → chunk-OSHFDFBV.js} +716 -67
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -35,6 +35,68 @@ function bridgeFetch(url, init = {}) {
|
|
|
35
35
|
const requestInit = dispatcher ? { ...init, dispatcher } : init;
|
|
36
36
|
return fetch(url, requestInit);
|
|
37
37
|
}
|
|
38
|
+
function formatAttachmentSuffix(attachments) {
|
|
39
|
+
if (!attachments?.length) return "";
|
|
40
|
+
return ` [${attachments.length} attachment${attachments.length > 1 ? "s" : ""}: ${attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to download]`;
|
|
41
|
+
}
|
|
42
|
+
function guessMimeTypeFromFilename(filename) {
|
|
43
|
+
const ext = filename.includes(".") ? filename.slice(filename.lastIndexOf(".")).toLowerCase() : "";
|
|
44
|
+
const mimeMap = {
|
|
45
|
+
".jpg": "image/jpeg",
|
|
46
|
+
".jpeg": "image/jpeg",
|
|
47
|
+
".png": "image/png",
|
|
48
|
+
".gif": "image/gif",
|
|
49
|
+
".webp": "image/webp",
|
|
50
|
+
".pdf": "application/pdf",
|
|
51
|
+
".txt": "text/plain",
|
|
52
|
+
".md": "text/markdown",
|
|
53
|
+
".json": "application/json",
|
|
54
|
+
".csv": "text/csv",
|
|
55
|
+
".zip": "application/zip",
|
|
56
|
+
".tar": "application/x-tar",
|
|
57
|
+
".gz": "application/gzip",
|
|
58
|
+
".mp3": "audio/mpeg",
|
|
59
|
+
".wav": "audio/wav",
|
|
60
|
+
".mp4": "video/mp4",
|
|
61
|
+
".mov": "video/quicktime",
|
|
62
|
+
".doc": "application/msword",
|
|
63
|
+
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
64
|
+
".xls": "application/vnd.ms-excel",
|
|
65
|
+
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
66
|
+
".ppt": "application/vnd.ms-powerpoint",
|
|
67
|
+
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation"
|
|
68
|
+
};
|
|
69
|
+
return mimeMap[ext] || "application/octet-stream";
|
|
70
|
+
}
|
|
71
|
+
function parseFilenameFromContentDisposition(contentDisposition) {
|
|
72
|
+
if (!contentDisposition) return null;
|
|
73
|
+
const utf8Match = contentDisposition.match(/filename\*\s*=\s*UTF-8''([^;]+)/i);
|
|
74
|
+
if (utf8Match?.[1]) return decodeURIComponent(utf8Match[1]);
|
|
75
|
+
const plainMatch = contentDisposition.match(/filename\s*=\s*"([^"]+)"/i) || contentDisposition.match(/filename\s*=\s*([^;]+)/i);
|
|
76
|
+
return plainMatch?.[1] ? plainMatch[1].trim() : null;
|
|
77
|
+
}
|
|
78
|
+
function extensionForContentType(contentType) {
|
|
79
|
+
const normalized = contentType.split(";")[0]?.trim().toLowerCase();
|
|
80
|
+
const extMap = {
|
|
81
|
+
"image/jpeg": ".jpg",
|
|
82
|
+
"image/png": ".png",
|
|
83
|
+
"image/gif": ".gif",
|
|
84
|
+
"image/webp": ".webp",
|
|
85
|
+
"application/pdf": ".pdf",
|
|
86
|
+
"text/plain": ".txt",
|
|
87
|
+
"text/markdown": ".md",
|
|
88
|
+
"application/json": ".json",
|
|
89
|
+
"text/csv": ".csv",
|
|
90
|
+
"application/zip": ".zip",
|
|
91
|
+
"application/x-tar": ".tar",
|
|
92
|
+
"application/gzip": ".gz",
|
|
93
|
+
"audio/mpeg": ".mp3",
|
|
94
|
+
"audio/wav": ".wav",
|
|
95
|
+
"video/mp4": ".mp4",
|
|
96
|
+
"video/quicktime": ".mov"
|
|
97
|
+
};
|
|
98
|
+
return extMap[normalized] || ".bin";
|
|
99
|
+
}
|
|
38
100
|
function formatTarget(m) {
|
|
39
101
|
if (m.channel_type === "thread" && m.parent_channel_name) {
|
|
40
102
|
const shortId = m.channel_name.startsWith("thread-") ? m.channel_name.slice(7) : m.channel_name;
|
|
@@ -61,6 +123,11 @@ function formatSearchTarget(result) {
|
|
|
61
123
|
}
|
|
62
124
|
return `#${result.channelName}`;
|
|
63
125
|
}
|
|
126
|
+
function formatSenderHandle(message) {
|
|
127
|
+
const senderName = message.sender_name ?? message.senderName ?? "unknown";
|
|
128
|
+
const senderDescription = message.sender_description ?? message.senderDescription ?? null;
|
|
129
|
+
return senderDescription ? `@${senderName} \u2014 ${senderDescription}` : `@${senderName}`;
|
|
130
|
+
}
|
|
64
131
|
var server = new McpServer({
|
|
65
132
|
name: "chat",
|
|
66
133
|
version: "1.0.0"
|
|
@@ -117,9 +184,9 @@ ${formatMessages(data.recentUnread)}`;
|
|
|
117
184
|
);
|
|
118
185
|
server.tool(
|
|
119
186
|
"upload_file",
|
|
120
|
-
"Upload
|
|
187
|
+
"Upload a file to attach to a message. Returns an attachment ID that you can pass to send_message's attachment_ids parameter. Images keep preview behavior; other files are sent as downloadable attachments. Max size: 5MB.",
|
|
121
188
|
{
|
|
122
|
-
file_path: z.string().describe("Absolute path to the
|
|
189
|
+
file_path: z.string().describe("Absolute path to the file on your local filesystem"),
|
|
123
190
|
channel: z.string().describe("The channel target where this file will be used (e.g. '#general', 'dm:@richard')")
|
|
124
191
|
},
|
|
125
192
|
async ({ file_path, channel }) => {
|
|
@@ -136,7 +203,7 @@ server.tool(
|
|
|
136
203
|
if (stat.size > 5 * 1024 * 1024) {
|
|
137
204
|
return {
|
|
138
205
|
isError: true,
|
|
139
|
-
content: [{ type: "text", text: `Error: File too large (${(stat.size / 1024 / 1024).toFixed(1)}MB). Max 5MB.` }]
|
|
206
|
+
content: [{ type: "text", text: `Error: File too large (${(stat.size / 1024 / 1024).toFixed(1)}MB). Max 5MB per file.` }]
|
|
140
207
|
};
|
|
141
208
|
}
|
|
142
209
|
const listRes = await bridgeFetch(`${serverUrl}/internal/agent/${agentId}/resolve-channel`, {
|
|
@@ -156,15 +223,7 @@ server.tool(
|
|
|
156
223
|
}
|
|
157
224
|
const fileBuffer = fs.readFileSync(file_path);
|
|
158
225
|
const filename = path.basename(file_path);
|
|
159
|
-
const
|
|
160
|
-
const mimeMap = {
|
|
161
|
-
".jpg": "image/jpeg",
|
|
162
|
-
".jpeg": "image/jpeg",
|
|
163
|
-
".png": "image/png",
|
|
164
|
-
".gif": "image/gif",
|
|
165
|
-
".webp": "image/webp"
|
|
166
|
-
};
|
|
167
|
-
const mimeType = mimeMap[ext] || "application/octet-stream";
|
|
226
|
+
const mimeType = guessMimeTypeFromFilename(filename);
|
|
168
227
|
const blob = new Blob([fileBuffer], { type: mimeType });
|
|
169
228
|
const formData = new FormData();
|
|
170
229
|
formData.append("file", blob, filename);
|
|
@@ -206,7 +265,7 @@ Use this ID in send_message's attachment_ids parameter to include it in a messag
|
|
|
206
265
|
);
|
|
207
266
|
server.tool(
|
|
208
267
|
"view_file",
|
|
209
|
-
"Download an attached
|
|
268
|
+
"Download an attached file by its attachment ID and save it locally so you can inspect it. Returns the local file path.",
|
|
210
269
|
{
|
|
211
270
|
attachment_id: z.string().describe("The attachment UUID (from the 'id:...' shown in the message)")
|
|
212
271
|
},
|
|
@@ -221,9 +280,7 @@ server.tool(
|
|
|
221
280
|
if (existing) {
|
|
222
281
|
const cachedPath = path.join(cacheDir, existing);
|
|
223
282
|
return {
|
|
224
|
-
content: [{ type: "text", text: `File already cached at: ${cachedPath}
|
|
225
|
-
|
|
226
|
-
Use your Read tool to view this image.` }]
|
|
283
|
+
content: [{ type: "text", text: `File already cached at: ${cachedPath}` }]
|
|
227
284
|
};
|
|
228
285
|
}
|
|
229
286
|
const downloadHeaders = {};
|
|
@@ -241,20 +298,13 @@ Use your Read tool to view this image.` }]
|
|
|
241
298
|
};
|
|
242
299
|
}
|
|
243
300
|
const contentType = res.headers.get("content-type") || "application/octet-stream";
|
|
244
|
-
const
|
|
245
|
-
|
|
246
|
-
"image/png": ".png",
|
|
247
|
-
"image/gif": ".gif",
|
|
248
|
-
"image/webp": ".webp"
|
|
249
|
-
};
|
|
250
|
-
const ext = extMap[contentType] || ".bin";
|
|
301
|
+
const filename = parseFilenameFromContentDisposition(res.headers.get("content-disposition"));
|
|
302
|
+
const ext = filename ? path.extname(filename) || extensionForContentType(contentType) : extensionForContentType(contentType);
|
|
251
303
|
const filePath = path.join(cacheDir, `${attachment_id}${ext}`);
|
|
252
304
|
const buffer = Buffer.from(await res.arrayBuffer());
|
|
253
305
|
fs.writeFileSync(filePath, buffer);
|
|
254
306
|
return {
|
|
255
|
-
content: [{ type: "text", text: `Downloaded to: ${filePath}
|
|
256
|
-
|
|
257
|
-
Use your Read tool to view this image.` }]
|
|
307
|
+
content: [{ type: "text", text: `Downloaded to: ${filePath}` }]
|
|
258
308
|
};
|
|
259
309
|
} catch (err) {
|
|
260
310
|
return {
|
|
@@ -298,11 +348,11 @@ function formatMessages(messages) {
|
|
|
298
348
|
const target = formatTarget(m);
|
|
299
349
|
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
300
350
|
const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
|
|
301
|
-
const senderType = m.sender_type
|
|
351
|
+
const senderType = ` type=${m.sender_type}`;
|
|
302
352
|
const renderedContent = m.content;
|
|
303
|
-
const attachSuffix = m.attachments
|
|
353
|
+
const attachSuffix = formatAttachmentSuffix(m.attachments);
|
|
304
354
|
const taskSuffix = m.task_status ? ` [task #${m.task_number} status=${m.task_status}${m.task_assignee_id ? ` assignee=${m.task_assignee_type}:${m.task_assignee_id}` : ""}]` : "";
|
|
305
|
-
return `[target=${target} msg=${msgId} time=${time}${senderType}]
|
|
355
|
+
return `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(m)}: ${renderedContent}${attachSuffix}${taskSuffix}`;
|
|
306
356
|
}).join("\n");
|
|
307
357
|
}
|
|
308
358
|
server.tool(
|
|
@@ -333,7 +383,8 @@ server.tool(
|
|
|
333
383
|
text += "Other AI agents in this server.\n";
|
|
334
384
|
if (data.agents?.length > 0) {
|
|
335
385
|
for (const a of data.agents) {
|
|
336
|
-
text += ` - @${a.name} (${a.status})
|
|
386
|
+
text += a.description ? ` - @${a.name} (${a.status}) \u2014 ${a.description}
|
|
387
|
+
` : ` - @${a.name} (${a.status})
|
|
337
388
|
`;
|
|
338
389
|
}
|
|
339
390
|
} else {
|
|
@@ -343,7 +394,8 @@ server.tool(
|
|
|
343
394
|
text += 'To start a new DM: send_message(target="dm:@name"). To reply in an existing DM: reuse the target from received messages.\n';
|
|
344
395
|
if (data.humans?.length > 0) {
|
|
345
396
|
for (const u of data.humans) {
|
|
346
|
-
text += ` - @${u.name}
|
|
397
|
+
text += u.description ? ` - @${u.name} \u2014 ${u.description}
|
|
398
|
+
` : ` - @${u.name}
|
|
347
399
|
`;
|
|
348
400
|
}
|
|
349
401
|
} else {
|
|
@@ -408,7 +460,7 @@ thread: ${result.parentChannelName} -> ${target}` : "";
|
|
|
408
460
|
return [
|
|
409
461
|
`[${index + 1}] msg=${result.id} seq=${result.seq} time=${toLocalTime(result.createdAt)}`,
|
|
410
462
|
`target: ${target}${threadInfo}`,
|
|
411
|
-
`sender: @${result.senderName}${result.senderType
|
|
463
|
+
`sender: @${result.senderName} (${result.senderType})`,
|
|
412
464
|
`content: ${result.content}`,
|
|
413
465
|
`match: ${result.snippet}`,
|
|
414
466
|
`next: read_history(channel="${target}", around="${result.id}", limit=20)`
|
|
@@ -468,13 +520,13 @@ server.tool(
|
|
|
468
520
|
};
|
|
469
521
|
}
|
|
470
522
|
const formatted = data.messages.map((m) => {
|
|
471
|
-
const senderType = m.senderType
|
|
523
|
+
const senderType = ` type=${m.senderType}`;
|
|
472
524
|
const time = m.createdAt ? toLocalTime(m.createdAt) : "-";
|
|
473
525
|
const msgId = m.id || "-";
|
|
474
526
|
const renderedContent = m.content;
|
|
475
|
-
const attachSuffix = m.attachments
|
|
527
|
+
const attachSuffix = formatAttachmentSuffix(m.attachments);
|
|
476
528
|
const taskSuffix = m.taskStatus ? ` [task #${m.taskNumber} status=${m.taskStatus}${m.taskAssigneeId ? ` assignee=${m.taskAssigneeType}:${m.taskAssigneeId}` : ""}]` : "";
|
|
477
|
-
return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}]
|
|
529
|
+
return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(m)}: ${renderedContent}${attachSuffix}${taskSuffix}`;
|
|
478
530
|
}).join("\n");
|
|
479
531
|
let footer = "";
|
|
480
532
|
if (data.historyLimited) {
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
} from "./chunk-GX2DVINN.js";
|
|
4
4
|
|
|
5
5
|
// src/core.ts
|
|
6
|
-
import
|
|
7
|
-
import
|
|
6
|
+
import path9 from "path";
|
|
7
|
+
import os4 from "os";
|
|
8
8
|
import { createRequire } from "module";
|
|
9
9
|
import { execSync as execSync2 } from "child_process";
|
|
10
10
|
import { accessSync } from "fs";
|
|
@@ -38,7 +38,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
38
38
|
manageAgents: true,
|
|
39
39
|
manageMachines: true,
|
|
40
40
|
manageMembers: true,
|
|
41
|
-
changeMemberRoles:
|
|
41
|
+
changeMemberRoles: true,
|
|
42
42
|
manageBilling: false,
|
|
43
43
|
joinPublicChannels: true
|
|
44
44
|
}),
|
|
@@ -58,8 +58,10 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
58
58
|
var RUNTIMES = [
|
|
59
59
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
60
60
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
61
|
-
{ id: "
|
|
62
|
-
{ id: "
|
|
61
|
+
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
62
|
+
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
63
|
+
{ id: "cursor", displayName: "Cursor CLI", binary: "agent", supported: true },
|
|
64
|
+
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true }
|
|
63
65
|
];
|
|
64
66
|
var PLAN_CONFIG = {
|
|
65
67
|
free: {
|
|
@@ -97,8 +99,8 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
97
99
|
|
|
98
100
|
// src/agentProcessManager.ts
|
|
99
101
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
100
|
-
import
|
|
101
|
-
import
|
|
102
|
+
import path8 from "path";
|
|
103
|
+
import os3 from "os";
|
|
102
104
|
|
|
103
105
|
// src/drivers/claude.ts
|
|
104
106
|
import { spawn } from "child_process";
|
|
@@ -145,8 +147,8 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
145
147
|
8. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
|
|
146
148
|
9. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
147
149
|
10. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
148
|
-
11. **${t("upload_file")}** \u2014 Upload
|
|
149
|
-
12. **${t("view_file")}** \u2014 Download an attached
|
|
150
|
+
11. **${t("upload_file")}** \u2014 Upload a file to attach to a message. Returns an attachment ID to pass to send_message.
|
|
151
|
+
12. **${t("view_file")}** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
150
152
|
|
|
151
153
|
CRITICAL RULES:
|
|
152
154
|
${criticalRules.join("\n")}
|
|
@@ -166,18 +168,18 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
166
168
|
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
167
169
|
|
|
168
170
|
\`\`\`
|
|
169
|
-
[target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00] @richard: hello everyone
|
|
171
|
+
[target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00 type=human] @richard: hello everyone
|
|
170
172
|
[target=#general msg=e5f6a7b8 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
|
|
171
|
-
[target=dm:@richard msg=c9d0e1f2 time=2026-03-15T01:00:02] @richard: hey, can you help?
|
|
172
|
-
[target=#general:a1b2c3d4 msg=f3a4b5c6 time=2026-03-15T01:00:03] @richard: thread reply
|
|
173
|
-
[target=dm:@richard:x9y8z7a0 msg=d7e8f9a0 time=2026-03-15T01:00:04] @richard: DM thread reply
|
|
173
|
+
[target=dm:@richard msg=c9d0e1f2 time=2026-03-15T01:00:02 type=human] @richard: hey, can you help?
|
|
174
|
+
[target=#general:a1b2c3d4 msg=f3a4b5c6 time=2026-03-15T01:00:03 type=human] @richard: thread reply
|
|
175
|
+
[target=dm:@richard:x9y8z7a0 msg=d7e8f9a0 time=2026-03-15T01:00:04 type=human] @richard: DM thread reply
|
|
174
176
|
\`\`\`
|
|
175
177
|
|
|
176
178
|
Header fields:
|
|
177
179
|
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
178
180
|
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
179
181
|
- \`time=\` \u2014 timestamp.
|
|
180
|
-
- \`type
|
|
182
|
+
- \`type=\` \u2014 sender kind. Values are \`human\`, \`agent\`, or \`system\`.
|
|
181
183
|
|
|
182
184
|
### Sending messages
|
|
183
185
|
|
|
@@ -408,6 +410,7 @@ var ClaudeDriver = class {
|
|
|
408
410
|
id = "claude";
|
|
409
411
|
supportsStdinNotification = true;
|
|
410
412
|
mcpToolPrefix = "mcp__chat__";
|
|
413
|
+
busyDeliveryMode = "notification";
|
|
411
414
|
spawn(ctx) {
|
|
412
415
|
const mcpArgs = [
|
|
413
416
|
ctx.chatBridgePath,
|
|
@@ -630,8 +633,20 @@ var ClaudeDriver = class {
|
|
|
630
633
|
|
|
631
634
|
// src/drivers/codex.ts
|
|
632
635
|
import { spawn as spawn2, execSync } from "child_process";
|
|
633
|
-
import { existsSync } from "fs";
|
|
636
|
+
import { existsSync, readFileSync } from "fs";
|
|
637
|
+
import os from "os";
|
|
634
638
|
import path2 from "path";
|
|
639
|
+
function getCodexNotificationErrorMessage(params) {
|
|
640
|
+
const topLevelMessage = params?.message;
|
|
641
|
+
if (typeof topLevelMessage === "string" && topLevelMessage.trim()) {
|
|
642
|
+
return topLevelMessage;
|
|
643
|
+
}
|
|
644
|
+
const nestedMessage = params?.error?.message;
|
|
645
|
+
if (typeof nestedMessage === "string" && nestedMessage.trim()) {
|
|
646
|
+
return nestedMessage;
|
|
647
|
+
}
|
|
648
|
+
return null;
|
|
649
|
+
}
|
|
635
650
|
function ensureGitRepo(workingDirectory) {
|
|
636
651
|
const gitDir = path2.join(workingDirectory, ".git");
|
|
637
652
|
if (existsSync(gitDir)) return;
|
|
@@ -692,7 +707,7 @@ var CodexDriver = class {
|
|
|
692
707
|
id = "codex";
|
|
693
708
|
supportsStdinNotification = true;
|
|
694
709
|
mcpToolPrefix = "mcp_chat_";
|
|
695
|
-
|
|
710
|
+
busyDeliveryMode = "direct";
|
|
696
711
|
process = null;
|
|
697
712
|
requestId = 0;
|
|
698
713
|
threadId = null;
|
|
@@ -931,7 +946,10 @@ var CodexDriver = class {
|
|
|
931
946
|
break;
|
|
932
947
|
}
|
|
933
948
|
case "error":
|
|
934
|
-
events.push({
|
|
949
|
+
events.push({
|
|
950
|
+
kind: "error",
|
|
951
|
+
message: getCodexNotificationErrorMessage(message.params) || "Unknown Codex app-server error"
|
|
952
|
+
});
|
|
935
953
|
break;
|
|
936
954
|
}
|
|
937
955
|
return events;
|
|
@@ -1072,13 +1090,564 @@ var CodexDriver = class {
|
|
|
1072
1090
|
params
|
|
1073
1091
|
}) + "\n");
|
|
1074
1092
|
}
|
|
1093
|
+
async detectModels() {
|
|
1094
|
+
return detectCodexModels();
|
|
1095
|
+
}
|
|
1075
1096
|
};
|
|
1097
|
+
function detectCodexModels(home = os.homedir()) {
|
|
1098
|
+
const cachePath = path2.join(home, ".codex", "models_cache.json");
|
|
1099
|
+
const configPath = path2.join(home, ".codex", "config.toml");
|
|
1100
|
+
let models = [];
|
|
1101
|
+
try {
|
|
1102
|
+
const raw = readFileSync(cachePath, "utf8");
|
|
1103
|
+
const parsed = JSON.parse(raw);
|
|
1104
|
+
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
1105
|
+
for (const entry of entries) {
|
|
1106
|
+
const slug = typeof entry?.slug === "string" ? entry.slug : null;
|
|
1107
|
+
if (!slug) continue;
|
|
1108
|
+
if (entry?.visibility && entry.visibility !== "public") continue;
|
|
1109
|
+
if (entry?.supported_in_api === false) continue;
|
|
1110
|
+
const label = typeof entry?.display_name === "string" && entry.display_name.length > 0 ? entry.display_name : slug;
|
|
1111
|
+
models.push({ id: slug, label });
|
|
1112
|
+
}
|
|
1113
|
+
} catch {
|
|
1114
|
+
return null;
|
|
1115
|
+
}
|
|
1116
|
+
if (models.length === 0) return null;
|
|
1117
|
+
let defaultModel;
|
|
1118
|
+
try {
|
|
1119
|
+
const raw = readFileSync(configPath, "utf8");
|
|
1120
|
+
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
1121
|
+
if (match) defaultModel = match[1];
|
|
1122
|
+
} catch {
|
|
1123
|
+
}
|
|
1124
|
+
return { models, default: defaultModel };
|
|
1125
|
+
}
|
|
1076
1126
|
|
|
1077
|
-
// src/drivers/
|
|
1078
|
-
import { randomUUID } from "crypto";
|
|
1127
|
+
// src/drivers/copilot.ts
|
|
1079
1128
|
import { spawn as spawn3 } from "child_process";
|
|
1080
|
-
import { existsSync as existsSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1081
1129
|
import path3 from "path";
|
|
1130
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
1131
|
+
var CopilotDriver = class {
|
|
1132
|
+
id = "copilot";
|
|
1133
|
+
supportsStdinNotification = false;
|
|
1134
|
+
mcpToolPrefix = "";
|
|
1135
|
+
busyDeliveryMode = "none";
|
|
1136
|
+
sessionId = null;
|
|
1137
|
+
sessionAnnounced = false;
|
|
1138
|
+
spawn(ctx) {
|
|
1139
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
1140
|
+
this.sessionAnnounced = false;
|
|
1141
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1142
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1143
|
+
const mcpArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
|
|
1144
|
+
const mcpConfigPath = path3.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
|
|
1145
|
+
writeFileSync2(mcpConfigPath, JSON.stringify({
|
|
1146
|
+
mcpServers: {
|
|
1147
|
+
chat: {
|
|
1148
|
+
command: mcpCommand,
|
|
1149
|
+
args: mcpArgs
|
|
1150
|
+
}
|
|
1151
|
+
}
|
|
1152
|
+
}), "utf8");
|
|
1153
|
+
const args = [
|
|
1154
|
+
"--output-format",
|
|
1155
|
+
"json",
|
|
1156
|
+
"--allow-all-tools",
|
|
1157
|
+
"--allow-all-paths",
|
|
1158
|
+
"--additional-mcp-config",
|
|
1159
|
+
`@${mcpConfigPath}`,
|
|
1160
|
+
"-p",
|
|
1161
|
+
ctx.prompt
|
|
1162
|
+
];
|
|
1163
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1164
|
+
args.push("--model", ctx.config.model);
|
|
1165
|
+
}
|
|
1166
|
+
if (ctx.config.reasoningEffort) {
|
|
1167
|
+
args.push("--effort", ctx.config.reasoningEffort);
|
|
1168
|
+
}
|
|
1169
|
+
if (ctx.config.sessionId) {
|
|
1170
|
+
args.push(`--resume=${ctx.config.sessionId}`);
|
|
1171
|
+
}
|
|
1172
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1173
|
+
const proc = spawn3("copilot", args, {
|
|
1174
|
+
cwd: ctx.workingDirectory,
|
|
1175
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1176
|
+
env: spawnEnv,
|
|
1177
|
+
shell: process.platform === "win32"
|
|
1178
|
+
});
|
|
1179
|
+
return { process: proc };
|
|
1180
|
+
}
|
|
1181
|
+
parseLine(line) {
|
|
1182
|
+
let event;
|
|
1183
|
+
try {
|
|
1184
|
+
event = JSON.parse(line);
|
|
1185
|
+
} catch {
|
|
1186
|
+
return [];
|
|
1187
|
+
}
|
|
1188
|
+
const events = [];
|
|
1189
|
+
const eventType = event.type;
|
|
1190
|
+
const data = event.data || {};
|
|
1191
|
+
if (event.ephemeral && eventType?.startsWith("session.")) {
|
|
1192
|
+
return [];
|
|
1193
|
+
}
|
|
1194
|
+
switch (eventType) {
|
|
1195
|
+
case "assistant.turn_start":
|
|
1196
|
+
if (!this.sessionAnnounced && data.sessionId) {
|
|
1197
|
+
this.sessionId = data.sessionId;
|
|
1198
|
+
events.push({ kind: "session_init", sessionId: data.sessionId });
|
|
1199
|
+
this.sessionAnnounced = true;
|
|
1200
|
+
}
|
|
1201
|
+
events.push({ kind: "thinking", text: "" });
|
|
1202
|
+
break;
|
|
1203
|
+
case "assistant.reasoning":
|
|
1204
|
+
if (data.content) {
|
|
1205
|
+
events.push({ kind: "thinking", text: data.content });
|
|
1206
|
+
}
|
|
1207
|
+
break;
|
|
1208
|
+
case "assistant.message_delta":
|
|
1209
|
+
if (data.deltaContent) {
|
|
1210
|
+
events.push({ kind: "text", text: data.deltaContent });
|
|
1211
|
+
}
|
|
1212
|
+
break;
|
|
1213
|
+
case "assistant.message": {
|
|
1214
|
+
if (Array.isArray(data.toolRequests)) {
|
|
1215
|
+
for (const req of data.toolRequests) {
|
|
1216
|
+
events.push({
|
|
1217
|
+
kind: "tool_call",
|
|
1218
|
+
name: req.name || req.toolName || "unknown_tool",
|
|
1219
|
+
input: req.arguments || req.parameters || req.input || {}
|
|
1220
|
+
});
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
if (!event.ephemeral && data.content && typeof data.content === "string") {
|
|
1224
|
+
if (!Array.isArray(data.toolRequests) || data.toolRequests.length === 0) {
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
break;
|
|
1228
|
+
}
|
|
1229
|
+
case "assistant.turn_end":
|
|
1230
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1231
|
+
break;
|
|
1232
|
+
case "result": {
|
|
1233
|
+
const resultSessionId = event.sessionId || data.sessionId;
|
|
1234
|
+
const exitCode = event.exitCode ?? data.exitCode;
|
|
1235
|
+
if (!this.sessionAnnounced && resultSessionId) {
|
|
1236
|
+
this.sessionId = resultSessionId;
|
|
1237
|
+
events.push({ kind: "session_init", sessionId: resultSessionId });
|
|
1238
|
+
this.sessionAnnounced = true;
|
|
1239
|
+
}
|
|
1240
|
+
if (exitCode && exitCode !== 0) {
|
|
1241
|
+
events.push({ kind: "error", message: `Copilot exited with code ${exitCode}` });
|
|
1242
|
+
}
|
|
1243
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1244
|
+
break;
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
return events;
|
|
1248
|
+
}
|
|
1249
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1250
|
+
return null;
|
|
1251
|
+
}
|
|
1252
|
+
buildSystemPrompt(config, _agentId) {
|
|
1253
|
+
return buildBaseSystemPrompt(config, {
|
|
1254
|
+
toolPrefix: "",
|
|
1255
|
+
extraCriticalRules: [
|
|
1256
|
+
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
1257
|
+
],
|
|
1258
|
+
postStartupNotes: [],
|
|
1259
|
+
includeStdinNotificationSection: false,
|
|
1260
|
+
messageNotificationStyle: "poll"
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
toolDisplayName(name) {
|
|
1264
|
+
if (name === "list_tasks") return "Viewing task board\u2026";
|
|
1265
|
+
if (name === "create_tasks") return "Creating tasks\u2026";
|
|
1266
|
+
if (name === "claim_tasks") return "Claiming tasks\u2026";
|
|
1267
|
+
if (name === "unclaim_task") return "Unclaiming task\u2026";
|
|
1268
|
+
if (name === "update_task_status") return "Updating task\u2026";
|
|
1269
|
+
if (name === "send_message" || name === "receive_message" || name === "read_history" || name === "list_server") return "";
|
|
1270
|
+
if (name === "shell" || name === "Shell") return "Running command\u2026";
|
|
1271
|
+
if (name === "read_file" || name === "ReadFile") return "Reading file\u2026";
|
|
1272
|
+
if (name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") return "Editing file\u2026";
|
|
1273
|
+
if (name === "search_files" || name === "Glob" || name === "Grep") return "Searching code\u2026";
|
|
1274
|
+
if (name === "web_search" || name === "SearchWeb") return "Searching web\u2026";
|
|
1275
|
+
if (name === "fetch_url" || name === "FetchURL") return "Fetching web\u2026";
|
|
1276
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1277
|
+
}
|
|
1278
|
+
summarizeToolInput(name, input) {
|
|
1279
|
+
if (!input || typeof input !== "object") return "";
|
|
1280
|
+
try {
|
|
1281
|
+
if (name === "shell" || name === "Shell") {
|
|
1282
|
+
const cmd = input.command || "";
|
|
1283
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1284
|
+
}
|
|
1285
|
+
if (name === "read_file" || name === "ReadFile" || name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") {
|
|
1286
|
+
return input.path || input.file_path || "";
|
|
1287
|
+
}
|
|
1288
|
+
if (name === "Glob" || name === "Grep" || name === "search_files") return input.pattern || input.query || "";
|
|
1289
|
+
if (name === "web_search" || name === "SearchWeb") return input.query || "";
|
|
1290
|
+
if (name === "fetch_url" || name === "FetchURL") return input.url || "";
|
|
1291
|
+
if (name === "send_message") return input.target || input.channel || "";
|
|
1292
|
+
if (name === "read_history") return input.target || input.channel || "";
|
|
1293
|
+
if (name === "list_tasks") return input.channel || "";
|
|
1294
|
+
if (name === "create_tasks") return input.channel || "";
|
|
1295
|
+
if (name === "claim_tasks") {
|
|
1296
|
+
const nums = input.task_numbers;
|
|
1297
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1298
|
+
}
|
|
1299
|
+
if (name === "unclaim_task" || name === "update_task_status") {
|
|
1300
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
1301
|
+
}
|
|
1302
|
+
return "";
|
|
1303
|
+
} catch {
|
|
1304
|
+
return "";
|
|
1305
|
+
}
|
|
1306
|
+
}
|
|
1307
|
+
};
|
|
1308
|
+
|
|
1309
|
+
// src/drivers/cursor.ts
|
|
1310
|
+
import { spawn as spawn4 } from "child_process";
|
|
1311
|
+
import { writeFileSync as writeFileSync3, mkdirSync, existsSync as existsSync2 } from "fs";
|
|
1312
|
+
import path4 from "path";
|
|
1313
|
+
var CursorDriver = class {
|
|
1314
|
+
id = "cursor";
|
|
1315
|
+
supportsStdinNotification = false;
|
|
1316
|
+
mcpToolPrefix = "mcp__chat__";
|
|
1317
|
+
busyDeliveryMode = "none";
|
|
1318
|
+
spawn(ctx) {
|
|
1319
|
+
const cursorDir = path4.join(ctx.workingDirectory, ".cursor");
|
|
1320
|
+
if (!existsSync2(cursorDir)) {
|
|
1321
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
1322
|
+
}
|
|
1323
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1324
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1325
|
+
const mcpArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
|
|
1326
|
+
const mcpConfigPath = path4.join(cursorDir, "mcp.json");
|
|
1327
|
+
writeFileSync3(mcpConfigPath, JSON.stringify({
|
|
1328
|
+
mcpServers: {
|
|
1329
|
+
chat: {
|
|
1330
|
+
command: mcpCommand,
|
|
1331
|
+
args: mcpArgs
|
|
1332
|
+
}
|
|
1333
|
+
}
|
|
1334
|
+
}), "utf8");
|
|
1335
|
+
const args = [
|
|
1336
|
+
"--print",
|
|
1337
|
+
"--output-format",
|
|
1338
|
+
"stream-json",
|
|
1339
|
+
"--yolo",
|
|
1340
|
+
"--approve-mcps",
|
|
1341
|
+
"--trust"
|
|
1342
|
+
];
|
|
1343
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1344
|
+
args.push("--model", ctx.config.model);
|
|
1345
|
+
}
|
|
1346
|
+
if (ctx.config.sessionId) {
|
|
1347
|
+
args.push("--resume", ctx.config.sessionId);
|
|
1348
|
+
}
|
|
1349
|
+
args.push(ctx.prompt);
|
|
1350
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1351
|
+
const proc = spawn4("agent", args, {
|
|
1352
|
+
cwd: ctx.workingDirectory,
|
|
1353
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1354
|
+
env: spawnEnv,
|
|
1355
|
+
shell: process.platform === "win32"
|
|
1356
|
+
});
|
|
1357
|
+
return { process: proc };
|
|
1358
|
+
}
|
|
1359
|
+
parseLine(line) {
|
|
1360
|
+
let event;
|
|
1361
|
+
try {
|
|
1362
|
+
event = JSON.parse(line);
|
|
1363
|
+
} catch {
|
|
1364
|
+
return [];
|
|
1365
|
+
}
|
|
1366
|
+
const events = [];
|
|
1367
|
+
switch (event.type) {
|
|
1368
|
+
case "system":
|
|
1369
|
+
if (event.subtype === "init" && event.session_id) {
|
|
1370
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
1371
|
+
}
|
|
1372
|
+
break;
|
|
1373
|
+
case "assistant": {
|
|
1374
|
+
const content = event.message?.content;
|
|
1375
|
+
if (Array.isArray(content)) {
|
|
1376
|
+
for (const block of content) {
|
|
1377
|
+
if (block.type === "thinking" && block.thinking) {
|
|
1378
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
1379
|
+
} else if (block.type === "text" && block.text) {
|
|
1380
|
+
events.push({ kind: "text", text: block.text });
|
|
1381
|
+
} else if (block.type === "tool_use") {
|
|
1382
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
}
|
|
1386
|
+
break;
|
|
1387
|
+
}
|
|
1388
|
+
case "result": {
|
|
1389
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
1390
|
+
if (subtype !== "success" || event.is_error) {
|
|
1391
|
+
const parts = [];
|
|
1392
|
+
if (Array.isArray(event.errors)) {
|
|
1393
|
+
for (const err of event.errors) {
|
|
1394
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
if (typeof event.result === "string" && event.result.trim()) {
|
|
1398
|
+
parts.push(event.result.trim());
|
|
1399
|
+
}
|
|
1400
|
+
const detail = parts.join(" | ") || "Execution failed";
|
|
1401
|
+
events.push({ kind: "error", message: detail });
|
|
1402
|
+
}
|
|
1403
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
1404
|
+
break;
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
return events;
|
|
1408
|
+
}
|
|
1409
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1410
|
+
return null;
|
|
1411
|
+
}
|
|
1412
|
+
buildSystemPrompt(config, _agentId) {
|
|
1413
|
+
return buildBaseSystemPrompt(config, {
|
|
1414
|
+
toolPrefix: "mcp__chat__",
|
|
1415
|
+
extraCriticalRules: [
|
|
1416
|
+
"- Do NOT use bash/curl/sqlite to send or receive messages. The MCP tools handle everything."
|
|
1417
|
+
],
|
|
1418
|
+
postStartupNotes: [],
|
|
1419
|
+
includeStdinNotificationSection: false,
|
|
1420
|
+
messageNotificationStyle: "poll"
|
|
1421
|
+
});
|
|
1422
|
+
}
|
|
1423
|
+
toolDisplayName(name) {
|
|
1424
|
+
if (name === "mcp__chat__upload_file") return "Uploading file\u2026";
|
|
1425
|
+
if (name === "mcp__chat__view_file") return "Viewing file\u2026";
|
|
1426
|
+
if (name === "mcp__chat__list_tasks") return "Listing tasks\u2026";
|
|
1427
|
+
if (name === "mcp__chat__create_tasks") return "Creating tasks\u2026";
|
|
1428
|
+
if (name === "mcp__chat__claim_tasks") return "Claiming tasks\u2026";
|
|
1429
|
+
if (name === "mcp__chat__unclaim_task") return "Unclaiming task\u2026";
|
|
1430
|
+
if (name === "mcp__chat__update_task_status") return "Updating task status\u2026";
|
|
1431
|
+
if (name === "mcp__chat__list_server") return "Listing server\u2026";
|
|
1432
|
+
if (name === "mcp__chat__read_history") return "Reading history\u2026";
|
|
1433
|
+
if (name === "mcp__chat__search_messages") return "Searching messages\u2026";
|
|
1434
|
+
if (name === "mcp__chat__check_messages") return "Checking messages\u2026";
|
|
1435
|
+
if (name.startsWith("mcp__chat__")) return "";
|
|
1436
|
+
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
1437
|
+
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
1438
|
+
if (name === "Edit" || name === "edit_file") return "Editing file\u2026";
|
|
1439
|
+
if (name === "Bash" || name === "bash") return "Running command\u2026";
|
|
1440
|
+
if (name === "Glob" || name === "glob") return "Searching files\u2026";
|
|
1441
|
+
if (name === "Grep" || name === "grep") return "Searching code\u2026";
|
|
1442
|
+
if (name === "WebFetch" || name === "web_fetch") return "Fetching web\u2026";
|
|
1443
|
+
if (name === "WebSearch" || name === "web_search") return "Searching web\u2026";
|
|
1444
|
+
if (name === "TodoWrite") return "Updating tasks\u2026";
|
|
1445
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1446
|
+
}
|
|
1447
|
+
summarizeToolInput(name, input) {
|
|
1448
|
+
if (!input || typeof input !== "object") return "";
|
|
1449
|
+
try {
|
|
1450
|
+
if (name === "Read" || name === "read_file") return input.file_path || input.path || "";
|
|
1451
|
+
if (name === "Write" || name === "write_file") return input.file_path || input.path || "";
|
|
1452
|
+
if (name === "Edit" || name === "edit_file") return input.file_path || input.path || "";
|
|
1453
|
+
if (name === "Bash" || name === "bash") {
|
|
1454
|
+
const cmd = input.command || "";
|
|
1455
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1456
|
+
}
|
|
1457
|
+
if (name === "Glob" || name === "glob") return input.pattern || "";
|
|
1458
|
+
if (name === "Grep" || name === "grep") return input.pattern || "";
|
|
1459
|
+
if (name === "WebFetch" || name === "web_fetch") return input.url || "";
|
|
1460
|
+
if (name === "WebSearch" || name === "web_search") return input.query || "";
|
|
1461
|
+
if (name === "mcp__chat__send_message") {
|
|
1462
|
+
return input.target || input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
1463
|
+
}
|
|
1464
|
+
if (name === "mcp__chat__read_history") return input.target || input.channel || "";
|
|
1465
|
+
if (name === "mcp__chat__search_messages") return input.query || "";
|
|
1466
|
+
if (name === "mcp__chat__list_tasks") return input.channel || "";
|
|
1467
|
+
if (name === "mcp__chat__create_tasks") return input.channel || "";
|
|
1468
|
+
if (name === "mcp__chat__claim_tasks") {
|
|
1469
|
+
const nums = input.task_numbers;
|
|
1470
|
+
return input.channel ? `${input.channel} #${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1471
|
+
}
|
|
1472
|
+
if (name === "mcp__chat__unclaim_task") {
|
|
1473
|
+
return input.channel ? `${input.channel} #${input.task_number}` : "";
|
|
1474
|
+
}
|
|
1475
|
+
if (name === "mcp__chat__update_task_status") {
|
|
1476
|
+
return input.channel ? `${input.channel} #${input.task_number}` : "";
|
|
1477
|
+
}
|
|
1478
|
+
if (name === "mcp__chat__upload_file") return input.file_path || "";
|
|
1479
|
+
return "";
|
|
1480
|
+
} catch {
|
|
1481
|
+
return "";
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
|
|
1486
|
+
// src/drivers/gemini.ts
|
|
1487
|
+
import { spawn as spawn5 } from "child_process";
|
|
1488
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync3 } from "fs";
|
|
1489
|
+
import path5 from "path";
|
|
1490
|
+
var GeminiDriver = class {
|
|
1491
|
+
id = "gemini";
|
|
1492
|
+
supportsStdinNotification = false;
|
|
1493
|
+
mcpToolPrefix = "";
|
|
1494
|
+
busyDeliveryMode = "none";
|
|
1495
|
+
sessionId = null;
|
|
1496
|
+
sessionAnnounced = false;
|
|
1497
|
+
spawn(ctx) {
|
|
1498
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
1499
|
+
this.sessionAnnounced = false;
|
|
1500
|
+
const geminiDir = path5.join(ctx.workingDirectory, ".gemini");
|
|
1501
|
+
if (!existsSync3(geminiDir)) {
|
|
1502
|
+
mkdirSync2(geminiDir, { recursive: true });
|
|
1503
|
+
}
|
|
1504
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1505
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1506
|
+
const mcpArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
|
|
1507
|
+
const settingsPath = path5.join(geminiDir, "settings.json");
|
|
1508
|
+
writeFileSync4(settingsPath, JSON.stringify({
|
|
1509
|
+
mcpServers: {
|
|
1510
|
+
chat: {
|
|
1511
|
+
command: mcpCommand,
|
|
1512
|
+
args: mcpArgs
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
}), "utf8");
|
|
1516
|
+
const args = [
|
|
1517
|
+
"--output-format",
|
|
1518
|
+
"stream-json",
|
|
1519
|
+
"--yolo",
|
|
1520
|
+
"-p",
|
|
1521
|
+
ctx.prompt
|
|
1522
|
+
];
|
|
1523
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1524
|
+
args.push("--model", ctx.config.model);
|
|
1525
|
+
}
|
|
1526
|
+
if (ctx.config.sessionId) {
|
|
1527
|
+
args.push("--resume", ctx.config.sessionId);
|
|
1528
|
+
}
|
|
1529
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1530
|
+
const proc = spawn5("gemini", args, {
|
|
1531
|
+
cwd: ctx.workingDirectory,
|
|
1532
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1533
|
+
env: spawnEnv,
|
|
1534
|
+
shell: process.platform === "win32"
|
|
1535
|
+
});
|
|
1536
|
+
return { process: proc };
|
|
1537
|
+
}
|
|
1538
|
+
parseLine(line) {
|
|
1539
|
+
let event;
|
|
1540
|
+
try {
|
|
1541
|
+
event = JSON.parse(line);
|
|
1542
|
+
} catch {
|
|
1543
|
+
return [];
|
|
1544
|
+
}
|
|
1545
|
+
const events = [];
|
|
1546
|
+
switch (event.type) {
|
|
1547
|
+
case "init":
|
|
1548
|
+
if (event.session_id) {
|
|
1549
|
+
this.sessionId = event.session_id;
|
|
1550
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
1551
|
+
this.sessionAnnounced = true;
|
|
1552
|
+
}
|
|
1553
|
+
break;
|
|
1554
|
+
case "message":
|
|
1555
|
+
if (event.role === "assistant" && event.content) {
|
|
1556
|
+
if (event.delta) {
|
|
1557
|
+
events.push({ kind: "text", text: event.content });
|
|
1558
|
+
} else {
|
|
1559
|
+
events.push({ kind: "text", text: event.content });
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
break;
|
|
1563
|
+
case "tool_use":
|
|
1564
|
+
events.push({
|
|
1565
|
+
kind: "tool_call",
|
|
1566
|
+
name: event.tool_name || "unknown_tool",
|
|
1567
|
+
input: event.parameters
|
|
1568
|
+
});
|
|
1569
|
+
break;
|
|
1570
|
+
case "error":
|
|
1571
|
+
events.push({ kind: "error", message: event.message || "Unknown Gemini error" });
|
|
1572
|
+
break;
|
|
1573
|
+
case "result":
|
|
1574
|
+
if (event.status !== "success") {
|
|
1575
|
+
const raw = event.error_message || event.message || event.error || "";
|
|
1576
|
+
const detail = typeof raw === "string" ? raw : raw?.message || JSON.stringify(raw);
|
|
1577
|
+
const msg = detail ? `Gemini error: ${detail}` : `Gemini session ended with status: ${event.status}`;
|
|
1578
|
+
events.push({ kind: "error", message: msg });
|
|
1579
|
+
}
|
|
1580
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1581
|
+
break;
|
|
1582
|
+
}
|
|
1583
|
+
return events;
|
|
1584
|
+
}
|
|
1585
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1586
|
+
return null;
|
|
1587
|
+
}
|
|
1588
|
+
buildSystemPrompt(config, _agentId) {
|
|
1589
|
+
return buildBaseSystemPrompt(config, {
|
|
1590
|
+
toolPrefix: "",
|
|
1591
|
+
extraCriticalRules: [
|
|
1592
|
+
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
1593
|
+
],
|
|
1594
|
+
postStartupNotes: [],
|
|
1595
|
+
includeStdinNotificationSection: false,
|
|
1596
|
+
messageNotificationStyle: "poll"
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
toolDisplayName(name) {
|
|
1600
|
+
if (name === "list_tasks") return "Viewing task board\u2026";
|
|
1601
|
+
if (name === "create_tasks") return "Creating tasks\u2026";
|
|
1602
|
+
if (name === "claim_tasks") return "Claiming tasks\u2026";
|
|
1603
|
+
if (name === "unclaim_task") return "Unclaiming task\u2026";
|
|
1604
|
+
if (name === "update_task_status") return "Updating task\u2026";
|
|
1605
|
+
if (name === "send_message" || name === "receive_message" || name === "read_history" || name === "list_server") return "";
|
|
1606
|
+
if (name === "shell" || name === "Shell") return "Running command\u2026";
|
|
1607
|
+
if (name === "read_file" || name === "ReadFile") return "Reading file\u2026";
|
|
1608
|
+
if (name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") return "Editing file\u2026";
|
|
1609
|
+
if (name === "search_files" || name === "Glob" || name === "Grep") return "Searching code\u2026";
|
|
1610
|
+
if (name === "web_search" || name === "SearchWeb") return "Searching web\u2026";
|
|
1611
|
+
if (name === "fetch_url" || name === "FetchURL") return "Fetching web\u2026";
|
|
1612
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1613
|
+
}
|
|
1614
|
+
summarizeToolInput(name, input) {
|
|
1615
|
+
if (!input || typeof input !== "object") return "";
|
|
1616
|
+
try {
|
|
1617
|
+
if (name === "shell" || name === "Shell") {
|
|
1618
|
+
const cmd = input.command || "";
|
|
1619
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1620
|
+
}
|
|
1621
|
+
if (name === "read_file" || name === "ReadFile" || name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") {
|
|
1622
|
+
return input.path || input.file_path || "";
|
|
1623
|
+
}
|
|
1624
|
+
if (name === "Glob" || name === "Grep" || name === "search_files") return input.pattern || input.query || "";
|
|
1625
|
+
if (name === "web_search" || name === "SearchWeb") return input.query || "";
|
|
1626
|
+
if (name === "fetch_url" || name === "FetchURL") return input.url || "";
|
|
1627
|
+
if (name === "send_message") return input.target || input.channel || "";
|
|
1628
|
+
if (name === "read_history") return input.target || input.channel || "";
|
|
1629
|
+
if (name === "list_tasks") return input.channel || "";
|
|
1630
|
+
if (name === "create_tasks") return input.channel || "";
|
|
1631
|
+
if (name === "claim_tasks") {
|
|
1632
|
+
const nums = input.task_numbers;
|
|
1633
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1634
|
+
}
|
|
1635
|
+
if (name === "unclaim_task" || name === "update_task_status") {
|
|
1636
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
1637
|
+
}
|
|
1638
|
+
return "";
|
|
1639
|
+
} catch {
|
|
1640
|
+
return "";
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1643
|
+
};
|
|
1644
|
+
|
|
1645
|
+
// src/drivers/kimi.ts
|
|
1646
|
+
import { randomUUID } from "crypto";
|
|
1647
|
+
import { spawn as spawn6 } from "child_process";
|
|
1648
|
+
import { existsSync as existsSync4, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
1649
|
+
import os2 from "os";
|
|
1650
|
+
import path6 from "path";
|
|
1082
1651
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
1083
1652
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
1084
1653
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
@@ -1095,7 +1664,7 @@ var KimiDriver = class {
|
|
|
1095
1664
|
id = "kimi";
|
|
1096
1665
|
supportsStdinNotification = true;
|
|
1097
1666
|
mcpToolPrefix = "";
|
|
1098
|
-
|
|
1667
|
+
busyDeliveryMode = "direct";
|
|
1099
1668
|
sessionId = null;
|
|
1100
1669
|
sessionAnnounced = false;
|
|
1101
1670
|
promptRequestId = null;
|
|
@@ -1107,20 +1676,20 @@ var KimiDriver = class {
|
|
|
1107
1676
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1108
1677
|
const command = isTsSource ? "npx" : "node";
|
|
1109
1678
|
const bridgeArgs = isTsSource ? ["tsx", ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey] : [ctx.chatBridgePath, "--agent-id", ctx.agentId, "--server-url", ctx.config.serverUrl, "--auth-token", ctx.config.authToken || ctx.daemonApiKey];
|
|
1110
|
-
const systemPromptPath =
|
|
1111
|
-
const agentFilePath =
|
|
1112
|
-
const mcpConfigPath =
|
|
1113
|
-
if (!isResume || !
|
|
1114
|
-
|
|
1679
|
+
const systemPromptPath = path6.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
1680
|
+
const agentFilePath = path6.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
1681
|
+
const mcpConfigPath = path6.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
1682
|
+
if (!isResume || !existsSync4(systemPromptPath)) {
|
|
1683
|
+
writeFileSync5(systemPromptPath, ctx.prompt, "utf8");
|
|
1115
1684
|
}
|
|
1116
|
-
|
|
1685
|
+
writeFileSync5(agentFilePath, [
|
|
1117
1686
|
"version: 1",
|
|
1118
1687
|
"agent:",
|
|
1119
1688
|
" extend: default",
|
|
1120
1689
|
` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
|
|
1121
1690
|
""
|
|
1122
1691
|
].join("\n"), "utf8");
|
|
1123
|
-
|
|
1692
|
+
writeFileSync5(mcpConfigPath, JSON.stringify({
|
|
1124
1693
|
mcpServers: {
|
|
1125
1694
|
chat: {
|
|
1126
1695
|
command,
|
|
@@ -1142,7 +1711,7 @@ var KimiDriver = class {
|
|
|
1142
1711
|
args.push("--model", ctx.config.model);
|
|
1143
1712
|
}
|
|
1144
1713
|
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
1145
|
-
const proc =
|
|
1714
|
+
const proc = spawn6("kimi", args, {
|
|
1146
1715
|
cwd: ctx.workingDirectory,
|
|
1147
1716
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1148
1717
|
env: spawnEnv,
|
|
@@ -1297,12 +1866,43 @@ var KimiDriver = class {
|
|
|
1297
1866
|
return "";
|
|
1298
1867
|
}
|
|
1299
1868
|
}
|
|
1869
|
+
async detectModels() {
|
|
1870
|
+
return detectKimiModels();
|
|
1871
|
+
}
|
|
1300
1872
|
};
|
|
1873
|
+
function detectKimiModels(home = os2.homedir()) {
|
|
1874
|
+
const configPath = path6.join(home, ".kimi", "config.toml");
|
|
1875
|
+
let raw;
|
|
1876
|
+
try {
|
|
1877
|
+
raw = readFileSync2(configPath, "utf8");
|
|
1878
|
+
} catch {
|
|
1879
|
+
return null;
|
|
1880
|
+
}
|
|
1881
|
+
const models = [];
|
|
1882
|
+
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
1883
|
+
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
1884
|
+
let match;
|
|
1885
|
+
while ((match = lineRe.exec(raw)) !== null) {
|
|
1886
|
+
let key = match[1].trim();
|
|
1887
|
+
if (key.startsWith('"') && key.endsWith('"')) key = key.slice(1, -1);
|
|
1888
|
+
if (!key) continue;
|
|
1889
|
+
models.push({ id: key, label: key });
|
|
1890
|
+
}
|
|
1891
|
+
void sectionRe;
|
|
1892
|
+
if (models.length === 0) return null;
|
|
1893
|
+
let defaultModel;
|
|
1894
|
+
const defaultMatch = raw.match(/^\s*default_model\s*=\s*"([^"]+)"/m);
|
|
1895
|
+
if (defaultMatch) defaultModel = defaultMatch[1];
|
|
1896
|
+
return { models, default: defaultModel };
|
|
1897
|
+
}
|
|
1301
1898
|
|
|
1302
1899
|
// src/drivers/index.ts
|
|
1303
1900
|
var driverFactories = {
|
|
1304
1901
|
claude: () => new ClaudeDriver(),
|
|
1305
1902
|
codex: () => new CodexDriver(),
|
|
1903
|
+
copilot: () => new CopilotDriver(),
|
|
1904
|
+
cursor: () => new CursorDriver(),
|
|
1905
|
+
gemini: () => new GeminiDriver(),
|
|
1306
1906
|
kimi: () => new KimiDriver()
|
|
1307
1907
|
};
|
|
1308
1908
|
function getDriver(runtimeId) {
|
|
@@ -1359,7 +1959,7 @@ var logger = {
|
|
|
1359
1959
|
|
|
1360
1960
|
// src/workspaces.ts
|
|
1361
1961
|
import { readdir, rm, stat } from "fs/promises";
|
|
1362
|
-
import
|
|
1962
|
+
import path7 from "path";
|
|
1363
1963
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
1364
1964
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
1365
1965
|
}
|
|
@@ -1367,7 +1967,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
1367
1967
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
1368
1968
|
return null;
|
|
1369
1969
|
}
|
|
1370
|
-
return
|
|
1970
|
+
return path7.join(dataDir, directoryName);
|
|
1371
1971
|
}
|
|
1372
1972
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
1373
1973
|
return {
|
|
@@ -1416,7 +2016,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
1416
2016
|
return summary;
|
|
1417
2017
|
}
|
|
1418
2018
|
const childSummaries = await Promise.all(
|
|
1419
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
2019
|
+
entries.map((entry) => summarizeWorkspaceEntry(path7.join(dirPath, entry.name), entry))
|
|
1420
2020
|
);
|
|
1421
2021
|
for (const childSummary of childSummaries) {
|
|
1422
2022
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -1435,7 +2035,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
1435
2035
|
if (!entry.isDirectory()) {
|
|
1436
2036
|
return null;
|
|
1437
2037
|
}
|
|
1438
|
-
const dirPath =
|
|
2038
|
+
const dirPath = path7.join(dataDir, entry.name);
|
|
1439
2039
|
try {
|
|
1440
2040
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
1441
2041
|
return {
|
|
@@ -1467,7 +2067,7 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
|
|
|
1467
2067
|
}
|
|
1468
2068
|
|
|
1469
2069
|
// src/agentProcessManager.ts
|
|
1470
|
-
var DATA_DIR =
|
|
2070
|
+
var DATA_DIR = path8.join(os3.homedir(), ".slock", "agents");
|
|
1471
2071
|
function toLocalTime(iso) {
|
|
1472
2072
|
const d = new Date(iso);
|
|
1473
2073
|
if (isNaN(d.getTime())) return iso;
|
|
@@ -1479,7 +2079,7 @@ function formatChannelLabel(message) {
|
|
|
1479
2079
|
}
|
|
1480
2080
|
function formatMessageTarget(message) {
|
|
1481
2081
|
if (message.channel_type === "thread" && message.parent_channel_name) {
|
|
1482
|
-
const shortId =
|
|
2082
|
+
const shortId = getMessageShortId(message.channel_name);
|
|
1483
2083
|
if (message.parent_channel_type === "dm") {
|
|
1484
2084
|
return `dm:@${message.parent_channel_name}:${shortId}`;
|
|
1485
2085
|
}
|
|
@@ -1490,15 +2090,47 @@ function formatMessageTarget(message) {
|
|
|
1490
2090
|
}
|
|
1491
2091
|
return `#${message.channel_name}`;
|
|
1492
2092
|
}
|
|
2093
|
+
function getMessageShortId(messageId) {
|
|
2094
|
+
return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
|
|
2095
|
+
}
|
|
2096
|
+
function formatSenderHandle(message) {
|
|
2097
|
+
return message.sender_description ? `@${message.sender_name} \u2014 ${message.sender_description}` : `@${message.sender_name}`;
|
|
2098
|
+
}
|
|
2099
|
+
function formatVisibleActorType(type) {
|
|
2100
|
+
return ` type=${type}`;
|
|
2101
|
+
}
|
|
2102
|
+
function formatTaskAssigneeType(type) {
|
|
2103
|
+
return type ?? null;
|
|
2104
|
+
}
|
|
2105
|
+
function formatThreadContextMessage(message) {
|
|
2106
|
+
const msgId = message.message_id ? getMessageShortId(message.message_id) : "-";
|
|
2107
|
+
const time = message.timestamp ? toLocalTime(message.timestamp) : "-";
|
|
2108
|
+
const senderType = formatVisibleActorType(message.sender_type);
|
|
2109
|
+
return `- [msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}`;
|
|
2110
|
+
}
|
|
1493
2111
|
function formatIncomingMessage(message) {
|
|
2112
|
+
const threadJoinPrefix = message.thread_join_context ? [
|
|
2113
|
+
`[System: You were added to a new thread via @mention. Read this context before replying.]`,
|
|
2114
|
+
`parent: ${message.thread_join_context.parent_target}`,
|
|
2115
|
+
`thread: ${message.thread_join_context.thread_target}`,
|
|
2116
|
+
`suggested next step: read_history(channel="${message.thread_join_context.suggested_read_history_target}")`,
|
|
2117
|
+
"",
|
|
2118
|
+
"Parent message:",
|
|
2119
|
+
formatThreadContextMessage(message.thread_join_context.parent_message),
|
|
2120
|
+
"",
|
|
2121
|
+
`Recent thread context${message.thread_join_context.history_truncated ? " (truncated)" : ""}:`,
|
|
2122
|
+
message.thread_join_context.recent_messages.length > 0 ? message.thread_join_context.recent_messages.map(formatThreadContextMessage).join("\n") : "- (no earlier thread replies)",
|
|
2123
|
+
""
|
|
2124
|
+
].join("\n") : "";
|
|
1494
2125
|
const target = formatMessageTarget(message);
|
|
1495
|
-
const msgId = message.message_id ? message.message_id
|
|
2126
|
+
const msgId = message.message_id ? getMessageShortId(message.message_id) : "-";
|
|
1496
2127
|
const time = message.timestamp ? toLocalTime(message.timestamp) : "-";
|
|
1497
|
-
const senderType = message.sender_type
|
|
1498
|
-
const
|
|
1499
|
-
const
|
|
1500
|
-
const
|
|
1501
|
-
return
|
|
2128
|
+
const senderType = formatVisibleActorType(message.sender_type);
|
|
2129
|
+
const attachSuffix = message.attachments?.length ? ` [${message.attachments.length} attachment${message.attachments.length > 1 ? "s" : ""}: ${message.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to download]` : "";
|
|
2130
|
+
const taskSuffix = message.task_status ? ` [task #${message.task_number} status=${message.task_status}${message.task_assignee_id ? ` assignee=${formatTaskAssigneeType(message.task_assignee_type)}:${message.task_assignee_id}` : ""}]` : "";
|
|
2131
|
+
const body = `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}${attachSuffix}${taskSuffix}`;
|
|
2132
|
+
return threadJoinPrefix ? `${threadJoinPrefix}
|
|
2133
|
+
${body}` : body;
|
|
1502
2134
|
}
|
|
1503
2135
|
function buildUnreadSummary(messages, excludeChannel) {
|
|
1504
2136
|
const summary = /* @__PURE__ */ new Map();
|
|
@@ -1571,7 +2203,7 @@ function getMessageDeliveryText(driver) {
|
|
|
1571
2203
|
}
|
|
1572
2204
|
function getBusyDeliveryNote(driver) {
|
|
1573
2205
|
if (!driver.supportsStdinNotification) return "";
|
|
1574
|
-
if (driver.
|
|
2206
|
+
if (driver.busyDeliveryMode === "direct") {
|
|
1575
2207
|
return "\n\nNote: While you are busy, new messages may be delivered directly into your active turn. Handle them when appropriate and keep working.";
|
|
1576
2208
|
}
|
|
1577
2209
|
return "\n\nNote: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.";
|
|
@@ -1609,9 +2241,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
1609
2241
|
this.agentsStarting.add(agentId);
|
|
1610
2242
|
try {
|
|
1611
2243
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
1612
|
-
const agentDataDir =
|
|
2244
|
+
const agentDataDir = path8.join(this.dataDir, agentId);
|
|
1613
2245
|
await mkdir(agentDataDir, { recursive: true });
|
|
1614
|
-
const memoryMdPath =
|
|
2246
|
+
const memoryMdPath = path8.join(agentDataDir, "MEMORY.md");
|
|
1615
2247
|
try {
|
|
1616
2248
|
await access(memoryMdPath);
|
|
1617
2249
|
} catch {
|
|
@@ -1629,7 +2261,7 @@ ${config.description || "No role defined yet."}
|
|
|
1629
2261
|
`;
|
|
1630
2262
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
1631
2263
|
}
|
|
1632
|
-
await mkdir(
|
|
2264
|
+
await mkdir(path8.join(agentDataDir, "notes"), { recursive: true });
|
|
1633
2265
|
const isResume = !!config.sessionId;
|
|
1634
2266
|
let prompt;
|
|
1635
2267
|
if (isResume && resumePrompt) {
|
|
@@ -1958,7 +2590,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1958
2590
|
}
|
|
1959
2591
|
}
|
|
1960
2592
|
async resetWorkspace(agentId) {
|
|
1961
|
-
const agentDataDir =
|
|
2593
|
+
const agentDataDir = path8.join(this.dataDir, agentId);
|
|
1962
2594
|
try {
|
|
1963
2595
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
1964
2596
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -1996,7 +2628,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1996
2628
|
}
|
|
1997
2629
|
// Workspace file browsing
|
|
1998
2630
|
async getFileTree(agentId, dirPath) {
|
|
1999
|
-
const agentDir =
|
|
2631
|
+
const agentDir = path8.join(this.dataDir, agentId);
|
|
2000
2632
|
try {
|
|
2001
2633
|
await stat2(agentDir);
|
|
2002
2634
|
} catch {
|
|
@@ -2004,8 +2636,8 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2004
2636
|
}
|
|
2005
2637
|
let targetDir = agentDir;
|
|
2006
2638
|
if (dirPath) {
|
|
2007
|
-
const resolved =
|
|
2008
|
-
if (!resolved.startsWith(agentDir +
|
|
2639
|
+
const resolved = path8.resolve(agentDir, dirPath);
|
|
2640
|
+
if (!resolved.startsWith(agentDir + path8.sep) && resolved !== agentDir) {
|
|
2009
2641
|
return [];
|
|
2010
2642
|
}
|
|
2011
2643
|
targetDir = resolved;
|
|
@@ -2013,9 +2645,9 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2013
2645
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
2014
2646
|
}
|
|
2015
2647
|
async readFile(agentId, filePath) {
|
|
2016
|
-
const agentDir =
|
|
2017
|
-
const resolved =
|
|
2018
|
-
if (!resolved.startsWith(agentDir +
|
|
2648
|
+
const agentDir = path8.join(this.dataDir, agentId);
|
|
2649
|
+
const resolved = path8.resolve(agentDir, filePath);
|
|
2650
|
+
if (!resolved.startsWith(agentDir + path8.sep) && resolved !== agentDir) {
|
|
2019
2651
|
throw new Error("Access denied");
|
|
2020
2652
|
}
|
|
2021
2653
|
const info = await stat2(resolved);
|
|
@@ -2039,7 +2671,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2039
2671
|
".sh",
|
|
2040
2672
|
".py"
|
|
2041
2673
|
]);
|
|
2042
|
-
const ext =
|
|
2674
|
+
const ext = path8.extname(resolved).toLowerCase();
|
|
2043
2675
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
2044
2676
|
return { content: null, binary: true };
|
|
2045
2677
|
}
|
|
@@ -2065,14 +2697,14 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2065
2697
|
async listSkills(agentId, runtimeHint) {
|
|
2066
2698
|
const agent = this.agents.get(agentId);
|
|
2067
2699
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
2068
|
-
const home =
|
|
2069
|
-
const workspaceDir =
|
|
2700
|
+
const home = os3.homedir();
|
|
2701
|
+
const workspaceDir = path8.join(this.dataDir, agentId);
|
|
2070
2702
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
2071
2703
|
const globalResults = await Promise.all(
|
|
2072
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
2704
|
+
paths.global.map((p) => this.scanSkillsDir(path8.join(home, p)))
|
|
2073
2705
|
);
|
|
2074
2706
|
const workspaceResults = await Promise.all(
|
|
2075
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
2707
|
+
paths.workspace.map((p) => this.scanSkillsDir(path8.join(workspaceDir, p)))
|
|
2076
2708
|
);
|
|
2077
2709
|
const dedup = (skills) => {
|
|
2078
2710
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2101,7 +2733,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2101
2733
|
const skills = [];
|
|
2102
2734
|
for (const entry of entries) {
|
|
2103
2735
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
2104
|
-
const skillMd =
|
|
2736
|
+
const skillMd = path8.join(dir, entry.name, "SKILL.md");
|
|
2105
2737
|
try {
|
|
2106
2738
|
const content = await readFile(skillMd, "utf-8");
|
|
2107
2739
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -2112,7 +2744,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2112
2744
|
} else if (entry.name.endsWith(".md")) {
|
|
2113
2745
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
2114
2746
|
try {
|
|
2115
|
-
const content = await readFile(
|
|
2747
|
+
const content = await readFile(path8.join(dir, entry.name), "utf-8");
|
|
2116
2748
|
const skill = this.parseSkillMd(cmdName, content);
|
|
2117
2749
|
skill.sourcePath = dir;
|
|
2118
2750
|
skills.push(skill);
|
|
@@ -2287,7 +2919,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2287
2919
|
if (count === 0) return;
|
|
2288
2920
|
if (ap.isIdle) return;
|
|
2289
2921
|
if (!ap.sessionId) return;
|
|
2290
|
-
if (ap.driver.
|
|
2922
|
+
if (ap.driver.busyDeliveryMode === "direct" && ap.inbox.length > 0) {
|
|
2291
2923
|
const queuedMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
2292
2924
|
console.log(`[Agent ${agentId}] Delivering queued message via stdin while busy`);
|
|
2293
2925
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
@@ -2350,8 +2982,8 @@ Respond as appropriate. Complete all your work before stopping.`;
|
|
|
2350
2982
|
const nodes = [];
|
|
2351
2983
|
for (const entry of entries) {
|
|
2352
2984
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2353
|
-
const fullPath =
|
|
2354
|
-
const relativePath =
|
|
2985
|
+
const fullPath = path8.join(dir, entry.name);
|
|
2986
|
+
const relativePath = path8.relative(rootDir, fullPath);
|
|
2355
2987
|
let info;
|
|
2356
2988
|
try {
|
|
2357
2989
|
info = await stat2(fullPath);
|
|
@@ -2522,13 +3154,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
2522
3154
|
}
|
|
2523
3155
|
}
|
|
2524
3156
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
2525
|
-
const dirname =
|
|
2526
|
-
const jsPath =
|
|
3157
|
+
const dirname = path9.dirname(fileURLToPath(moduleUrl));
|
|
3158
|
+
const jsPath = path9.resolve(dirname, "chat-bridge.js");
|
|
2527
3159
|
try {
|
|
2528
3160
|
accessSync(jsPath);
|
|
2529
3161
|
return jsPath;
|
|
2530
3162
|
} catch {
|
|
2531
|
-
return
|
|
3163
|
+
return path9.resolve(dirname, "chat-bridge.ts");
|
|
2532
3164
|
}
|
|
2533
3165
|
}
|
|
2534
3166
|
function detectRuntimes() {
|
|
@@ -2570,6 +3202,8 @@ function summarizeIncomingMessage(msg) {
|
|
|
2570
3202
|
return `(agent=${msg.agentId}, runtime=${msg.runtime || "auto"})`;
|
|
2571
3203
|
case "machine:workspace:delete":
|
|
2572
3204
|
return `(directory=${msg.directoryName})`;
|
|
3205
|
+
case "machine:runtime_models:detect":
|
|
3206
|
+
return `(runtime=${msg.runtime}, req=${msg.requestId})`;
|
|
2573
3207
|
default:
|
|
2574
3208
|
return "";
|
|
2575
3209
|
}
|
|
@@ -2688,6 +3322,21 @@ var DaemonCore = class {
|
|
|
2688
3322
|
this.connection.send({ type: "machine:workspace:delete_result", directoryName: msg.directoryName, success });
|
|
2689
3323
|
});
|
|
2690
3324
|
break;
|
|
3325
|
+
case "machine:runtime_models:detect": {
|
|
3326
|
+
const driver = getDriver(msg.runtime);
|
|
3327
|
+
const detect = typeof driver?.detectModels === "function" ? driver.detectModels() : Promise.resolve(null);
|
|
3328
|
+
Promise.resolve(detect).then((result) => {
|
|
3329
|
+
if (result) {
|
|
3330
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, models: result.models, default: result.default });
|
|
3331
|
+
} else {
|
|
3332
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: "unsupported" });
|
|
3333
|
+
}
|
|
3334
|
+
}).catch((err) => {
|
|
3335
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3336
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: reason });
|
|
3337
|
+
});
|
|
3338
|
+
break;
|
|
3339
|
+
}
|
|
2691
3340
|
case "ping":
|
|
2692
3341
|
this.connection.send({ type: "pong" });
|
|
2693
3342
|
break;
|
|
@@ -2702,8 +3351,8 @@ var DaemonCore = class {
|
|
|
2702
3351
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
2703
3352
|
runtimes,
|
|
2704
3353
|
runningAgents: this.agentManager.getRunningAgentIds(),
|
|
2705
|
-
hostname: this.options.hostname ??
|
|
2706
|
-
os: this.options.osDescription ?? `${
|
|
3354
|
+
hostname: this.options.hostname ?? os4.hostname(),
|
|
3355
|
+
os: this.options.osDescription ?? `${os4.platform()} ${os4.arch()}`,
|
|
2707
3356
|
daemonVersion: this.daemonVersion
|
|
2708
3357
|
});
|
|
2709
3358
|
for (const agentId of this.agentManager.getRunningAgentIds()) {
|
package/dist/core.js
CHANGED
package/dist/index.js
CHANGED