@slock-ai/daemon 0.13.0 → 0.16.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 +159 -10
- package/dist/index.js +54 -35
- package/package.json +1 -1
package/dist/chat-bridge.js
CHANGED
|
@@ -51,14 +51,15 @@ server.tool(
|
|
|
51
51
|
target: z.string().describe(
|
|
52
52
|
"Where to send. Reuse the identifier from received messages. Format: '#channel' for channels, 'dm:@name' for DMs, '#channel:id' for channel threads, 'dm:@name:id' for DM threads. Examples: '#general', 'dm:@richard', '#general:abcd1234', 'dm:@richard:abcd1234'."
|
|
53
53
|
),
|
|
54
|
-
content: z.string().describe("The message content")
|
|
54
|
+
content: z.string().describe("The message content"),
|
|
55
|
+
attachment_ids: z.array(z.string()).optional().describe("Optional attachment IDs from upload_file to include with the message")
|
|
55
56
|
},
|
|
56
|
-
async ({ target, content }) => {
|
|
57
|
+
async ({ target, content, attachment_ids }) => {
|
|
57
58
|
try {
|
|
58
59
|
const res = await fetch(`${serverUrl}/internal/agent/${agentId}/send`, {
|
|
59
60
|
method: "POST",
|
|
60
61
|
headers: commonHeaders,
|
|
61
|
-
body: JSON.stringify({ target, content })
|
|
62
|
+
body: JSON.stringify({ target, content, attachmentIds: attachment_ids })
|
|
62
63
|
});
|
|
63
64
|
const data = await res.json();
|
|
64
65
|
if (!res.ok) {
|
|
@@ -68,11 +69,13 @@ server.tool(
|
|
|
68
69
|
]
|
|
69
70
|
};
|
|
70
71
|
}
|
|
72
|
+
const shortId = data.messageId ? data.messageId.slice(0, 8) : null;
|
|
73
|
+
const replyHint = shortId ? ` (to reply in this message's thread, use target "${target.includes(":") ? target : target + ":" + shortId}")` : "";
|
|
71
74
|
return {
|
|
72
75
|
content: [
|
|
73
76
|
{
|
|
74
77
|
type: "text",
|
|
75
|
-
text: `Message sent to ${target}`
|
|
78
|
+
text: `Message sent to ${target}. Message ID: ${data.messageId}${replyHint}`
|
|
76
79
|
}
|
|
77
80
|
]
|
|
78
81
|
};
|
|
@@ -83,6 +86,148 @@ server.tool(
|
|
|
83
86
|
}
|
|
84
87
|
}
|
|
85
88
|
);
|
|
89
|
+
server.tool(
|
|
90
|
+
"upload_file",
|
|
91
|
+
"Upload an image file to attach to a message. Returns an attachment ID that you can pass to send_message's attachment_ids parameter. Supported formats: JPEG, PNG, GIF, WebP. Max size: 5MB.",
|
|
92
|
+
{
|
|
93
|
+
file_path: z.string().describe("Absolute path to the image file on your local filesystem"),
|
|
94
|
+
channel: z.string().describe("The channel target where this file will be used (e.g. '#general', 'dm:@richard')")
|
|
95
|
+
},
|
|
96
|
+
async ({ file_path, channel }) => {
|
|
97
|
+
try {
|
|
98
|
+
const fs = await import("fs");
|
|
99
|
+
const path = await import("path");
|
|
100
|
+
if (!fs.existsSync(file_path)) {
|
|
101
|
+
return {
|
|
102
|
+
content: [{ type: "text", text: `Error: File not found: ${file_path}` }]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const stat = fs.statSync(file_path);
|
|
106
|
+
if (stat.size > 5 * 1024 * 1024) {
|
|
107
|
+
return {
|
|
108
|
+
content: [{ type: "text", text: `Error: File too large (${(stat.size / 1024 / 1024).toFixed(1)}MB). Max 5MB.` }]
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
const listRes = await fetch(`${serverUrl}/internal/agent/${agentId}/resolve-channel`, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: commonHeaders,
|
|
114
|
+
body: JSON.stringify({ target: channel })
|
|
115
|
+
});
|
|
116
|
+
let channelId;
|
|
117
|
+
if (listRes.ok) {
|
|
118
|
+
const listData = await listRes.json();
|
|
119
|
+
channelId = listData.channelId;
|
|
120
|
+
} else {
|
|
121
|
+
return {
|
|
122
|
+
content: [{ type: "text", text: `Error: Could not resolve channel: ${channel}` }]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
const fileBuffer = fs.readFileSync(file_path);
|
|
126
|
+
const filename = path.basename(file_path);
|
|
127
|
+
const ext = path.extname(file_path).toLowerCase();
|
|
128
|
+
const mimeMap = {
|
|
129
|
+
".jpg": "image/jpeg",
|
|
130
|
+
".jpeg": "image/jpeg",
|
|
131
|
+
".png": "image/png",
|
|
132
|
+
".gif": "image/gif",
|
|
133
|
+
".webp": "image/webp"
|
|
134
|
+
};
|
|
135
|
+
const mimeType = mimeMap[ext] || "application/octet-stream";
|
|
136
|
+
const blob = new Blob([fileBuffer], { type: mimeType });
|
|
137
|
+
const formData = new FormData();
|
|
138
|
+
formData.append("file", blob, filename);
|
|
139
|
+
formData.append("channelId", channelId);
|
|
140
|
+
const uploadHeaders = {};
|
|
141
|
+
if (authToken) {
|
|
142
|
+
uploadHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
143
|
+
}
|
|
144
|
+
const res = await fetch(`${serverUrl}/internal/agent/${agentId}/upload`, {
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: uploadHeaders,
|
|
147
|
+
body: formData
|
|
148
|
+
});
|
|
149
|
+
const data = await res.json();
|
|
150
|
+
if (!res.ok) {
|
|
151
|
+
return {
|
|
152
|
+
content: [{ type: "text", text: `Error: ${data.error}` }]
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
content: [
|
|
157
|
+
{
|
|
158
|
+
type: "text",
|
|
159
|
+
text: `File uploaded: ${data.filename} (${(data.sizeBytes / 1024).toFixed(1)}KB)
|
|
160
|
+
Attachment ID: ${data.id}
|
|
161
|
+
|
|
162
|
+
Use this ID in send_message's attachment_ids parameter to include it in a message.`
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
};
|
|
166
|
+
} catch (err) {
|
|
167
|
+
return {
|
|
168
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
server.tool(
|
|
174
|
+
"view_file",
|
|
175
|
+
"Download an attached image by its attachment ID and save it locally so you can view it. Returns the local file path. Use this when you see '[use view_file to see]' in a message with images.",
|
|
176
|
+
{
|
|
177
|
+
attachment_id: z.string().describe("The attachment UUID (from the 'id:...' shown in the message)")
|
|
178
|
+
},
|
|
179
|
+
async ({ attachment_id }) => {
|
|
180
|
+
try {
|
|
181
|
+
const fs = await import("fs");
|
|
182
|
+
const path = await import("path");
|
|
183
|
+
const os = await import("os");
|
|
184
|
+
const cacheDir = path.join(os.homedir(), ".slock", "attachments");
|
|
185
|
+
fs.mkdirSync(cacheDir, { recursive: true });
|
|
186
|
+
const existing = fs.readdirSync(cacheDir).find((f) => f.startsWith(attachment_id));
|
|
187
|
+
if (existing) {
|
|
188
|
+
const cachedPath = path.join(cacheDir, existing);
|
|
189
|
+
return {
|
|
190
|
+
content: [{ type: "text", text: `File already cached at: ${cachedPath}
|
|
191
|
+
|
|
192
|
+
Use your Read tool to view this image.` }]
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
const downloadHeaders = {};
|
|
196
|
+
if (authToken) {
|
|
197
|
+
downloadHeaders["Authorization"] = `Bearer ${authToken}`;
|
|
198
|
+
}
|
|
199
|
+
const res = await fetch(`${serverUrl}/api/attachments/${attachment_id}`, {
|
|
200
|
+
headers: downloadHeaders,
|
|
201
|
+
redirect: "follow"
|
|
202
|
+
});
|
|
203
|
+
if (!res.ok) {
|
|
204
|
+
return {
|
|
205
|
+
content: [{ type: "text", text: `Error: Failed to download attachment (${res.status})` }]
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
const contentType = res.headers.get("content-type") || "application/octet-stream";
|
|
209
|
+
const extMap = {
|
|
210
|
+
"image/jpeg": ".jpg",
|
|
211
|
+
"image/png": ".png",
|
|
212
|
+
"image/gif": ".gif",
|
|
213
|
+
"image/webp": ".webp"
|
|
214
|
+
};
|
|
215
|
+
const ext = extMap[contentType] || ".bin";
|
|
216
|
+
const filePath = path.join(cacheDir, `${attachment_id}${ext}`);
|
|
217
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
218
|
+
fs.writeFileSync(filePath, buffer);
|
|
219
|
+
return {
|
|
220
|
+
content: [{ type: "text", text: `Downloaded to: ${filePath}
|
|
221
|
+
|
|
222
|
+
Use your Read tool to view this image.` }]
|
|
223
|
+
};
|
|
224
|
+
} catch (err) {
|
|
225
|
+
return {
|
|
226
|
+
content: [{ type: "text", text: `Error: ${err.message}` }]
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
);
|
|
86
231
|
server.tool(
|
|
87
232
|
"receive_message",
|
|
88
233
|
"Receive new messages. Use block=true to wait for new messages. Returns messages formatted as [#channel], [dm:@peer], or [thread:#channel:id] followed by the sender and content.",
|
|
@@ -107,9 +252,11 @@ server.tool(
|
|
|
107
252
|
}
|
|
108
253
|
const formatted = data.messages.map((m) => {
|
|
109
254
|
const target = formatTarget(m);
|
|
110
|
-
const
|
|
111
|
-
const time = m.timestamp ?
|
|
112
|
-
|
|
255
|
+
const msgId = m.message_id ? m.message_id.slice(0, 8) : "-";
|
|
256
|
+
const time = m.timestamp ? toLocalTime(m.timestamp) : "-";
|
|
257
|
+
const senderType = m.sender_type === "agent" ? " type=agent" : "";
|
|
258
|
+
const attachSuffix = m.attachments?.length ? ` [${m.attachments.length} image${m.attachments.length > 1 ? "s" : ""}: ${m.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to see]` : "";
|
|
259
|
+
return `[target=${target} msg=${msgId} time=${time}${senderType}] @${m.sender_name}: ${m.content}${attachSuffix}`;
|
|
113
260
|
}).join("\n");
|
|
114
261
|
return {
|
|
115
262
|
content: [{ type: "text", text: formatted }]
|
|
@@ -211,9 +358,11 @@ server.tool(
|
|
|
211
358
|
};
|
|
212
359
|
}
|
|
213
360
|
const formatted = data.messages.map((m) => {
|
|
214
|
-
const
|
|
215
|
-
const time = m.createdAt ?
|
|
216
|
-
|
|
361
|
+
const senderType = m.senderType === "agent" ? " type=agent" : "";
|
|
362
|
+
const time = m.createdAt ? toLocalTime(m.createdAt) : "-";
|
|
363
|
+
const msgId = m.id ? m.id.slice(0, 8) : "-";
|
|
364
|
+
const attachSuffix = m.attachments?.length ? ` [${m.attachments.length} image${m.attachments.length > 1 ? "s" : ""}: ${m.attachments.map((a) => `${a.filename} (id:${a.id})`).join(", ")} \u2014 use view_file to see]` : "";
|
|
365
|
+
return `[seq=${m.seq} msg=${msgId} time=${time}${senderType}] @${m.senderName}: ${m.content}${attachSuffix}`;
|
|
217
366
|
}).join("\n");
|
|
218
367
|
let footer = "";
|
|
219
368
|
if (data.historyLimited) {
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import
|
|
4
|
+
import path4 from "path";
|
|
5
5
|
import os2 from "os";
|
|
6
6
|
import { createRequire } from "module";
|
|
7
7
|
import { execSync as execSync2 } from "child_process";
|
|
@@ -84,11 +84,13 @@ var DaemonConnection = class {
|
|
|
84
84
|
|
|
85
85
|
// src/agentProcessManager.ts
|
|
86
86
|
import { mkdir, writeFile, access, readdir, stat, readFile, rm } from "fs/promises";
|
|
87
|
-
import
|
|
87
|
+
import path3 from "path";
|
|
88
88
|
import os from "os";
|
|
89
89
|
|
|
90
90
|
// src/drivers/claude.ts
|
|
91
91
|
import { spawn } from "child_process";
|
|
92
|
+
import { writeFileSync } from "fs";
|
|
93
|
+
import path from "path";
|
|
92
94
|
|
|
93
95
|
// src/drivers/systemPrompt.ts
|
|
94
96
|
function toolRef(prefix, name) {
|
|
@@ -127,6 +129,7 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
127
129
|
7. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
|
|
128
130
|
8. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
129
131
|
9. **${t("update_task_status")}** \u2014 Change a task's status (e.g. to in_review or done).
|
|
132
|
+
10. **${t("upload_file")}** \u2014 Upload an image file to attach to a message. Returns an attachment ID to pass to send_message.
|
|
130
133
|
|
|
131
134
|
CRITICAL RULES:
|
|
132
135
|
${criticalRules.join("\n")}
|
|
@@ -143,14 +146,21 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
143
146
|
|
|
144
147
|
## Messaging
|
|
145
148
|
|
|
146
|
-
Messages you receive
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
150
|
+
|
|
151
|
+
\`\`\`
|
|
152
|
+
[target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00] @richard: hello everyone
|
|
153
|
+
[target=#general msg=e5f6a7b8 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
|
|
154
|
+
[target=dm:@richard msg=c9d0e1f2 time=2026-03-15T01:00:02] @richard: hey, can you help?
|
|
155
|
+
[target=#general:a1b2c3d4 msg=f3a4b5c6 time=2026-03-15T01:00:03] @richard: thread reply
|
|
156
|
+
[target=dm:@richard:x9y8z7a0 msg=d7e8f9a0 time=2026-03-15T01:00:04] @richard: DM thread reply
|
|
157
|
+
\`\`\`
|
|
152
158
|
|
|
153
|
-
|
|
159
|
+
Header fields:
|
|
160
|
+
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
161
|
+
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
162
|
+
- \`time=\` \u2014 timestamp.
|
|
163
|
+
- \`type=agent\` \u2014 present only if the sender is an agent.
|
|
154
164
|
|
|
155
165
|
### Sending messages
|
|
156
166
|
|
|
@@ -165,9 +175,11 @@ The \`[...]\` prefix is the **target** \u2014 it identifies where the message ca
|
|
|
165
175
|
|
|
166
176
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
167
177
|
|
|
168
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
178
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:a1b2c3d4\` (thread in #general) or \`dm:@richard:x9y8z7a0\` (thread in a DM).
|
|
169
179
|
- When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
|
|
170
|
-
-
|
|
180
|
+
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=a1b2c3d4 ...]\`, reply with \`send_message(target="#general:a1b2c3d4", content="...")\`. The thread will be auto-created if it doesn't exist yet.
|
|
181
|
+
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
182
|
+
- You can read thread history: \`read_history(channel="#general:a1b2c3d4")\`
|
|
171
183
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.
|
|
172
184
|
|
|
173
185
|
### Discovering people and channels
|
|
@@ -360,6 +372,8 @@ var ClaudeDriver = class {
|
|
|
360
372
|
}
|
|
361
373
|
}
|
|
362
374
|
});
|
|
375
|
+
const mcpConfigPath = path.join(ctx.workingDirectory, ".slock-claude-mcp.json");
|
|
376
|
+
writeFileSync(mcpConfigPath, mcpConfig, "utf8");
|
|
363
377
|
const args2 = [
|
|
364
378
|
"--allow-dangerously-skip-permissions",
|
|
365
379
|
"--dangerously-skip-permissions",
|
|
@@ -369,7 +383,7 @@ var ClaudeDriver = class {
|
|
|
369
383
|
"--input-format",
|
|
370
384
|
"stream-json",
|
|
371
385
|
"--mcp-config",
|
|
372
|
-
|
|
386
|
+
mcpConfigPath,
|
|
373
387
|
"--model",
|
|
374
388
|
ctx.config.model || "sonnet"
|
|
375
389
|
];
|
|
@@ -496,6 +510,7 @@ var ClaudeDriver = class {
|
|
|
496
510
|
if (name === "mcp__chat__unclaim_task" || name === "mcp__chat__complete_task") {
|
|
497
511
|
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
498
512
|
}
|
|
513
|
+
if (name === "mcp__chat__upload_file") return input.file_path || "";
|
|
499
514
|
return "";
|
|
500
515
|
} catch {
|
|
501
516
|
return "";
|
|
@@ -506,13 +521,13 @@ var ClaudeDriver = class {
|
|
|
506
521
|
// src/drivers/codex.ts
|
|
507
522
|
import { spawn as spawn2, execSync } from "child_process";
|
|
508
523
|
import { existsSync } from "fs";
|
|
509
|
-
import
|
|
524
|
+
import path2 from "path";
|
|
510
525
|
var CodexDriver = class {
|
|
511
526
|
id = "codex";
|
|
512
527
|
supportsStdinNotification = false;
|
|
513
528
|
mcpToolPrefix = "mcp_chat_";
|
|
514
529
|
spawn(ctx) {
|
|
515
|
-
const gitDir =
|
|
530
|
+
const gitDir = path2.join(ctx.workingDirectory, ".git");
|
|
516
531
|
if (!existsSync(gitDir)) {
|
|
517
532
|
execSync("git init", { cwd: ctx.workingDirectory, stdio: "pipe" });
|
|
518
533
|
execSync("git add -A && git commit --allow-empty -m 'init'", {
|
|
@@ -707,6 +722,7 @@ var CodexDriver = class {
|
|
|
707
722
|
if (name === `${this.mcpToolPrefix}unclaim_task` || name === `${this.mcpToolPrefix}complete_task`) {
|
|
708
723
|
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
709
724
|
}
|
|
725
|
+
if (name === `${this.mcpToolPrefix}upload_file`) return input.file_path || "";
|
|
710
726
|
return "";
|
|
711
727
|
} catch {
|
|
712
728
|
return "";
|
|
@@ -728,7 +744,7 @@ function getDriver(runtimeId) {
|
|
|
728
744
|
}
|
|
729
745
|
|
|
730
746
|
// src/agentProcessManager.ts
|
|
731
|
-
var DATA_DIR =
|
|
747
|
+
var DATA_DIR = path3.join(os.homedir(), ".slock", "agents");
|
|
732
748
|
function toLocalTime(iso) {
|
|
733
749
|
const d = new Date(iso);
|
|
734
750
|
if (isNaN(d.getTime())) return iso;
|
|
@@ -753,9 +769,9 @@ var AgentProcessManager = class {
|
|
|
753
769
|
this.agentsStarting.add(agentId);
|
|
754
770
|
try {
|
|
755
771
|
const driver = getDriver(config.runtime || "claude");
|
|
756
|
-
const agentDataDir =
|
|
772
|
+
const agentDataDir = path3.join(DATA_DIR, agentId);
|
|
757
773
|
await mkdir(agentDataDir, { recursive: true });
|
|
758
|
-
const memoryMdPath =
|
|
774
|
+
const memoryMdPath = path3.join(agentDataDir, "MEMORY.md");
|
|
759
775
|
try {
|
|
760
776
|
await access(memoryMdPath);
|
|
761
777
|
} catch {
|
|
@@ -773,7 +789,7 @@ ${config.description || "No role defined yet."}
|
|
|
773
789
|
`;
|
|
774
790
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
775
791
|
}
|
|
776
|
-
await mkdir(
|
|
792
|
+
await mkdir(path3.join(agentDataDir, "notes"), { recursive: true });
|
|
777
793
|
const isResume = !!config.sessionId;
|
|
778
794
|
let prompt;
|
|
779
795
|
if (isResume && wakeMessage) {
|
|
@@ -953,7 +969,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
953
969
|
}
|
|
954
970
|
}
|
|
955
971
|
async resetWorkspace(agentId) {
|
|
956
|
-
const agentDataDir =
|
|
972
|
+
const agentDataDir = path3.join(DATA_DIR, agentId);
|
|
957
973
|
try {
|
|
958
974
|
await rm(agentDataDir, { recursive: true, force: true });
|
|
959
975
|
console.log(`[Agent ${agentId}] Workspace deleted: ${agentDataDir}`);
|
|
@@ -979,14 +995,14 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
979
995
|
}
|
|
980
996
|
for (const entry of entries) {
|
|
981
997
|
if (!entry.isDirectory()) continue;
|
|
982
|
-
const dirPath =
|
|
998
|
+
const dirPath = path3.join(DATA_DIR, entry.name);
|
|
983
999
|
try {
|
|
984
1000
|
const dirContents = await readdir(dirPath, { withFileTypes: true });
|
|
985
1001
|
let totalSize = 0;
|
|
986
1002
|
let latestMtime = /* @__PURE__ */ new Date(0);
|
|
987
1003
|
let fileCount = 0;
|
|
988
1004
|
for (const item of dirContents) {
|
|
989
|
-
const itemPath =
|
|
1005
|
+
const itemPath = path3.join(dirPath, item.name);
|
|
990
1006
|
try {
|
|
991
1007
|
const info = await stat(itemPath);
|
|
992
1008
|
if (item.isFile()) {
|
|
@@ -1016,7 +1032,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1016
1032
|
if (directoryName.includes("/") || directoryName.includes("..") || directoryName.includes("\\")) {
|
|
1017
1033
|
return false;
|
|
1018
1034
|
}
|
|
1019
|
-
const targetDir =
|
|
1035
|
+
const targetDir = path3.join(DATA_DIR, directoryName);
|
|
1020
1036
|
try {
|
|
1021
1037
|
await rm(targetDir, { recursive: true, force: true });
|
|
1022
1038
|
console.log(`[Workspace] Deleted directory: ${targetDir}`);
|
|
@@ -1028,7 +1044,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1028
1044
|
}
|
|
1029
1045
|
// Workspace file browsing
|
|
1030
1046
|
async getFileTree(agentId, dirPath) {
|
|
1031
|
-
const agentDir =
|
|
1047
|
+
const agentDir = path3.join(DATA_DIR, agentId);
|
|
1032
1048
|
try {
|
|
1033
1049
|
await stat(agentDir);
|
|
1034
1050
|
} catch {
|
|
@@ -1036,8 +1052,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1036
1052
|
}
|
|
1037
1053
|
let targetDir = agentDir;
|
|
1038
1054
|
if (dirPath) {
|
|
1039
|
-
const resolved =
|
|
1040
|
-
if (!resolved.startsWith(agentDir +
|
|
1055
|
+
const resolved = path3.resolve(agentDir, dirPath);
|
|
1056
|
+
if (!resolved.startsWith(agentDir + path3.sep) && resolved !== agentDir) {
|
|
1041
1057
|
return [];
|
|
1042
1058
|
}
|
|
1043
1059
|
targetDir = resolved;
|
|
@@ -1045,9 +1061,9 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1045
1061
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
1046
1062
|
}
|
|
1047
1063
|
async readFile(agentId, filePath) {
|
|
1048
|
-
const agentDir =
|
|
1049
|
-
const resolved =
|
|
1050
|
-
if (!resolved.startsWith(agentDir +
|
|
1064
|
+
const agentDir = path3.join(DATA_DIR, agentId);
|
|
1065
|
+
const resolved = path3.resolve(agentDir, filePath);
|
|
1066
|
+
if (!resolved.startsWith(agentDir + path3.sep) && resolved !== agentDir) {
|
|
1051
1067
|
throw new Error("Access denied");
|
|
1052
1068
|
}
|
|
1053
1069
|
const info = await stat(resolved);
|
|
@@ -1071,7 +1087,7 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1071
1087
|
".sh",
|
|
1072
1088
|
".py"
|
|
1073
1089
|
]);
|
|
1074
|
-
const ext =
|
|
1090
|
+
const ext = path3.extname(resolved).toLowerCase();
|
|
1075
1091
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
1076
1092
|
return { content: null, binary: true };
|
|
1077
1093
|
}
|
|
@@ -1110,7 +1126,10 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1110
1126
|
const inputSummary = driver.summarizeToolInput(toolName, event.input);
|
|
1111
1127
|
trajectory.push({ kind: "tool_start", toolName, toolInput: inputSummary });
|
|
1112
1128
|
if (toolName === `${driver.mcpToolPrefix}receive_message`) {
|
|
1113
|
-
|
|
1129
|
+
const isBlocking = event.input?.block !== false;
|
|
1130
|
+
if (isBlocking) {
|
|
1131
|
+
activity = "online";
|
|
1132
|
+
}
|
|
1114
1133
|
if (ap) {
|
|
1115
1134
|
ap.isInReceiveMessage = true;
|
|
1116
1135
|
ap.pendingNotificationCount = 0;
|
|
@@ -1185,8 +1204,8 @@ Note: While you are busy, you may receive [System notification: ...] messages ab
|
|
|
1185
1204
|
const nodes = [];
|
|
1186
1205
|
for (const entry of entries) {
|
|
1187
1206
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
1188
|
-
const fullPath =
|
|
1189
|
-
const relativePath =
|
|
1207
|
+
const fullPath = path3.join(dir, entry.name);
|
|
1208
|
+
const relativePath = path3.relative(rootDir, fullPath);
|
|
1190
1209
|
let info;
|
|
1191
1210
|
try {
|
|
1192
1211
|
info = await stat(fullPath);
|
|
@@ -1281,12 +1300,12 @@ if (!serverUrl || !apiKey) {
|
|
|
1281
1300
|
console.error("Usage: slock-daemon --server-url <url> --api-key <key>");
|
|
1282
1301
|
process.exit(1);
|
|
1283
1302
|
}
|
|
1284
|
-
var __dirname =
|
|
1285
|
-
var chatBridgePath =
|
|
1303
|
+
var __dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
1304
|
+
var chatBridgePath = path4.resolve(__dirname, "chat-bridge.js");
|
|
1286
1305
|
try {
|
|
1287
1306
|
accessSync(chatBridgePath);
|
|
1288
1307
|
} catch {
|
|
1289
|
-
chatBridgePath =
|
|
1308
|
+
chatBridgePath = path4.resolve(__dirname, "chat-bridge.ts");
|
|
1290
1309
|
}
|
|
1291
1310
|
var connection;
|
|
1292
1311
|
var agentManager = new AgentProcessManager(chatBridgePath, (msg) => {
|