@slock-ai/daemon 0.34.0 → 0.36.1-alpha.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 +479 -123
- package/dist/{chunk-ZXIMDHR7.js → chunk-7RQ2H2AM.js} +900 -131
- package/dist/{chunk-GX2DVINN.js → chunk-E6OOH3IC.js} +46 -1
- package/dist/core.js +4 -3
- package/dist/index.js +2 -2
- package/package.json +1 -1
|
@@ -1,15 +1,37 @@
|
|
|
1
1
|
import {
|
|
2
|
-
buildWebSocketOptions
|
|
3
|
-
|
|
2
|
+
buildWebSocketOptions,
|
|
3
|
+
logger
|
|
4
|
+
} from "./chunk-E6OOH3IC.js";
|
|
4
5
|
|
|
5
6
|
// src/core.ts
|
|
6
|
-
import
|
|
7
|
-
import
|
|
7
|
+
import path10 from "path";
|
|
8
|
+
import os4 from "os";
|
|
8
9
|
import { createRequire } from "module";
|
|
9
10
|
import { execSync as execSync2 } from "child_process";
|
|
10
11
|
import { accessSync } from "fs";
|
|
11
12
|
import { fileURLToPath } from "url";
|
|
12
13
|
|
|
14
|
+
// ../shared/src/testing/failpoints.ts
|
|
15
|
+
var NoopFailpointRegistry = class {
|
|
16
|
+
get enabled() {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
isEnabled() {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
configure() {
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
}
|
|
26
|
+
getTrace() {
|
|
27
|
+
return [];
|
|
28
|
+
}
|
|
29
|
+
hit(_key, _context, fallback) {
|
|
30
|
+
return fallback ? fallback() : void 0;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
var noopFailpointRegistry = new NoopFailpointRegistry();
|
|
34
|
+
|
|
13
35
|
// ../shared/src/serverPermissions.ts
|
|
14
36
|
var EMPTY_SERVER_CAPABILITIES = Object.freeze({
|
|
15
37
|
manageServer: false,
|
|
@@ -38,7 +60,7 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
38
60
|
manageAgents: true,
|
|
39
61
|
manageMachines: true,
|
|
40
62
|
manageMembers: true,
|
|
41
|
-
changeMemberRoles:
|
|
63
|
+
changeMemberRoles: true,
|
|
42
64
|
manageBilling: false,
|
|
43
65
|
joinPublicChannels: true
|
|
44
66
|
}),
|
|
@@ -58,8 +80,10 @@ var SERVER_CAPABILITY_MATRIX = {
|
|
|
58
80
|
var RUNTIMES = [
|
|
59
81
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
60
82
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
61
|
-
{ id: "
|
|
62
|
-
{ id: "
|
|
83
|
+
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
84
|
+
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
85
|
+
{ id: "cursor", displayName: "Cursor CLI", binary: "agent", supported: true },
|
|
86
|
+
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true }
|
|
63
87
|
];
|
|
64
88
|
var PLAN_CONFIG = {
|
|
65
89
|
free: {
|
|
@@ -97,13 +121,13 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
97
121
|
|
|
98
122
|
// src/agentProcessManager.ts
|
|
99
123
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
100
|
-
import
|
|
101
|
-
import
|
|
124
|
+
import path9 from "path";
|
|
125
|
+
import os3 from "os";
|
|
102
126
|
|
|
103
127
|
// src/drivers/claude.ts
|
|
104
128
|
import { spawn } from "child_process";
|
|
105
129
|
import { writeFileSync } from "fs";
|
|
106
|
-
import
|
|
130
|
+
import path2 from "path";
|
|
107
131
|
|
|
108
132
|
// src/drivers/systemPrompt.ts
|
|
109
133
|
function toolRef(prefix, name) {
|
|
@@ -145,8 +169,8 @@ You have MCP tools from the "chat" server. Use ONLY these for communication:
|
|
|
145
169
|
8. **${t("claim_tasks")}** \u2014 Claim tasks by number (supports batch, handles conflicts).
|
|
146
170
|
9. **${t("unclaim_task")}** \u2014 Release your claim on a task.
|
|
147
171
|
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
|
|
172
|
+
11. **${t("upload_file")}** \u2014 Upload a file to attach to a message. Returns an attachment ID to pass to send_message.
|
|
173
|
+
12. **${t("view_file")}** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
150
174
|
|
|
151
175
|
CRITICAL RULES:
|
|
152
176
|
${criticalRules.join("\n")}
|
|
@@ -166,18 +190,18 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
166
190
|
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
167
191
|
|
|
168
192
|
\`\`\`
|
|
169
|
-
[target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00] @richard: hello everyone
|
|
193
|
+
[target=#general msg=a1b2c3d4 time=2026-03-15T01:00:00 type=human] @richard: hello everyone
|
|
170
194
|
[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
|
|
195
|
+
[target=dm:@richard msg=c9d0e1f2 time=2026-03-15T01:00:02 type=human] @richard: hey, can you help?
|
|
196
|
+
[target=#general:a1b2c3d4 msg=f3a4b5c6 time=2026-03-15T01:00:03 type=human] @richard: thread reply
|
|
197
|
+
[target=dm:@richard:x9y8z7a0 msg=d7e8f9a0 time=2026-03-15T01:00:04 type=human] @richard: DM thread reply
|
|
174
198
|
\`\`\`
|
|
175
199
|
|
|
176
200
|
Header fields:
|
|
177
201
|
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
178
202
|
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
179
203
|
- \`time=\` \u2014 timestamp.
|
|
180
|
-
- \`type
|
|
204
|
+
- \`type=\` \u2014 sender kind. Values are \`human\`, \`agent\`, or \`system\`.
|
|
181
205
|
|
|
182
206
|
### Sending messages
|
|
183
207
|
|
|
@@ -295,6 +319,8 @@ Keep the user informed. They cannot see your internal reasoning, so:
|
|
|
295
319
|
|
|
296
320
|
Use plain-text @mentions (e.g. \`@alice\`) and #channel references (e.g. \`#general\`, \`#1\`) \u2014 no HTML tags.
|
|
297
321
|
|
|
322
|
+
When referring to a task in message content, use the explicit form \`task #123\`. Do not use bare \`#123\` for task references \u2014 bare \`#123\` is ambiguous with PR, issue, and release references.
|
|
323
|
+
|
|
298
324
|
When referencing a channel or mentioning someone, write them as plain text without backticks. Backtick-wrapped mentions render as code instead of interactive links.
|
|
299
325
|
|
|
300
326
|
### Formatting \u2014 URLs in non-English text
|
|
@@ -403,11 +429,83 @@ ${config.description}. This may evolve.`;
|
|
|
403
429
|
return prompt;
|
|
404
430
|
}
|
|
405
431
|
|
|
432
|
+
// src/drivers/probe.ts
|
|
433
|
+
import { execFileSync } from "child_process";
|
|
434
|
+
import { existsSync } from "fs";
|
|
435
|
+
import path from "path";
|
|
436
|
+
function normalizeExecOutput(raw) {
|
|
437
|
+
return Buffer.isBuffer(raw) ? raw.toString("utf8") : String(raw ?? "");
|
|
438
|
+
}
|
|
439
|
+
function resolveCommandOnPath(command, deps = {}) {
|
|
440
|
+
const platform = deps.platform ?? process.platform;
|
|
441
|
+
const env = deps.env ?? process.env;
|
|
442
|
+
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
443
|
+
const locator = platform === "win32" ? "where" : "which";
|
|
444
|
+
try {
|
|
445
|
+
const output = normalizeExecOutput(execFileSyncFn(locator, [command], {
|
|
446
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
447
|
+
env
|
|
448
|
+
}));
|
|
449
|
+
const resolved = output.trim().split(/\r?\n/)[0];
|
|
450
|
+
return resolved || null;
|
|
451
|
+
} catch {
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
function firstExistingPath(candidates, deps = {}) {
|
|
456
|
+
const exists = deps.existsSyncFn ?? existsSync;
|
|
457
|
+
for (const candidate of candidates) {
|
|
458
|
+
if (exists(candidate)) return candidate;
|
|
459
|
+
}
|
|
460
|
+
return null;
|
|
461
|
+
}
|
|
462
|
+
function readCommandVersion(command, args = [], deps = {}) {
|
|
463
|
+
const env = deps.env ?? process.env;
|
|
464
|
+
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
465
|
+
try {
|
|
466
|
+
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
467
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
468
|
+
env,
|
|
469
|
+
timeout: 5e3
|
|
470
|
+
}));
|
|
471
|
+
return output.trim().split(/\r?\n/)[0] || null;
|
|
472
|
+
} catch {
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
function resolveHomePath(relativePath, deps = {}) {
|
|
477
|
+
const homeDir = deps.homeDir ?? deps.env?.HOME ?? process.env.HOME ?? "";
|
|
478
|
+
return path.join(homeDir, relativePath);
|
|
479
|
+
}
|
|
480
|
+
|
|
406
481
|
// src/drivers/claude.ts
|
|
482
|
+
var CLAUDE_DESKTOP_CLI_RELATIVE_PATH = path2.join("Applications", "Claude Code URL Handler.app", "Contents", "MacOS", "claude");
|
|
483
|
+
var CLAUDE_DESKTOP_CLI_SYSTEM_PATH = "/Applications/Claude Code URL Handler.app/Contents/MacOS/claude";
|
|
484
|
+
function resolveClaudeCommand(deps = {}) {
|
|
485
|
+
const pathCommand = resolveCommandOnPath("claude", deps);
|
|
486
|
+
if (pathCommand) return pathCommand;
|
|
487
|
+
if ((deps.platform ?? process.platform) !== "darwin") return null;
|
|
488
|
+
return firstExistingPath([
|
|
489
|
+
resolveHomePath(CLAUDE_DESKTOP_CLI_RELATIVE_PATH, deps),
|
|
490
|
+
CLAUDE_DESKTOP_CLI_SYSTEM_PATH
|
|
491
|
+
], deps);
|
|
492
|
+
}
|
|
493
|
+
function probeClaude(deps = {}) {
|
|
494
|
+
const command = resolveClaudeCommand(deps);
|
|
495
|
+
if (!command) return { available: false };
|
|
496
|
+
return {
|
|
497
|
+
available: true,
|
|
498
|
+
version: readCommandVersion(command, [], deps) ?? void 0
|
|
499
|
+
};
|
|
500
|
+
}
|
|
407
501
|
var ClaudeDriver = class {
|
|
408
502
|
id = "claude";
|
|
409
503
|
supportsStdinNotification = true;
|
|
410
504
|
mcpToolPrefix = "mcp__chat__";
|
|
505
|
+
busyDeliveryMode = "notification";
|
|
506
|
+
probe() {
|
|
507
|
+
return probeClaude();
|
|
508
|
+
}
|
|
411
509
|
spawn(ctx) {
|
|
412
510
|
const mcpArgs = [
|
|
413
511
|
ctx.chatBridgePath,
|
|
@@ -429,7 +527,7 @@ var ClaudeDriver = class {
|
|
|
429
527
|
});
|
|
430
528
|
let mcpConfigArg;
|
|
431
529
|
if (process.platform === "win32") {
|
|
432
|
-
const mcpConfigPath =
|
|
530
|
+
const mcpConfigPath = path2.join(ctx.workingDirectory, ".slock-claude-mcp.json");
|
|
433
531
|
writeFileSync(mcpConfigPath, mcpConfig, "utf8");
|
|
434
532
|
mcpConfigArg = mcpConfigPath;
|
|
435
533
|
} else {
|
|
@@ -455,7 +553,7 @@ var ClaudeDriver = class {
|
|
|
455
553
|
}
|
|
456
554
|
const spawnEnv = { ...process.env, FORCE_COLOR: "0", ...ctx.config.envVars || {} };
|
|
457
555
|
delete spawnEnv.CLAUDECODE;
|
|
458
|
-
const proc = spawn("claude", args, {
|
|
556
|
+
const proc = spawn(resolveClaudeCommand() ?? "claude", args, {
|
|
459
557
|
cwd: ctx.workingDirectory,
|
|
460
558
|
stdio: ["pipe", "pipe", "pipe"],
|
|
461
559
|
env: spawnEnv,
|
|
@@ -630,11 +728,23 @@ var ClaudeDriver = class {
|
|
|
630
728
|
|
|
631
729
|
// src/drivers/codex.ts
|
|
632
730
|
import { spawn as spawn2, execSync } from "child_process";
|
|
633
|
-
import { existsSync } from "fs";
|
|
634
|
-
import
|
|
731
|
+
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
732
|
+
import os from "os";
|
|
733
|
+
import path3 from "path";
|
|
734
|
+
function getCodexNotificationErrorMessage(params) {
|
|
735
|
+
const topLevelMessage = params?.message;
|
|
736
|
+
if (typeof topLevelMessage === "string" && topLevelMessage.trim()) {
|
|
737
|
+
return topLevelMessage;
|
|
738
|
+
}
|
|
739
|
+
const nestedMessage = params?.error?.message;
|
|
740
|
+
if (typeof nestedMessage === "string" && nestedMessage.trim()) {
|
|
741
|
+
return nestedMessage;
|
|
742
|
+
}
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
635
745
|
function ensureGitRepo(workingDirectory) {
|
|
636
|
-
const gitDir =
|
|
637
|
-
if (
|
|
746
|
+
const gitDir = path3.join(workingDirectory, ".git");
|
|
747
|
+
if (existsSync2(gitDir)) return;
|
|
638
748
|
execSync("git init", { cwd: workingDirectory, stdio: "pipe" });
|
|
639
749
|
execSync("git add -A && git commit --allow-empty -m 'init'", {
|
|
640
750
|
cwd: workingDirectory,
|
|
@@ -648,22 +758,48 @@ function ensureGitRepo(workingDirectory) {
|
|
|
648
758
|
}
|
|
649
759
|
});
|
|
650
760
|
}
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
761
|
+
var CODEX_DESKTOP_BUNDLE_PATH = "/Applications/Codex.app/Contents/Resources/codex";
|
|
762
|
+
function resolveCodexCommand(deps = {}) {
|
|
763
|
+
const pathCommand = resolveCommandOnPath("codex", deps);
|
|
764
|
+
if (pathCommand) return pathCommand;
|
|
765
|
+
if ((deps.platform ?? process.platform) !== "darwin") return null;
|
|
766
|
+
return firstExistingPath([CODEX_DESKTOP_BUNDLE_PATH], deps);
|
|
767
|
+
}
|
|
768
|
+
function probeCodex(deps = {}) {
|
|
769
|
+
if ((deps.platform ?? process.platform) === "win32") {
|
|
770
|
+
try {
|
|
771
|
+
const resolved = resolveCodexSpawn([], deps);
|
|
772
|
+
return {
|
|
773
|
+
available: true,
|
|
774
|
+
version: readCommandVersion(resolved.command, resolved.args, deps) ?? void 0
|
|
775
|
+
};
|
|
776
|
+
} catch {
|
|
777
|
+
return { available: false };
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
const command = resolveCodexCommand(deps);
|
|
781
|
+
if (!command) return { available: false };
|
|
782
|
+
return {
|
|
783
|
+
available: true,
|
|
784
|
+
version: readCommandVersion(command, [], deps) ?? void 0
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
function resolveCodexSpawn(commandArgs, deps = {}) {
|
|
788
|
+
if ((deps.platform ?? process.platform) !== "win32") {
|
|
789
|
+
return { command: resolveCodexCommand(deps) ?? "codex", args: commandArgs };
|
|
654
790
|
}
|
|
655
791
|
let codexEntry = null;
|
|
656
792
|
try {
|
|
657
793
|
const globalRoot = execSync("npm root -g", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
658
|
-
const candidate =
|
|
659
|
-
if (
|
|
794
|
+
const candidate = path3.join(globalRoot, "@openai", "codex", "bin", "codex.js");
|
|
795
|
+
if (existsSync2(candidate)) codexEntry = candidate;
|
|
660
796
|
} catch {
|
|
661
797
|
}
|
|
662
798
|
if (!codexEntry) {
|
|
663
799
|
try {
|
|
664
800
|
const cmdPath = execSync("where codex", { encoding: "utf8", stdio: ["pipe", "pipe", "pipe"] }).trim().split(/\r?\n/)[0];
|
|
665
|
-
const candidate =
|
|
666
|
-
if (
|
|
801
|
+
const candidate = path3.join(path3.dirname(cmdPath), "node_modules", "@openai", "codex", "bin", "codex.js");
|
|
802
|
+
if (existsSync2(candidate)) codexEntry = candidate;
|
|
667
803
|
} catch {
|
|
668
804
|
}
|
|
669
805
|
}
|
|
@@ -692,7 +828,10 @@ var CodexDriver = class {
|
|
|
692
828
|
id = "codex";
|
|
693
829
|
supportsStdinNotification = true;
|
|
694
830
|
mcpToolPrefix = "mcp_chat_";
|
|
695
|
-
|
|
831
|
+
busyDeliveryMode = "direct";
|
|
832
|
+
probe() {
|
|
833
|
+
return probeCodex();
|
|
834
|
+
}
|
|
696
835
|
process = null;
|
|
697
836
|
requestId = 0;
|
|
698
837
|
threadId = null;
|
|
@@ -931,7 +1070,10 @@ var CodexDriver = class {
|
|
|
931
1070
|
break;
|
|
932
1071
|
}
|
|
933
1072
|
case "error":
|
|
934
|
-
events.push({
|
|
1073
|
+
events.push({
|
|
1074
|
+
kind: "error",
|
|
1075
|
+
message: getCodexNotificationErrorMessage(message.params) || "Unknown Codex app-server error"
|
|
1076
|
+
});
|
|
935
1077
|
break;
|
|
936
1078
|
}
|
|
937
1079
|
return events;
|
|
@@ -1072,13 +1214,564 @@ var CodexDriver = class {
|
|
|
1072
1214
|
params
|
|
1073
1215
|
}) + "\n");
|
|
1074
1216
|
}
|
|
1217
|
+
async detectModels() {
|
|
1218
|
+
return detectCodexModels();
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
function detectCodexModels(home = os.homedir()) {
|
|
1222
|
+
const cachePath = path3.join(home, ".codex", "models_cache.json");
|
|
1223
|
+
const configPath = path3.join(home, ".codex", "config.toml");
|
|
1224
|
+
let models = [];
|
|
1225
|
+
try {
|
|
1226
|
+
const raw = readFileSync(cachePath, "utf8");
|
|
1227
|
+
const parsed = JSON.parse(raw);
|
|
1228
|
+
const entries = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
1229
|
+
for (const entry of entries) {
|
|
1230
|
+
const slug = typeof entry?.slug === "string" ? entry.slug : null;
|
|
1231
|
+
if (!slug) continue;
|
|
1232
|
+
if (entry?.visibility && entry.visibility !== "public") continue;
|
|
1233
|
+
if (entry?.supported_in_api === false) continue;
|
|
1234
|
+
const label = typeof entry?.display_name === "string" && entry.display_name.length > 0 ? entry.display_name : slug;
|
|
1235
|
+
models.push({ id: slug, label });
|
|
1236
|
+
}
|
|
1237
|
+
} catch {
|
|
1238
|
+
return null;
|
|
1239
|
+
}
|
|
1240
|
+
if (models.length === 0) return null;
|
|
1241
|
+
let defaultModel;
|
|
1242
|
+
try {
|
|
1243
|
+
const raw = readFileSync(configPath, "utf8");
|
|
1244
|
+
const match = raw.match(/^\s*model\s*=\s*"([^"]+)"/m);
|
|
1245
|
+
if (match) defaultModel = match[1];
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1248
|
+
return { models, default: defaultModel };
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// src/drivers/copilot.ts
|
|
1252
|
+
import { spawn as spawn3 } from "child_process";
|
|
1253
|
+
import path4 from "path";
|
|
1254
|
+
import { writeFileSync as writeFileSync2 } from "fs";
|
|
1255
|
+
var CopilotDriver = class {
|
|
1256
|
+
id = "copilot";
|
|
1257
|
+
supportsStdinNotification = false;
|
|
1258
|
+
mcpToolPrefix = "";
|
|
1259
|
+
busyDeliveryMode = "none";
|
|
1260
|
+
sessionId = null;
|
|
1261
|
+
sessionAnnounced = false;
|
|
1262
|
+
spawn(ctx) {
|
|
1263
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
1264
|
+
this.sessionAnnounced = false;
|
|
1265
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1266
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1267
|
+
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];
|
|
1268
|
+
const mcpConfigPath = path4.join(ctx.workingDirectory, ".slock-copilot-mcp.json");
|
|
1269
|
+
writeFileSync2(mcpConfigPath, JSON.stringify({
|
|
1270
|
+
mcpServers: {
|
|
1271
|
+
chat: {
|
|
1272
|
+
command: mcpCommand,
|
|
1273
|
+
args: mcpArgs
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
}), "utf8");
|
|
1277
|
+
const args = [
|
|
1278
|
+
"--output-format",
|
|
1279
|
+
"json",
|
|
1280
|
+
"--allow-all-tools",
|
|
1281
|
+
"--allow-all-paths",
|
|
1282
|
+
"--additional-mcp-config",
|
|
1283
|
+
`@${mcpConfigPath}`,
|
|
1284
|
+
"-p",
|
|
1285
|
+
ctx.prompt
|
|
1286
|
+
];
|
|
1287
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1288
|
+
args.push("--model", ctx.config.model);
|
|
1289
|
+
}
|
|
1290
|
+
if (ctx.config.reasoningEffort) {
|
|
1291
|
+
args.push("--effort", ctx.config.reasoningEffort);
|
|
1292
|
+
}
|
|
1293
|
+
if (ctx.config.sessionId) {
|
|
1294
|
+
args.push(`--resume=${ctx.config.sessionId}`);
|
|
1295
|
+
}
|
|
1296
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1297
|
+
const proc = spawn3("copilot", args, {
|
|
1298
|
+
cwd: ctx.workingDirectory,
|
|
1299
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1300
|
+
env: spawnEnv,
|
|
1301
|
+
shell: process.platform === "win32"
|
|
1302
|
+
});
|
|
1303
|
+
return { process: proc };
|
|
1304
|
+
}
|
|
1305
|
+
parseLine(line) {
|
|
1306
|
+
let event;
|
|
1307
|
+
try {
|
|
1308
|
+
event = JSON.parse(line);
|
|
1309
|
+
} catch {
|
|
1310
|
+
return [];
|
|
1311
|
+
}
|
|
1312
|
+
const events = [];
|
|
1313
|
+
const eventType = event.type;
|
|
1314
|
+
const data = event.data || {};
|
|
1315
|
+
if (event.ephemeral && eventType?.startsWith("session.")) {
|
|
1316
|
+
return [];
|
|
1317
|
+
}
|
|
1318
|
+
switch (eventType) {
|
|
1319
|
+
case "assistant.turn_start":
|
|
1320
|
+
if (!this.sessionAnnounced && data.sessionId) {
|
|
1321
|
+
this.sessionId = data.sessionId;
|
|
1322
|
+
events.push({ kind: "session_init", sessionId: data.sessionId });
|
|
1323
|
+
this.sessionAnnounced = true;
|
|
1324
|
+
}
|
|
1325
|
+
events.push({ kind: "thinking", text: "" });
|
|
1326
|
+
break;
|
|
1327
|
+
case "assistant.reasoning":
|
|
1328
|
+
if (data.content) {
|
|
1329
|
+
events.push({ kind: "thinking", text: data.content });
|
|
1330
|
+
}
|
|
1331
|
+
break;
|
|
1332
|
+
case "assistant.message_delta":
|
|
1333
|
+
if (data.deltaContent) {
|
|
1334
|
+
events.push({ kind: "text", text: data.deltaContent });
|
|
1335
|
+
}
|
|
1336
|
+
break;
|
|
1337
|
+
case "assistant.message": {
|
|
1338
|
+
if (Array.isArray(data.toolRequests)) {
|
|
1339
|
+
for (const req of data.toolRequests) {
|
|
1340
|
+
events.push({
|
|
1341
|
+
kind: "tool_call",
|
|
1342
|
+
name: req.name || req.toolName || "unknown_tool",
|
|
1343
|
+
input: req.arguments || req.parameters || req.input || {}
|
|
1344
|
+
});
|
|
1345
|
+
}
|
|
1346
|
+
}
|
|
1347
|
+
if (!event.ephemeral && data.content && typeof data.content === "string") {
|
|
1348
|
+
if (!Array.isArray(data.toolRequests) || data.toolRequests.length === 0) {
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
case "assistant.turn_end":
|
|
1354
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1355
|
+
break;
|
|
1356
|
+
case "result": {
|
|
1357
|
+
const resultSessionId = event.sessionId || data.sessionId;
|
|
1358
|
+
const exitCode = event.exitCode ?? data.exitCode;
|
|
1359
|
+
if (!this.sessionAnnounced && resultSessionId) {
|
|
1360
|
+
this.sessionId = resultSessionId;
|
|
1361
|
+
events.push({ kind: "session_init", sessionId: resultSessionId });
|
|
1362
|
+
this.sessionAnnounced = true;
|
|
1363
|
+
}
|
|
1364
|
+
if (exitCode && exitCode !== 0) {
|
|
1365
|
+
events.push({ kind: "error", message: `Copilot exited with code ${exitCode}` });
|
|
1366
|
+
}
|
|
1367
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1368
|
+
break;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
return events;
|
|
1372
|
+
}
|
|
1373
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1374
|
+
return null;
|
|
1375
|
+
}
|
|
1376
|
+
buildSystemPrompt(config, _agentId) {
|
|
1377
|
+
return buildBaseSystemPrompt(config, {
|
|
1378
|
+
toolPrefix: "",
|
|
1379
|
+
extraCriticalRules: [
|
|
1380
|
+
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
1381
|
+
],
|
|
1382
|
+
postStartupNotes: [],
|
|
1383
|
+
includeStdinNotificationSection: false,
|
|
1384
|
+
messageNotificationStyle: "poll"
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
toolDisplayName(name) {
|
|
1388
|
+
if (name === "list_tasks") return "Viewing task board\u2026";
|
|
1389
|
+
if (name === "create_tasks") return "Creating tasks\u2026";
|
|
1390
|
+
if (name === "claim_tasks") return "Claiming tasks\u2026";
|
|
1391
|
+
if (name === "unclaim_task") return "Unclaiming task\u2026";
|
|
1392
|
+
if (name === "update_task_status") return "Updating task\u2026";
|
|
1393
|
+
if (name === "send_message" || name === "receive_message" || name === "read_history" || name === "list_server") return "";
|
|
1394
|
+
if (name === "shell" || name === "Shell") return "Running command\u2026";
|
|
1395
|
+
if (name === "read_file" || name === "ReadFile") return "Reading file\u2026";
|
|
1396
|
+
if (name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") return "Editing file\u2026";
|
|
1397
|
+
if (name === "search_files" || name === "Glob" || name === "Grep") return "Searching code\u2026";
|
|
1398
|
+
if (name === "web_search" || name === "SearchWeb") return "Searching web\u2026";
|
|
1399
|
+
if (name === "fetch_url" || name === "FetchURL") return "Fetching web\u2026";
|
|
1400
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1401
|
+
}
|
|
1402
|
+
summarizeToolInput(name, input) {
|
|
1403
|
+
if (!input || typeof input !== "object") return "";
|
|
1404
|
+
try {
|
|
1405
|
+
if (name === "shell" || name === "Shell") {
|
|
1406
|
+
const cmd = input.command || "";
|
|
1407
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1408
|
+
}
|
|
1409
|
+
if (name === "read_file" || name === "ReadFile" || name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") {
|
|
1410
|
+
return input.path || input.file_path || "";
|
|
1411
|
+
}
|
|
1412
|
+
if (name === "Glob" || name === "Grep" || name === "search_files") return input.pattern || input.query || "";
|
|
1413
|
+
if (name === "web_search" || name === "SearchWeb") return input.query || "";
|
|
1414
|
+
if (name === "fetch_url" || name === "FetchURL") return input.url || "";
|
|
1415
|
+
if (name === "send_message") return input.target || input.channel || "";
|
|
1416
|
+
if (name === "read_history") return input.target || input.channel || "";
|
|
1417
|
+
if (name === "list_tasks") return input.channel || "";
|
|
1418
|
+
if (name === "create_tasks") return input.channel || "";
|
|
1419
|
+
if (name === "claim_tasks") {
|
|
1420
|
+
const nums = input.task_numbers;
|
|
1421
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1422
|
+
}
|
|
1423
|
+
if (name === "unclaim_task" || name === "update_task_status") {
|
|
1424
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
1425
|
+
}
|
|
1426
|
+
return "";
|
|
1427
|
+
} catch {
|
|
1428
|
+
return "";
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
};
|
|
1432
|
+
|
|
1433
|
+
// src/drivers/cursor.ts
|
|
1434
|
+
import { spawn as spawn4 } from "child_process";
|
|
1435
|
+
import { writeFileSync as writeFileSync3, mkdirSync, existsSync as existsSync3 } from "fs";
|
|
1436
|
+
import path5 from "path";
|
|
1437
|
+
var CursorDriver = class {
|
|
1438
|
+
id = "cursor";
|
|
1439
|
+
supportsStdinNotification = false;
|
|
1440
|
+
mcpToolPrefix = "mcp__chat__";
|
|
1441
|
+
busyDeliveryMode = "none";
|
|
1442
|
+
spawn(ctx) {
|
|
1443
|
+
const cursorDir = path5.join(ctx.workingDirectory, ".cursor");
|
|
1444
|
+
if (!existsSync3(cursorDir)) {
|
|
1445
|
+
mkdirSync(cursorDir, { recursive: true });
|
|
1446
|
+
}
|
|
1447
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1448
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1449
|
+
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];
|
|
1450
|
+
const mcpConfigPath = path5.join(cursorDir, "mcp.json");
|
|
1451
|
+
writeFileSync3(mcpConfigPath, JSON.stringify({
|
|
1452
|
+
mcpServers: {
|
|
1453
|
+
chat: {
|
|
1454
|
+
command: mcpCommand,
|
|
1455
|
+
args: mcpArgs
|
|
1456
|
+
}
|
|
1457
|
+
}
|
|
1458
|
+
}), "utf8");
|
|
1459
|
+
const args = [
|
|
1460
|
+
"--print",
|
|
1461
|
+
"--output-format",
|
|
1462
|
+
"stream-json",
|
|
1463
|
+
"--yolo",
|
|
1464
|
+
"--approve-mcps",
|
|
1465
|
+
"--trust"
|
|
1466
|
+
];
|
|
1467
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1468
|
+
args.push("--model", ctx.config.model);
|
|
1469
|
+
}
|
|
1470
|
+
if (ctx.config.sessionId) {
|
|
1471
|
+
args.push("--resume", ctx.config.sessionId);
|
|
1472
|
+
}
|
|
1473
|
+
args.push(ctx.prompt);
|
|
1474
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1475
|
+
const proc = spawn4("agent", args, {
|
|
1476
|
+
cwd: ctx.workingDirectory,
|
|
1477
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1478
|
+
env: spawnEnv,
|
|
1479
|
+
shell: process.platform === "win32"
|
|
1480
|
+
});
|
|
1481
|
+
return { process: proc };
|
|
1482
|
+
}
|
|
1483
|
+
parseLine(line) {
|
|
1484
|
+
let event;
|
|
1485
|
+
try {
|
|
1486
|
+
event = JSON.parse(line);
|
|
1487
|
+
} catch {
|
|
1488
|
+
return [];
|
|
1489
|
+
}
|
|
1490
|
+
const events = [];
|
|
1491
|
+
switch (event.type) {
|
|
1492
|
+
case "system":
|
|
1493
|
+
if (event.subtype === "init" && event.session_id) {
|
|
1494
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
1495
|
+
}
|
|
1496
|
+
break;
|
|
1497
|
+
case "assistant": {
|
|
1498
|
+
const content = event.message?.content;
|
|
1499
|
+
if (Array.isArray(content)) {
|
|
1500
|
+
for (const block of content) {
|
|
1501
|
+
if (block.type === "thinking" && block.thinking) {
|
|
1502
|
+
events.push({ kind: "thinking", text: block.thinking });
|
|
1503
|
+
} else if (block.type === "text" && block.text) {
|
|
1504
|
+
events.push({ kind: "text", text: block.text });
|
|
1505
|
+
} else if (block.type === "tool_use") {
|
|
1506
|
+
events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
}
|
|
1510
|
+
break;
|
|
1511
|
+
}
|
|
1512
|
+
case "result": {
|
|
1513
|
+
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
1514
|
+
if (subtype !== "success" || event.is_error) {
|
|
1515
|
+
const parts = [];
|
|
1516
|
+
if (Array.isArray(event.errors)) {
|
|
1517
|
+
for (const err of event.errors) {
|
|
1518
|
+
if (typeof err === "string" && err.trim()) parts.push(err.trim());
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
if (typeof event.result === "string" && event.result.trim()) {
|
|
1522
|
+
parts.push(event.result.trim());
|
|
1523
|
+
}
|
|
1524
|
+
const detail = parts.join(" | ") || "Execution failed";
|
|
1525
|
+
events.push({ kind: "error", message: detail });
|
|
1526
|
+
}
|
|
1527
|
+
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1531
|
+
return events;
|
|
1532
|
+
}
|
|
1533
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
buildSystemPrompt(config, _agentId) {
|
|
1537
|
+
return buildBaseSystemPrompt(config, {
|
|
1538
|
+
toolPrefix: "mcp__chat__",
|
|
1539
|
+
extraCriticalRules: [
|
|
1540
|
+
"- Do NOT use bash/curl/sqlite to send or receive messages. The MCP tools handle everything."
|
|
1541
|
+
],
|
|
1542
|
+
postStartupNotes: [],
|
|
1543
|
+
includeStdinNotificationSection: false,
|
|
1544
|
+
messageNotificationStyle: "poll"
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
toolDisplayName(name) {
|
|
1548
|
+
if (name === "mcp__chat__upload_file") return "Uploading file\u2026";
|
|
1549
|
+
if (name === "mcp__chat__view_file") return "Viewing file\u2026";
|
|
1550
|
+
if (name === "mcp__chat__list_tasks") return "Listing tasks\u2026";
|
|
1551
|
+
if (name === "mcp__chat__create_tasks") return "Creating tasks\u2026";
|
|
1552
|
+
if (name === "mcp__chat__claim_tasks") return "Claiming tasks\u2026";
|
|
1553
|
+
if (name === "mcp__chat__unclaim_task") return "Unclaiming task\u2026";
|
|
1554
|
+
if (name === "mcp__chat__update_task_status") return "Updating task status\u2026";
|
|
1555
|
+
if (name === "mcp__chat__list_server") return "Listing server\u2026";
|
|
1556
|
+
if (name === "mcp__chat__read_history") return "Reading history\u2026";
|
|
1557
|
+
if (name === "mcp__chat__search_messages") return "Searching messages\u2026";
|
|
1558
|
+
if (name === "mcp__chat__check_messages") return "Checking messages\u2026";
|
|
1559
|
+
if (name.startsWith("mcp__chat__")) return "";
|
|
1560
|
+
if (name === "Read" || name === "read_file") return "Reading file\u2026";
|
|
1561
|
+
if (name === "Write" || name === "write_file") return "Writing file\u2026";
|
|
1562
|
+
if (name === "Edit" || name === "edit_file") return "Editing file\u2026";
|
|
1563
|
+
if (name === "Bash" || name === "bash") return "Running command\u2026";
|
|
1564
|
+
if (name === "Glob" || name === "glob") return "Searching files\u2026";
|
|
1565
|
+
if (name === "Grep" || name === "grep") return "Searching code\u2026";
|
|
1566
|
+
if (name === "WebFetch" || name === "web_fetch") return "Fetching web\u2026";
|
|
1567
|
+
if (name === "WebSearch" || name === "web_search") return "Searching web\u2026";
|
|
1568
|
+
if (name === "TodoWrite") return "Updating tasks\u2026";
|
|
1569
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1570
|
+
}
|
|
1571
|
+
summarizeToolInput(name, input) {
|
|
1572
|
+
if (!input || typeof input !== "object") return "";
|
|
1573
|
+
try {
|
|
1574
|
+
if (name === "Read" || name === "read_file") return input.file_path || input.path || "";
|
|
1575
|
+
if (name === "Write" || name === "write_file") return input.file_path || input.path || "";
|
|
1576
|
+
if (name === "Edit" || name === "edit_file") return input.file_path || input.path || "";
|
|
1577
|
+
if (name === "Bash" || name === "bash") {
|
|
1578
|
+
const cmd = input.command || "";
|
|
1579
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1580
|
+
}
|
|
1581
|
+
if (name === "Glob" || name === "glob") return input.pattern || "";
|
|
1582
|
+
if (name === "Grep" || name === "grep") return input.pattern || "";
|
|
1583
|
+
if (name === "WebFetch" || name === "web_fetch") return input.url || "";
|
|
1584
|
+
if (name === "WebSearch" || name === "web_search") return input.query || "";
|
|
1585
|
+
if (name === "mcp__chat__send_message") {
|
|
1586
|
+
return input.target || input.channel || (input.dm_to ? `DM:@${input.dm_to}` : "");
|
|
1587
|
+
}
|
|
1588
|
+
if (name === "mcp__chat__read_history") return input.target || input.channel || "";
|
|
1589
|
+
if (name === "mcp__chat__search_messages") return input.query || "";
|
|
1590
|
+
if (name === "mcp__chat__list_tasks") return input.channel || "";
|
|
1591
|
+
if (name === "mcp__chat__create_tasks") return input.channel || "";
|
|
1592
|
+
if (name === "mcp__chat__claim_tasks") {
|
|
1593
|
+
const nums = input.task_numbers;
|
|
1594
|
+
return input.channel ? `${input.channel} #${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1595
|
+
}
|
|
1596
|
+
if (name === "mcp__chat__unclaim_task") {
|
|
1597
|
+
return input.channel ? `${input.channel} #${input.task_number}` : "";
|
|
1598
|
+
}
|
|
1599
|
+
if (name === "mcp__chat__update_task_status") {
|
|
1600
|
+
return input.channel ? `${input.channel} #${input.task_number}` : "";
|
|
1601
|
+
}
|
|
1602
|
+
if (name === "mcp__chat__upload_file") return input.file_path || "";
|
|
1603
|
+
return "";
|
|
1604
|
+
} catch {
|
|
1605
|
+
return "";
|
|
1606
|
+
}
|
|
1607
|
+
}
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
// src/drivers/gemini.ts
|
|
1611
|
+
import { spawn as spawn5 } from "child_process";
|
|
1612
|
+
import { writeFileSync as writeFileSync4, mkdirSync as mkdirSync2, existsSync as existsSync4 } from "fs";
|
|
1613
|
+
import path6 from "path";
|
|
1614
|
+
var GeminiDriver = class {
|
|
1615
|
+
id = "gemini";
|
|
1616
|
+
supportsStdinNotification = false;
|
|
1617
|
+
mcpToolPrefix = "";
|
|
1618
|
+
busyDeliveryMode = "none";
|
|
1619
|
+
sessionId = null;
|
|
1620
|
+
sessionAnnounced = false;
|
|
1621
|
+
spawn(ctx) {
|
|
1622
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
1623
|
+
this.sessionAnnounced = false;
|
|
1624
|
+
const geminiDir = path6.join(ctx.workingDirectory, ".gemini");
|
|
1625
|
+
if (!existsSync4(geminiDir)) {
|
|
1626
|
+
mkdirSync2(geminiDir, { recursive: true });
|
|
1627
|
+
}
|
|
1628
|
+
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1629
|
+
const mcpCommand = isTsSource ? "npx" : "node";
|
|
1630
|
+
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];
|
|
1631
|
+
const settingsPath = path6.join(geminiDir, "settings.json");
|
|
1632
|
+
writeFileSync4(settingsPath, JSON.stringify({
|
|
1633
|
+
mcpServers: {
|
|
1634
|
+
chat: {
|
|
1635
|
+
command: mcpCommand,
|
|
1636
|
+
args: mcpArgs
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
}), "utf8");
|
|
1640
|
+
const args = [
|
|
1641
|
+
"--output-format",
|
|
1642
|
+
"stream-json",
|
|
1643
|
+
"--yolo",
|
|
1644
|
+
"-p",
|
|
1645
|
+
ctx.prompt
|
|
1646
|
+
];
|
|
1647
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
1648
|
+
args.push("--model", ctx.config.model);
|
|
1649
|
+
}
|
|
1650
|
+
if (ctx.config.sessionId) {
|
|
1651
|
+
args.push("--resume", ctx.config.sessionId);
|
|
1652
|
+
}
|
|
1653
|
+
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1", ...ctx.config.envVars || {} };
|
|
1654
|
+
const proc = spawn5("gemini", args, {
|
|
1655
|
+
cwd: ctx.workingDirectory,
|
|
1656
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
1657
|
+
env: spawnEnv,
|
|
1658
|
+
shell: process.platform === "win32"
|
|
1659
|
+
});
|
|
1660
|
+
return { process: proc };
|
|
1661
|
+
}
|
|
1662
|
+
parseLine(line) {
|
|
1663
|
+
let event;
|
|
1664
|
+
try {
|
|
1665
|
+
event = JSON.parse(line);
|
|
1666
|
+
} catch {
|
|
1667
|
+
return [];
|
|
1668
|
+
}
|
|
1669
|
+
const events = [];
|
|
1670
|
+
switch (event.type) {
|
|
1671
|
+
case "init":
|
|
1672
|
+
if (event.session_id) {
|
|
1673
|
+
this.sessionId = event.session_id;
|
|
1674
|
+
events.push({ kind: "session_init", sessionId: event.session_id });
|
|
1675
|
+
this.sessionAnnounced = true;
|
|
1676
|
+
}
|
|
1677
|
+
break;
|
|
1678
|
+
case "message":
|
|
1679
|
+
if (event.role === "assistant" && event.content) {
|
|
1680
|
+
if (event.delta) {
|
|
1681
|
+
events.push({ kind: "text", text: event.content });
|
|
1682
|
+
} else {
|
|
1683
|
+
events.push({ kind: "text", text: event.content });
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
break;
|
|
1687
|
+
case "tool_use":
|
|
1688
|
+
events.push({
|
|
1689
|
+
kind: "tool_call",
|
|
1690
|
+
name: event.tool_name || "unknown_tool",
|
|
1691
|
+
input: event.parameters
|
|
1692
|
+
});
|
|
1693
|
+
break;
|
|
1694
|
+
case "error":
|
|
1695
|
+
events.push({ kind: "error", message: event.message || "Unknown Gemini error" });
|
|
1696
|
+
break;
|
|
1697
|
+
case "result":
|
|
1698
|
+
if (event.status !== "success") {
|
|
1699
|
+
const raw = event.error_message || event.message || event.error || "";
|
|
1700
|
+
const detail = typeof raw === "string" ? raw : raw?.message || JSON.stringify(raw);
|
|
1701
|
+
const msg = detail ? `Gemini error: ${detail}` : `Gemini session ended with status: ${event.status}`;
|
|
1702
|
+
events.push({ kind: "error", message: msg });
|
|
1703
|
+
}
|
|
1704
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
1705
|
+
break;
|
|
1706
|
+
}
|
|
1707
|
+
return events;
|
|
1708
|
+
}
|
|
1709
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
1710
|
+
return null;
|
|
1711
|
+
}
|
|
1712
|
+
buildSystemPrompt(config, _agentId) {
|
|
1713
|
+
return buildBaseSystemPrompt(config, {
|
|
1714
|
+
toolPrefix: "",
|
|
1715
|
+
extraCriticalRules: [
|
|
1716
|
+
"- Do NOT use shell commands to send or receive messages. The MCP tools handle everything."
|
|
1717
|
+
],
|
|
1718
|
+
postStartupNotes: [],
|
|
1719
|
+
includeStdinNotificationSection: false,
|
|
1720
|
+
messageNotificationStyle: "poll"
|
|
1721
|
+
});
|
|
1722
|
+
}
|
|
1723
|
+
toolDisplayName(name) {
|
|
1724
|
+
if (name === "list_tasks") return "Viewing task board\u2026";
|
|
1725
|
+
if (name === "create_tasks") return "Creating tasks\u2026";
|
|
1726
|
+
if (name === "claim_tasks") return "Claiming tasks\u2026";
|
|
1727
|
+
if (name === "unclaim_task") return "Unclaiming task\u2026";
|
|
1728
|
+
if (name === "update_task_status") return "Updating task\u2026";
|
|
1729
|
+
if (name === "send_message" || name === "receive_message" || name === "read_history" || name === "list_server") return "";
|
|
1730
|
+
if (name === "shell" || name === "Shell") return "Running command\u2026";
|
|
1731
|
+
if (name === "read_file" || name === "ReadFile") return "Reading file\u2026";
|
|
1732
|
+
if (name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") return "Editing file\u2026";
|
|
1733
|
+
if (name === "search_files" || name === "Glob" || name === "Grep") return "Searching code\u2026";
|
|
1734
|
+
if (name === "web_search" || name === "SearchWeb") return "Searching web\u2026";
|
|
1735
|
+
if (name === "fetch_url" || name === "FetchURL") return "Fetching web\u2026";
|
|
1736
|
+
return `Using ${name.length > 20 ? name.slice(0, 20) + "\u2026" : name}\u2026`;
|
|
1737
|
+
}
|
|
1738
|
+
summarizeToolInput(name, input) {
|
|
1739
|
+
if (!input || typeof input !== "object") return "";
|
|
1740
|
+
try {
|
|
1741
|
+
if (name === "shell" || name === "Shell") {
|
|
1742
|
+
const cmd = input.command || "";
|
|
1743
|
+
return cmd.length > 100 ? cmd.slice(0, 100) + "\u2026" : cmd;
|
|
1744
|
+
}
|
|
1745
|
+
if (name === "read_file" || name === "ReadFile" || name === "write_file" || name === "WriteFile" || name === "edit_file" || name === "EditFile") {
|
|
1746
|
+
return input.path || input.file_path || "";
|
|
1747
|
+
}
|
|
1748
|
+
if (name === "Glob" || name === "Grep" || name === "search_files") return input.pattern || input.query || "";
|
|
1749
|
+
if (name === "web_search" || name === "SearchWeb") return input.query || "";
|
|
1750
|
+
if (name === "fetch_url" || name === "FetchURL") return input.url || "";
|
|
1751
|
+
if (name === "send_message") return input.target || input.channel || "";
|
|
1752
|
+
if (name === "read_history") return input.target || input.channel || "";
|
|
1753
|
+
if (name === "list_tasks") return input.channel || "";
|
|
1754
|
+
if (name === "create_tasks") return input.channel || "";
|
|
1755
|
+
if (name === "claim_tasks") {
|
|
1756
|
+
const nums = input.task_numbers;
|
|
1757
|
+
return input.channel ? `${input.channel} #t${Array.isArray(nums) ? nums.join(",#t") : nums}` : "";
|
|
1758
|
+
}
|
|
1759
|
+
if (name === "unclaim_task" || name === "update_task_status") {
|
|
1760
|
+
return input.channel ? `${input.channel} #t${input.task_number}` : "";
|
|
1761
|
+
}
|
|
1762
|
+
return "";
|
|
1763
|
+
} catch {
|
|
1764
|
+
return "";
|
|
1765
|
+
}
|
|
1766
|
+
}
|
|
1075
1767
|
};
|
|
1076
1768
|
|
|
1077
1769
|
// src/drivers/kimi.ts
|
|
1078
1770
|
import { randomUUID } from "crypto";
|
|
1079
|
-
import { spawn as
|
|
1080
|
-
import { existsSync as
|
|
1081
|
-
import
|
|
1771
|
+
import { spawn as spawn6 } from "child_process";
|
|
1772
|
+
import { existsSync as existsSync5, readFileSync as readFileSync2, writeFileSync as writeFileSync5 } from "fs";
|
|
1773
|
+
import os2 from "os";
|
|
1774
|
+
import path7 from "path";
|
|
1082
1775
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
1083
1776
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
1084
1777
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
@@ -1095,7 +1788,7 @@ var KimiDriver = class {
|
|
|
1095
1788
|
id = "kimi";
|
|
1096
1789
|
supportsStdinNotification = true;
|
|
1097
1790
|
mcpToolPrefix = "";
|
|
1098
|
-
|
|
1791
|
+
busyDeliveryMode = "direct";
|
|
1099
1792
|
sessionId = null;
|
|
1100
1793
|
sessionAnnounced = false;
|
|
1101
1794
|
promptRequestId = null;
|
|
@@ -1107,20 +1800,20 @@ var KimiDriver = class {
|
|
|
1107
1800
|
const isTsSource = ctx.chatBridgePath.endsWith(".ts");
|
|
1108
1801
|
const command = isTsSource ? "npx" : "node";
|
|
1109
1802
|
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
|
-
|
|
1803
|
+
const systemPromptPath = path7.join(ctx.workingDirectory, KIMI_SYSTEM_PROMPT_FILE);
|
|
1804
|
+
const agentFilePath = path7.join(ctx.workingDirectory, KIMI_AGENT_FILE);
|
|
1805
|
+
const mcpConfigPath = path7.join(ctx.workingDirectory, KIMI_MCP_FILE);
|
|
1806
|
+
if (!isResume || !existsSync5(systemPromptPath)) {
|
|
1807
|
+
writeFileSync5(systemPromptPath, ctx.prompt, "utf8");
|
|
1115
1808
|
}
|
|
1116
|
-
|
|
1809
|
+
writeFileSync5(agentFilePath, [
|
|
1117
1810
|
"version: 1",
|
|
1118
1811
|
"agent:",
|
|
1119
1812
|
" extend: default",
|
|
1120
1813
|
` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
|
|
1121
1814
|
""
|
|
1122
1815
|
].join("\n"), "utf8");
|
|
1123
|
-
|
|
1816
|
+
writeFileSync5(mcpConfigPath, JSON.stringify({
|
|
1124
1817
|
mcpServers: {
|
|
1125
1818
|
chat: {
|
|
1126
1819
|
command,
|
|
@@ -1142,7 +1835,7 @@ var KimiDriver = class {
|
|
|
1142
1835
|
args.push("--model", ctx.config.model);
|
|
1143
1836
|
}
|
|
1144
1837
|
const spawnEnv = { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
1145
|
-
const proc =
|
|
1838
|
+
const proc = spawn6("kimi", args, {
|
|
1146
1839
|
cwd: ctx.workingDirectory,
|
|
1147
1840
|
stdio: ["pipe", "pipe", "pipe"],
|
|
1148
1841
|
env: spawnEnv,
|
|
@@ -1297,12 +1990,43 @@ var KimiDriver = class {
|
|
|
1297
1990
|
return "";
|
|
1298
1991
|
}
|
|
1299
1992
|
}
|
|
1993
|
+
async detectModels() {
|
|
1994
|
+
return detectKimiModels();
|
|
1995
|
+
}
|
|
1300
1996
|
};
|
|
1997
|
+
function detectKimiModels(home = os2.homedir()) {
|
|
1998
|
+
const configPath = path7.join(home, ".kimi", "config.toml");
|
|
1999
|
+
let raw;
|
|
2000
|
+
try {
|
|
2001
|
+
raw = readFileSync2(configPath, "utf8");
|
|
2002
|
+
} catch {
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
2005
|
+
const models = [];
|
|
2006
|
+
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
2007
|
+
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
2008
|
+
let match;
|
|
2009
|
+
while ((match = lineRe.exec(raw)) !== null) {
|
|
2010
|
+
let key = match[1].trim();
|
|
2011
|
+
if (key.startsWith('"') && key.endsWith('"')) key = key.slice(1, -1);
|
|
2012
|
+
if (!key) continue;
|
|
2013
|
+
models.push({ id: key, label: key });
|
|
2014
|
+
}
|
|
2015
|
+
void sectionRe;
|
|
2016
|
+
if (models.length === 0) return null;
|
|
2017
|
+
let defaultModel;
|
|
2018
|
+
const defaultMatch = raw.match(/^\s*default_model\s*=\s*"([^"]+)"/m);
|
|
2019
|
+
if (defaultMatch) defaultModel = defaultMatch[1];
|
|
2020
|
+
return { models, default: defaultModel };
|
|
2021
|
+
}
|
|
1301
2022
|
|
|
1302
2023
|
// src/drivers/index.ts
|
|
1303
2024
|
var driverFactories = {
|
|
1304
2025
|
claude: () => new ClaudeDriver(),
|
|
1305
2026
|
codex: () => new CodexDriver(),
|
|
2027
|
+
copilot: () => new CopilotDriver(),
|
|
2028
|
+
cursor: () => new CursorDriver(),
|
|
2029
|
+
gemini: () => new GeminiDriver(),
|
|
1306
2030
|
kimi: () => new KimiDriver()
|
|
1307
2031
|
};
|
|
1308
2032
|
function getDriver(runtimeId) {
|
|
@@ -1314,52 +2038,9 @@ function getDriver(runtimeId) {
|
|
|
1314
2038
|
return driver;
|
|
1315
2039
|
}
|
|
1316
2040
|
|
|
1317
|
-
// src/logger.ts
|
|
1318
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
1319
|
-
function timestamp() {
|
|
1320
|
-
const d = /* @__PURE__ */ new Date();
|
|
1321
|
-
const pad = (n) => String(n).padStart(2, "0");
|
|
1322
|
-
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
|
|
1323
|
-
}
|
|
1324
|
-
function format(level, msg) {
|
|
1325
|
-
return `${timestamp()} [${level}] ${msg}`;
|
|
1326
|
-
}
|
|
1327
|
-
function emit(event) {
|
|
1328
|
-
for (const listener of listeners) {
|
|
1329
|
-
listener(event);
|
|
1330
|
-
}
|
|
1331
|
-
}
|
|
1332
|
-
function subscribeDaemonLogs(listener) {
|
|
1333
|
-
listeners.add(listener);
|
|
1334
|
-
return () => {
|
|
1335
|
-
listeners.delete(listener);
|
|
1336
|
-
};
|
|
1337
|
-
}
|
|
1338
|
-
var logger = {
|
|
1339
|
-
info(msg) {
|
|
1340
|
-
const line = format("INFO", msg);
|
|
1341
|
-
console.log(line);
|
|
1342
|
-
emit({ level: "INFO", line, message: msg });
|
|
1343
|
-
},
|
|
1344
|
-
warn(msg) {
|
|
1345
|
-
const line = format("WARN", msg);
|
|
1346
|
-
console.warn(line);
|
|
1347
|
-
emit({ level: "WARN", line, message: msg });
|
|
1348
|
-
},
|
|
1349
|
-
error(msg, err) {
|
|
1350
|
-
const line = format("ERROR", msg);
|
|
1351
|
-
if (err) {
|
|
1352
|
-
console.error(line, err);
|
|
1353
|
-
} else {
|
|
1354
|
-
console.error(line);
|
|
1355
|
-
}
|
|
1356
|
-
emit({ level: "ERROR", line, message: msg, error: err });
|
|
1357
|
-
}
|
|
1358
|
-
};
|
|
1359
|
-
|
|
1360
2041
|
// src/workspaces.ts
|
|
1361
2042
|
import { readdir, rm, stat } from "fs/promises";
|
|
1362
|
-
import
|
|
2043
|
+
import path8 from "path";
|
|
1363
2044
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
1364
2045
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
1365
2046
|
}
|
|
@@ -1367,7 +2048,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
1367
2048
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
1368
2049
|
return null;
|
|
1369
2050
|
}
|
|
1370
|
-
return
|
|
2051
|
+
return path8.join(dataDir, directoryName);
|
|
1371
2052
|
}
|
|
1372
2053
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
1373
2054
|
return {
|
|
@@ -1416,7 +2097,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
1416
2097
|
return summary;
|
|
1417
2098
|
}
|
|
1418
2099
|
const childSummaries = await Promise.all(
|
|
1419
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
2100
|
+
entries.map((entry) => summarizeWorkspaceEntry(path8.join(dirPath, entry.name), entry))
|
|
1420
2101
|
);
|
|
1421
2102
|
for (const childSummary of childSummaries) {
|
|
1422
2103
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -1435,7 +2116,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
1435
2116
|
if (!entry.isDirectory()) {
|
|
1436
2117
|
return null;
|
|
1437
2118
|
}
|
|
1438
|
-
const dirPath =
|
|
2119
|
+
const dirPath = path8.join(dataDir, entry.name);
|
|
1439
2120
|
try {
|
|
1440
2121
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
1441
2122
|
return {
|
|
@@ -1467,7 +2148,7 @@ async function deleteWorkspaceDirectory(dataDir, directoryName) {
|
|
|
1467
2148
|
}
|
|
1468
2149
|
|
|
1469
2150
|
// src/agentProcessManager.ts
|
|
1470
|
-
var DATA_DIR =
|
|
2151
|
+
var DATA_DIR = path9.join(os3.homedir(), ".slock", "agents");
|
|
1471
2152
|
function toLocalTime(iso) {
|
|
1472
2153
|
const d = new Date(iso);
|
|
1473
2154
|
if (isNaN(d.getTime())) return iso;
|
|
@@ -1479,7 +2160,7 @@ function formatChannelLabel(message) {
|
|
|
1479
2160
|
}
|
|
1480
2161
|
function formatMessageTarget(message) {
|
|
1481
2162
|
if (message.channel_type === "thread" && message.parent_channel_name) {
|
|
1482
|
-
const shortId =
|
|
2163
|
+
const shortId = getMessageShortId(message.channel_name);
|
|
1483
2164
|
if (message.parent_channel_type === "dm") {
|
|
1484
2165
|
return `dm:@${message.parent_channel_name}:${shortId}`;
|
|
1485
2166
|
}
|
|
@@ -1490,15 +2171,47 @@ function formatMessageTarget(message) {
|
|
|
1490
2171
|
}
|
|
1491
2172
|
return `#${message.channel_name}`;
|
|
1492
2173
|
}
|
|
2174
|
+
function getMessageShortId(messageId) {
|
|
2175
|
+
return messageId.startsWith("thread-") ? messageId.slice(7) : messageId.slice(0, 8);
|
|
2176
|
+
}
|
|
2177
|
+
function formatSenderHandle(message) {
|
|
2178
|
+
return message.sender_description ? `@${message.sender_name} \u2014 ${message.sender_description}` : `@${message.sender_name}`;
|
|
2179
|
+
}
|
|
2180
|
+
function formatVisibleActorType(type) {
|
|
2181
|
+
return ` type=${type}`;
|
|
2182
|
+
}
|
|
2183
|
+
function formatTaskAssigneeType(type) {
|
|
2184
|
+
return type ?? null;
|
|
2185
|
+
}
|
|
2186
|
+
function formatThreadContextMessage(message) {
|
|
2187
|
+
const msgId = message.message_id ? getMessageShortId(message.message_id) : "-";
|
|
2188
|
+
const time = message.timestamp ? toLocalTime(message.timestamp) : "-";
|
|
2189
|
+
const senderType = formatVisibleActorType(message.sender_type);
|
|
2190
|
+
return `- [msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}`;
|
|
2191
|
+
}
|
|
1493
2192
|
function formatIncomingMessage(message) {
|
|
2193
|
+
const threadJoinPrefix = message.thread_join_context ? [
|
|
2194
|
+
`[System: You were added to a new thread via @mention. Read this context before replying.]`,
|
|
2195
|
+
`parent: ${message.thread_join_context.parent_target}`,
|
|
2196
|
+
`thread: ${message.thread_join_context.thread_target}`,
|
|
2197
|
+
`suggested next step: read_history(channel="${message.thread_join_context.suggested_read_history_target}")`,
|
|
2198
|
+
"",
|
|
2199
|
+
"Parent message:",
|
|
2200
|
+
formatThreadContextMessage(message.thread_join_context.parent_message),
|
|
2201
|
+
"",
|
|
2202
|
+
`Recent thread context${message.thread_join_context.history_truncated ? " (truncated)" : ""}:`,
|
|
2203
|
+
message.thread_join_context.recent_messages.length > 0 ? message.thread_join_context.recent_messages.map(formatThreadContextMessage).join("\n") : "- (no earlier thread replies)",
|
|
2204
|
+
""
|
|
2205
|
+
].join("\n") : "";
|
|
1494
2206
|
const target = formatMessageTarget(message);
|
|
1495
|
-
const msgId = message.message_id ? message.message_id
|
|
2207
|
+
const msgId = message.message_id ? getMessageShortId(message.message_id) : "-";
|
|
1496
2208
|
const time = message.timestamp ? toLocalTime(message.timestamp) : "-";
|
|
1497
|
-
const senderType = message.sender_type
|
|
1498
|
-
const
|
|
1499
|
-
const
|
|
1500
|
-
const
|
|
1501
|
-
return
|
|
2209
|
+
const senderType = formatVisibleActorType(message.sender_type);
|
|
2210
|
+
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]` : "";
|
|
2211
|
+
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}` : ""}]` : "";
|
|
2212
|
+
const body = `[target=${target} msg=${msgId} time=${time}${senderType}] ${formatSenderHandle(message)}: ${message.content}${attachSuffix}${taskSuffix}`;
|
|
2213
|
+
return threadJoinPrefix ? `${threadJoinPrefix}
|
|
2214
|
+
${body}` : body;
|
|
1502
2215
|
}
|
|
1503
2216
|
function buildUnreadSummary(messages, excludeChannel) {
|
|
1504
2217
|
const summary = /* @__PURE__ */ new Map();
|
|
@@ -1561,6 +2274,19 @@ function summarizeCrash(code, signal) {
|
|
|
1561
2274
|
if (typeof code === "number") return `exit code ${code}`;
|
|
1562
2275
|
return "unknown exit";
|
|
1563
2276
|
}
|
|
2277
|
+
function classifyTerminalFailure(ap) {
|
|
2278
|
+
const candidates = [
|
|
2279
|
+
ap.lastRuntimeError,
|
|
2280
|
+
...ap.recentStderr
|
|
2281
|
+
].filter((value) => !!value);
|
|
2282
|
+
for (const text of candidates) {
|
|
2283
|
+
const lower = text.toLowerCase();
|
|
2284
|
+
if (lower.includes("usage limit") || lower.includes("quota exceeded") || lower.includes("quota limit") || lower.includes("budget limit exceeded") || lower.includes("usage not included in your plan") || lower.includes("modelnotfounderror") || lower.includes("requested entity was not found") || lower.includes("model deprecated") || lower.includes("model not found")) {
|
|
2285
|
+
return text;
|
|
2286
|
+
}
|
|
2287
|
+
}
|
|
2288
|
+
return null;
|
|
2289
|
+
}
|
|
1564
2290
|
function isMissingResumeSession(ap) {
|
|
1565
2291
|
if (ap.driver.id !== "claude") return false;
|
|
1566
2292
|
if (!ap.sessionId) return false;
|
|
@@ -1571,7 +2297,7 @@ function getMessageDeliveryText(driver) {
|
|
|
1571
2297
|
}
|
|
1572
2298
|
function getBusyDeliveryNote(driver) {
|
|
1573
2299
|
if (!driver.supportsStdinNotification) return "";
|
|
1574
|
-
if (driver.
|
|
2300
|
+
if (driver.busyDeliveryMode === "direct") {
|
|
1575
2301
|
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
2302
|
}
|
|
1577
2303
|
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 +2335,9 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
1609
2335
|
this.agentsStarting.add(agentId);
|
|
1610
2336
|
try {
|
|
1611
2337
|
const driver = this.driverResolver(config.runtime || "claude");
|
|
1612
|
-
const agentDataDir =
|
|
2338
|
+
const agentDataDir = path9.join(this.dataDir, agentId);
|
|
1613
2339
|
await mkdir(agentDataDir, { recursive: true });
|
|
1614
|
-
const memoryMdPath =
|
|
2340
|
+
const memoryMdPath = path9.join(agentDataDir, "MEMORY.md");
|
|
1615
2341
|
try {
|
|
1616
2342
|
await access(memoryMdPath);
|
|
1617
2343
|
} catch {
|
|
@@ -1629,7 +2355,7 @@ ${config.description || "No role defined yet."}
|
|
|
1629
2355
|
`;
|
|
1630
2356
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
1631
2357
|
}
|
|
1632
|
-
await mkdir(
|
|
2358
|
+
await mkdir(path9.join(agentDataDir, "notes"), { recursive: true });
|
|
1633
2359
|
const isResume = !!config.sessionId;
|
|
1634
2360
|
let prompt;
|
|
1635
2361
|
if (isResume && resumePrompt) {
|
|
@@ -1766,6 +2492,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1766
2492
|
this.agents.delete(agentId);
|
|
1767
2493
|
const finalCode = ap.exitCode ?? code;
|
|
1768
2494
|
const finalSignal = ap.exitSignal ?? signal;
|
|
2495
|
+
const terminalFailureDetail = classifyTerminalFailure(ap);
|
|
1769
2496
|
if (finalCode === 0) {
|
|
1770
2497
|
const queuedWakeMessage = !ap.driver.supportsStdinNotification ? ap.inbox.shift() : void 0;
|
|
1771
2498
|
const unreadSummary2 = queuedWakeMessage ? buildUnreadSummary(ap.inbox, formatChannelLabel(queuedWakeMessage)) : void 0;
|
|
@@ -1818,13 +2545,23 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1818
2545
|
this.startAgent(agentId, restartConfig, void 0, void 0, void 0, ap.launchId || void 0).catch((err) => {
|
|
1819
2546
|
logger.error(`[Agent ${agentId}] Cold start recovery failed`, err);
|
|
1820
2547
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
1821
|
-
this.broadcastActivity(agentId, "offline", `Crashed (${summary})
|
|
2548
|
+
this.broadcastActivity(agentId, "offline", `Crashed (${summary})`, [], ap.launchId);
|
|
1822
2549
|
});
|
|
1823
2550
|
return;
|
|
1824
2551
|
}
|
|
1825
2552
|
logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
|
|
1826
2553
|
this.sendAgentStatus(agentId, "inactive", ap.launchId);
|
|
1827
|
-
|
|
2554
|
+
if (terminalFailureDetail) {
|
|
2555
|
+
this.broadcastActivity(
|
|
2556
|
+
agentId,
|
|
2557
|
+
"error",
|
|
2558
|
+
terminalFailureDetail,
|
|
2559
|
+
[{ kind: "text", text: `Error: ${terminalFailureDetail}` }],
|
|
2560
|
+
ap.launchId
|
|
2561
|
+
);
|
|
2562
|
+
} else {
|
|
2563
|
+
this.broadcastActivity(agentId, "offline", `Crashed (${summary})`, [], ap.launchId);
|
|
2564
|
+
}
|
|
1828
2565
|
}
|
|
1829
2566
|
}
|
|
1830
2567
|
});
|
|
@@ -1958,7 +2695,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1958
2695
|
}
|
|
1959
2696
|
}
|
|
1960
2697
|
async resetWorkspace(agentId) {
|
|
1961
|
-
const agentDataDir =
|
|
2698
|
+
const agentDataDir = path9.join(this.dataDir, agentId);
|
|
1962
2699
|
try {
|
|
1963
2700
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
1964
2701
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -1996,7 +2733,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
1996
2733
|
}
|
|
1997
2734
|
// Workspace file browsing
|
|
1998
2735
|
async getFileTree(agentId, dirPath) {
|
|
1999
|
-
const agentDir =
|
|
2736
|
+
const agentDir = path9.join(this.dataDir, agentId);
|
|
2000
2737
|
try {
|
|
2001
2738
|
await stat2(agentDir);
|
|
2002
2739
|
} catch {
|
|
@@ -2004,8 +2741,8 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2004
2741
|
}
|
|
2005
2742
|
let targetDir = agentDir;
|
|
2006
2743
|
if (dirPath) {
|
|
2007
|
-
const resolved =
|
|
2008
|
-
if (!resolved.startsWith(agentDir +
|
|
2744
|
+
const resolved = path9.resolve(agentDir, dirPath);
|
|
2745
|
+
if (!resolved.startsWith(agentDir + path9.sep) && resolved !== agentDir) {
|
|
2009
2746
|
return [];
|
|
2010
2747
|
}
|
|
2011
2748
|
targetDir = resolved;
|
|
@@ -2013,9 +2750,9 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2013
2750
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
2014
2751
|
}
|
|
2015
2752
|
async readFile(agentId, filePath) {
|
|
2016
|
-
const agentDir =
|
|
2017
|
-
const resolved =
|
|
2018
|
-
if (!resolved.startsWith(agentDir +
|
|
2753
|
+
const agentDir = path9.join(this.dataDir, agentId);
|
|
2754
|
+
const resolved = path9.resolve(agentDir, filePath);
|
|
2755
|
+
if (!resolved.startsWith(agentDir + path9.sep) && resolved !== agentDir) {
|
|
2019
2756
|
throw new Error("Access denied");
|
|
2020
2757
|
}
|
|
2021
2758
|
const info = await stat2(resolved);
|
|
@@ -2039,7 +2776,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2039
2776
|
".sh",
|
|
2040
2777
|
".py"
|
|
2041
2778
|
]);
|
|
2042
|
-
const ext =
|
|
2779
|
+
const ext = path9.extname(resolved).toLowerCase();
|
|
2043
2780
|
if (!TEXT_EXTENSIONS.has(ext) && ext !== "") {
|
|
2044
2781
|
return { content: null, binary: true };
|
|
2045
2782
|
}
|
|
@@ -2065,14 +2802,14 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2065
2802
|
async listSkills(agentId, runtimeHint) {
|
|
2066
2803
|
const agent = this.agents.get(agentId);
|
|
2067
2804
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
2068
|
-
const home =
|
|
2069
|
-
const workspaceDir =
|
|
2805
|
+
const home = os3.homedir();
|
|
2806
|
+
const workspaceDir = path9.join(this.dataDir, agentId);
|
|
2070
2807
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
2071
2808
|
const globalResults = await Promise.all(
|
|
2072
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
2809
|
+
paths.global.map((p) => this.scanSkillsDir(path9.join(home, p)))
|
|
2073
2810
|
);
|
|
2074
2811
|
const workspaceResults = await Promise.all(
|
|
2075
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
2812
|
+
paths.workspace.map((p) => this.scanSkillsDir(path9.join(workspaceDir, p)))
|
|
2076
2813
|
);
|
|
2077
2814
|
const dedup = (skills) => {
|
|
2078
2815
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -2101,7 +2838,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2101
2838
|
const skills = [];
|
|
2102
2839
|
for (const entry of entries) {
|
|
2103
2840
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
2104
|
-
const skillMd =
|
|
2841
|
+
const skillMd = path9.join(dir, entry.name, "SKILL.md");
|
|
2105
2842
|
try {
|
|
2106
2843
|
const content = await readFile(skillMd, "utf-8");
|
|
2107
2844
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -2112,7 +2849,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2112
2849
|
} else if (entry.name.endsWith(".md")) {
|
|
2113
2850
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
2114
2851
|
try {
|
|
2115
|
-
const content = await readFile(
|
|
2852
|
+
const content = await readFile(path9.join(dir, entry.name), "utf-8");
|
|
2116
2853
|
const skill = this.parseSkillMd(cmdName, content);
|
|
2117
2854
|
skill.sourcePath = dir;
|
|
2118
2855
|
skills.push(skill);
|
|
@@ -2148,14 +2885,21 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2148
2885
|
* Broadcast an activity change — emits a single agent:activity event that carries
|
|
2149
2886
|
* both the status (for the dot indicator) and trajectory entries (for the activity log).
|
|
2150
2887
|
*/
|
|
2151
|
-
broadcastActivity(agentId, activity, detail, extraTrajectory = []) {
|
|
2888
|
+
broadcastActivity(agentId, activity, detail, extraTrajectory = [], launchIdOverride) {
|
|
2152
2889
|
const ap = this.agents.get(agentId);
|
|
2153
2890
|
const entries = [...extraTrajectory];
|
|
2154
2891
|
const hasToolStart = entries.some((e) => e.kind === "tool_start");
|
|
2155
2892
|
if (!hasToolStart) {
|
|
2156
2893
|
entries.push({ kind: "status", activity, detail });
|
|
2157
2894
|
}
|
|
2158
|
-
this.sendToServer({
|
|
2895
|
+
this.sendToServer({
|
|
2896
|
+
type: "agent:activity",
|
|
2897
|
+
agentId,
|
|
2898
|
+
activity,
|
|
2899
|
+
detail,
|
|
2900
|
+
entries,
|
|
2901
|
+
launchId: launchIdOverride || ap?.launchId || void 0
|
|
2902
|
+
});
|
|
2159
2903
|
if (ap) {
|
|
2160
2904
|
ap.lastActivity = activity;
|
|
2161
2905
|
ap.lastActivityDetail = detail;
|
|
@@ -2167,7 +2911,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2167
2911
|
agentId,
|
|
2168
2912
|
activity: ap.lastActivity,
|
|
2169
2913
|
detail: ap.lastActivityDetail,
|
|
2170
|
-
launchId: ap.launchId || void 0
|
|
2914
|
+
launchId: launchIdOverride || ap.launchId || void 0
|
|
2171
2915
|
});
|
|
2172
2916
|
}, ACTIVITY_HEARTBEAT_MS);
|
|
2173
2917
|
}
|
|
@@ -2287,7 +3031,7 @@ Use read_history to catch up on the channels listed above, then stop. Read each
|
|
|
2287
3031
|
if (count === 0) return;
|
|
2288
3032
|
if (ap.isIdle) return;
|
|
2289
3033
|
if (!ap.sessionId) return;
|
|
2290
|
-
if (ap.driver.
|
|
3034
|
+
if (ap.driver.busyDeliveryMode === "direct" && ap.inbox.length > 0) {
|
|
2291
3035
|
const queuedMessages = ap.inbox.splice(0, ap.inbox.length);
|
|
2292
3036
|
console.log(`[Agent ${agentId}] Delivering queued message via stdin while busy`);
|
|
2293
3037
|
this.broadcastActivity(agentId, "working", "Message received");
|
|
@@ -2350,8 +3094,8 @@ Respond as appropriate. Complete all your work before stopping.`;
|
|
|
2350
3094
|
const nodes = [];
|
|
2351
3095
|
for (const entry of entries) {
|
|
2352
3096
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
2353
|
-
const fullPath =
|
|
2354
|
-
const relativePath =
|
|
3097
|
+
const fullPath = path9.join(dir, entry.name);
|
|
3098
|
+
const relativePath = path9.relative(rootDir, fullPath);
|
|
2355
3099
|
let info;
|
|
2356
3100
|
try {
|
|
2357
3101
|
info = await stat2(fullPath);
|
|
@@ -2522,13 +3266,13 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
2522
3266
|
}
|
|
2523
3267
|
}
|
|
2524
3268
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
2525
|
-
const dirname =
|
|
2526
|
-
const jsPath =
|
|
3269
|
+
const dirname = path10.dirname(fileURLToPath(moduleUrl));
|
|
3270
|
+
const jsPath = path10.resolve(dirname, "chat-bridge.js");
|
|
2527
3271
|
try {
|
|
2528
3272
|
accessSync(jsPath);
|
|
2529
3273
|
return jsPath;
|
|
2530
3274
|
} catch {
|
|
2531
|
-
return
|
|
3275
|
+
return path10.resolve(dirname, "chat-bridge.ts");
|
|
2532
3276
|
}
|
|
2533
3277
|
}
|
|
2534
3278
|
function detectRuntimes() {
|
|
@@ -2536,6 +3280,15 @@ function detectRuntimes() {
|
|
|
2536
3280
|
const versions = {};
|
|
2537
3281
|
const cmd = process.platform === "win32" ? "where" : "which";
|
|
2538
3282
|
for (const runtime of RUNTIMES) {
|
|
3283
|
+
try {
|
|
3284
|
+
const probe = getDriver(runtime.id).probe?.();
|
|
3285
|
+
if (probe?.available) {
|
|
3286
|
+
ids.push(runtime.id);
|
|
3287
|
+
if (probe.version) versions[runtime.id] = probe.version;
|
|
3288
|
+
continue;
|
|
3289
|
+
}
|
|
3290
|
+
} catch {
|
|
3291
|
+
}
|
|
2539
3292
|
try {
|
|
2540
3293
|
execSync2(`${cmd} ${runtime.binary}`, { stdio: "pipe" });
|
|
2541
3294
|
ids.push(runtime.id);
|
|
@@ -2570,6 +3323,8 @@ function summarizeIncomingMessage(msg) {
|
|
|
2570
3323
|
return `(agent=${msg.agentId}, runtime=${msg.runtime || "auto"})`;
|
|
2571
3324
|
case "machine:workspace:delete":
|
|
2572
3325
|
return `(directory=${msg.directoryName})`;
|
|
3326
|
+
case "machine:runtime_models:detect":
|
|
3327
|
+
return `(runtime=${msg.runtime}, req=${msg.requestId})`;
|
|
2573
3328
|
default:
|
|
2574
3329
|
return "";
|
|
2575
3330
|
}
|
|
@@ -2688,6 +3443,21 @@ var DaemonCore = class {
|
|
|
2688
3443
|
this.connection.send({ type: "machine:workspace:delete_result", directoryName: msg.directoryName, success });
|
|
2689
3444
|
});
|
|
2690
3445
|
break;
|
|
3446
|
+
case "machine:runtime_models:detect": {
|
|
3447
|
+
const driver = getDriver(msg.runtime);
|
|
3448
|
+
const detect = typeof driver?.detectModels === "function" ? driver.detectModels() : Promise.resolve(null);
|
|
3449
|
+
Promise.resolve(detect).then((result) => {
|
|
3450
|
+
if (result) {
|
|
3451
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, models: result.models, default: result.default });
|
|
3452
|
+
} else {
|
|
3453
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: "unsupported" });
|
|
3454
|
+
}
|
|
3455
|
+
}).catch((err) => {
|
|
3456
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
3457
|
+
this.connection.send({ type: "machine:runtime_models:result", requestId: msg.requestId, error: reason });
|
|
3458
|
+
});
|
|
3459
|
+
break;
|
|
3460
|
+
}
|
|
2691
3461
|
case "ping":
|
|
2692
3462
|
this.connection.send({ type: "pong" });
|
|
2693
3463
|
break;
|
|
@@ -2702,8 +3472,8 @@ var DaemonCore = class {
|
|
|
2702
3472
|
capabilities: ["agent:start", "agent:stop", "agent:deliver", "workspace:files"],
|
|
2703
3473
|
runtimes,
|
|
2704
3474
|
runningAgents: this.agentManager.getRunningAgentIds(),
|
|
2705
|
-
hostname: this.options.hostname ??
|
|
2706
|
-
os: this.options.osDescription ?? `${
|
|
3475
|
+
hostname: this.options.hostname ?? os4.hostname(),
|
|
3476
|
+
os: this.options.osDescription ?? `${os4.platform()} ${os4.arch()}`,
|
|
2707
3477
|
daemonVersion: this.daemonVersion
|
|
2708
3478
|
});
|
|
2709
3479
|
for (const agentId of this.agentManager.getRunningAgentIds()) {
|
|
@@ -2725,7 +3495,6 @@ var DaemonCore = class {
|
|
|
2725
3495
|
};
|
|
2726
3496
|
|
|
2727
3497
|
export {
|
|
2728
|
-
subscribeDaemonLogs,
|
|
2729
3498
|
resolveWorkspaceDirectoryPath,
|
|
2730
3499
|
scanWorkspaceDirectories,
|
|
2731
3500
|
deleteWorkspaceDirectory,
|