@slock-ai/daemon 0.55.2 → 0.55.3-play.20260602082442
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/{chunk-IDDG4Q4Q.js → chunk-2UNHLLT6.js} +841 -159
- package/dist/cli/index.js +6 -3
- package/dist/cli/package.json +1 -1
- package/dist/core.js +6 -2
- package/dist/drivers/piSdkRunner.js +96 -0
- package/dist/index.js +5 -3
- package/package.json +2 -1
|
@@ -8,11 +8,11 @@ import {
|
|
|
8
8
|
} from "./chunk-M2KQBJR3.js";
|
|
9
9
|
|
|
10
10
|
// src/core.ts
|
|
11
|
-
import
|
|
11
|
+
import path17 from "path";
|
|
12
12
|
import os7 from "os";
|
|
13
13
|
import { createRequire as createRequire2 } from "module";
|
|
14
14
|
import { accessSync } from "fs";
|
|
15
|
-
import { fileURLToPath } from "url";
|
|
15
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
16
16
|
|
|
17
17
|
// ../shared/src/slockRefs.ts
|
|
18
18
|
var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
|
|
@@ -990,6 +990,7 @@ var RUNTIME_CONFIG_VERSION = 1;
|
|
|
990
990
|
var RUNTIMES = [
|
|
991
991
|
{ id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
|
|
992
992
|
{ id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
|
|
993
|
+
{ id: "pi", displayName: "Pi", binary: "pi", supported: true },
|
|
993
994
|
{ id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
|
|
994
995
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
995
996
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
@@ -999,9 +1000,14 @@ var RUNTIMES = [
|
|
|
999
1000
|
];
|
|
1000
1001
|
var RUNTIME_MODELS = {
|
|
1001
1002
|
claude: [
|
|
1002
|
-
{ id: "opus", label: "Opus" },
|
|
1003
|
-
{ id: "
|
|
1004
|
-
{ id: "
|
|
1003
|
+
{ id: "opus", label: "Claude Opus" },
|
|
1004
|
+
{ id: "claude-opus-4-8", label: "Claude Opus 4.8" },
|
|
1005
|
+
{ id: "claude-opus-4-7", label: "Claude Opus 4.7" },
|
|
1006
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
|
1007
|
+
{ id: "sonnet", label: "Claude Sonnet" },
|
|
1008
|
+
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
|
1009
|
+
{ id: "haiku", label: "Claude Haiku" },
|
|
1010
|
+
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5" }
|
|
1005
1011
|
],
|
|
1006
1012
|
codex: [
|
|
1007
1013
|
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
@@ -1042,6 +1048,13 @@ var RUNTIME_MODELS = {
|
|
|
1042
1048
|
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
1043
1049
|
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
1044
1050
|
],
|
|
1051
|
+
pi: [
|
|
1052
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro" },
|
|
1053
|
+
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash" },
|
|
1054
|
+
{ id: "kimi-coding/kimi-for-coding", label: "Kimi for Coding" },
|
|
1055
|
+
{ id: "zai/glm-5.1", label: "GLM-5.1" },
|
|
1056
|
+
{ id: "zai/glm-4.7", label: "GLM-4.7" }
|
|
1057
|
+
],
|
|
1045
1058
|
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
1046
1059
|
// built-in option is to defer to whatever default model the CLI already uses.
|
|
1047
1060
|
kimi: [
|
|
@@ -1086,22 +1099,28 @@ function isPresetRuntimeModel(runtime, model) {
|
|
|
1086
1099
|
function modelConfigFromLegacy(runtime, model) {
|
|
1087
1100
|
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
1088
1101
|
}
|
|
1089
|
-
function parseProviderConfig(runtime, value) {
|
|
1102
|
+
function parseProviderConfig(runtime, value, legacyApiUrl, legacyApiKey) {
|
|
1090
1103
|
if (runtime !== "claude") return void 0;
|
|
1091
1104
|
if (!isPlainRecord(value)) return { kind: "default" };
|
|
1092
1105
|
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
1093
1106
|
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
1094
1107
|
}
|
|
1108
|
+
if (value.kind === "custom" && legacyApiUrl?.trim() && legacyApiKey?.trim()) {
|
|
1109
|
+
return { kind: "custom", apiUrl: legacyApiUrl.trim(), apiKey: legacyApiKey.trim() };
|
|
1110
|
+
}
|
|
1095
1111
|
return { kind: "default" };
|
|
1096
1112
|
}
|
|
1097
|
-
function parseModelConfig(runtime, value, fallback) {
|
|
1113
|
+
function parseModelConfig(runtime, value, fallback, provider, legacyCustomModel) {
|
|
1098
1114
|
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
1099
1115
|
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
1100
1116
|
return { kind: "custom", name: value.name.trim() };
|
|
1101
1117
|
}
|
|
1102
|
-
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
|
|
1118
|
+
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim() && isPresetRuntimeModel(runtime, value.id.trim())) {
|
|
1103
1119
|
return { kind: "preset", id: value.id.trim() };
|
|
1104
1120
|
}
|
|
1121
|
+
if (provider?.kind === "custom" && runtime === "claude" && legacyCustomModel?.trim()) {
|
|
1122
|
+
return { kind: "custom", name: legacyCustomModel.trim() };
|
|
1123
|
+
}
|
|
1105
1124
|
return modelConfigFromLegacy(runtime, fallback);
|
|
1106
1125
|
}
|
|
1107
1126
|
function parseModeConfig(value) {
|
|
@@ -1117,12 +1136,13 @@ function hydrateRuntimeConfig(input) {
|
|
|
1117
1136
|
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
1118
1137
|
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
1119
1138
|
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
1120
|
-
const
|
|
1139
|
+
const legacyClaudeCustomModel = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_CUSTOM_MODEL_OPTION : void 0;
|
|
1140
|
+
const provider = stored ? parseProviderConfig(runtime, stored.provider, legacyClaudeApiUrl, legacyClaudeApiKey) : runtime === "claude" && legacyClaudeApiUrl && legacyClaudeApiKey ? { kind: "custom", apiUrl: legacyClaudeApiUrl, apiKey: legacyClaudeApiKey } : runtime === "claude" ? { kind: "default" } : void 0;
|
|
1121
1141
|
return {
|
|
1122
1142
|
version: RUNTIME_CONFIG_VERSION,
|
|
1123
1143
|
runtime,
|
|
1124
1144
|
...provider ? { provider } : {},
|
|
1125
|
-
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1145
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel, provider, legacyClaudeCustomModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1126
1146
|
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
1127
1147
|
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
1128
1148
|
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
@@ -1190,10 +1210,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1190
1210
|
};
|
|
1191
1211
|
|
|
1192
1212
|
// src/agentProcessManager.ts
|
|
1193
|
-
import { mkdirSync as
|
|
1213
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync8 } from "fs";
|
|
1194
1214
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1195
1215
|
import { createHash as createHash3 } from "crypto";
|
|
1196
|
-
import
|
|
1216
|
+
import path13 from "path";
|
|
1197
1217
|
import os5 from "os";
|
|
1198
1218
|
|
|
1199
1219
|
// src/drivers/claude.ts
|
|
@@ -1374,19 +1394,19 @@ If Slock says a message was not sent and was saved as a draft, choose one path:
|
|
|
1374
1394
|
|
|
1375
1395
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1376
1396
|
|
|
1377
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1397
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1378
1398
|
- 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.
|
|
1379
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1399
|
+
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, reply with \`slock message send --target "#general:00000000" <<'${messageHeredocDelimiter}'\` followed by the message body and \`${messageHeredocDelimiter}\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
|
|
1380
1400
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1381
|
-
- You can read thread history: \`slock message read --channel "#general:
|
|
1382
|
-
- You can stop receiving ordinary delivery for a thread with \`slock thread unfollow --target "#general:
|
|
1401
|
+
- You can read thread history: \`slock message read --channel "#general:00000000"\`
|
|
1402
|
+
- You can stop receiving ordinary delivery for a thread with \`slock thread unfollow --target "#general:00000000"\`. Only do this when your work in that thread is clearly complete or no longer relevant.
|
|
1383
1403
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.` : `### Threads
|
|
1384
1404
|
|
|
1385
1405
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1386
1406
|
|
|
1387
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1407
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1388
1408
|
- 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.
|
|
1389
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1409
|
+
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, call \`${t("send_message")}(target="#general:00000000", content="...")\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
|
|
1390
1410
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1391
1411
|
- You can read thread history via ${readCmd} with the same thread target.
|
|
1392
1412
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
|
|
@@ -1552,13 +1572,18 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
1552
1572
|
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
1553
1573
|
|
|
1554
1574
|
\`\`\`
|
|
1555
|
-
[target=#general msg=
|
|
1556
|
-
[target=#general msg=
|
|
1557
|
-
[target=dm:@richard msg=
|
|
1558
|
-
[target=#general:
|
|
1559
|
-
[target=dm:@richard:
|
|
1575
|
+
[target=#general msg=00000000 time=2026-03-15T01:00:00 type=human] @richard: hello everyone
|
|
1576
|
+
[target=#general msg=11111111 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
|
|
1577
|
+
[target=dm:@richard msg=22222222 time=2026-03-15T01:00:02 type=human] @richard: hey, can you help?
|
|
1578
|
+
[target=#general:00000000 msg=33333333 time=2026-03-15T01:00:03 type=human] @richard: thread reply
|
|
1579
|
+
[target=dm:@richard:22222222 msg=44444444 time=2026-03-15T01:00:04 type=human] @richard: DM thread reply
|
|
1560
1580
|
\`\`\`
|
|
1561
1581
|
|
|
1582
|
+
Prompt examples use obvious placeholder IDs such as \`00000000\`, \`11111111\`,
|
|
1583
|
+
and \`22222222\`. They show the shape of a real message ID but are not actual
|
|
1584
|
+
messages. Do not cite them as evidence; use only IDs from messages you actually
|
|
1585
|
+
received or read.
|
|
1586
|
+
|
|
1562
1587
|
Header fields:
|
|
1563
1588
|
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
1564
1589
|
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
@@ -1712,12 +1737,12 @@ How to handle these:
|
|
|
1712
1737
|
- Call ${checkCmd} at the next safe breakpoint to materialize the pending messages before taking side-effect actions that depend on current context.
|
|
1713
1738
|
- If the new message is higher priority, pivot after reading it. If not, continue your current work.`;
|
|
1714
1739
|
} else {
|
|
1715
|
-
const notifyExample = isCli ? `\`[
|
|
1740
|
+
const notifyExample = isCli ? `\`[Slock inbox notice: You have N pending inbox message(s). Call slock message check to read them when you're ready.]\`` : `\`[Slock inbox notice: You have N pending inbox message(s). Call ${t("check_messages")} to read them when you're ready.]\``;
|
|
1716
1741
|
prompt += `
|
|
1717
1742
|
|
|
1718
1743
|
## Message Notifications
|
|
1719
1744
|
|
|
1720
|
-
While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens,
|
|
1745
|
+
While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens, the daemon may write a Slock inbox notice like:
|
|
1721
1746
|
|
|
1722
1747
|
${notifyExample}
|
|
1723
1748
|
|
|
@@ -1778,6 +1803,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1778
1803
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1779
1804
|
}
|
|
1780
1805
|
|
|
1806
|
+
// src/authEnv.ts
|
|
1807
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1808
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1809
|
+
function scrubDaemonAuthEnv(env) {
|
|
1810
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1811
|
+
return env;
|
|
1812
|
+
}
|
|
1813
|
+
function scrubDaemonChildEnv(env) {
|
|
1814
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1815
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1816
|
+
return env;
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1781
1819
|
// src/agentCredentialProxy.ts
|
|
1782
1820
|
import { randomBytes } from "crypto";
|
|
1783
1821
|
import http from "http";
|
|
@@ -2172,7 +2210,7 @@ async function handleProxyRequest(req, res) {
|
|
|
2172
2210
|
const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
|
|
2173
2211
|
new Uint8Array(bodyBuffer).set(rawBodyBuffer);
|
|
2174
2212
|
body = bodyBuffer;
|
|
2175
|
-
headers.
|
|
2213
|
+
headers.delete("content-length");
|
|
2176
2214
|
}
|
|
2177
2215
|
let sendTarget;
|
|
2178
2216
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
@@ -2196,7 +2234,7 @@ async function handleProxyRequest(req, res) {
|
|
|
2196
2234
|
body = prepared.bodyText;
|
|
2197
2235
|
if (sideEffectAction === "send") sendTarget = prepared.target;
|
|
2198
2236
|
headers.set("content-type", "application/json");
|
|
2199
|
-
headers.
|
|
2237
|
+
headers.delete("content-length");
|
|
2200
2238
|
}
|
|
2201
2239
|
const upstream = await daemonFetch(target, {
|
|
2202
2240
|
method,
|
|
@@ -2385,6 +2423,20 @@ function maxMessageSeq(messages) {
|
|
|
2385
2423
|
}
|
|
2386
2424
|
return maxSeq > 0 ? maxSeq : void 0;
|
|
2387
2425
|
}
|
|
2426
|
+
function messageSenderId(message) {
|
|
2427
|
+
if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
|
|
2428
|
+
if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
|
|
2429
|
+
return void 0;
|
|
2430
|
+
}
|
|
2431
|
+
function isSelfAuthoredMessage(registration, message) {
|
|
2432
|
+
return messageSenderId(message) === registration.agentId;
|
|
2433
|
+
}
|
|
2434
|
+
function isMessageModelSeen(coordinator, target, message) {
|
|
2435
|
+
const seq = Math.floor(messageSeq(message));
|
|
2436
|
+
const boundary = coordinator.getBoundary(target);
|
|
2437
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
|
|
2438
|
+
return coordinator.isMessageModelSeen?.({ target, message }) === true;
|
|
2439
|
+
}
|
|
2388
2440
|
function resolveFreshnessBoundary(messages) {
|
|
2389
2441
|
const seenUpToSeq = maxMessageSeq(messages);
|
|
2390
2442
|
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
@@ -2525,7 +2577,7 @@ function parseTargetFields(target) {
|
|
|
2525
2577
|
}
|
|
2526
2578
|
function normalizeVisibleMessage(message, target) {
|
|
2527
2579
|
const targetFields = target ? parseTargetFields(target) : {};
|
|
2528
|
-
|
|
2580
|
+
const normalized = {
|
|
2529
2581
|
...targetFields,
|
|
2530
2582
|
...message,
|
|
2531
2583
|
message_id: message.message_id ?? message.id,
|
|
@@ -2534,6 +2586,9 @@ function normalizeVisibleMessage(message, target) {
|
|
|
2534
2586
|
sender_name: message.sender_name ?? message.senderName,
|
|
2535
2587
|
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
2536
2588
|
};
|
|
2589
|
+
const senderId = messageSenderId(message);
|
|
2590
|
+
if (senderId) normalized.sender_id = senderId;
|
|
2591
|
+
return normalized;
|
|
2537
2592
|
}
|
|
2538
2593
|
function normalizeVisibleMessages(messages, target) {
|
|
2539
2594
|
return messages.map((message) => normalizeVisibleMessage(message, target));
|
|
@@ -2634,6 +2689,9 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2634
2689
|
}
|
|
2635
2690
|
const recent = await loadRecentTargetMessages(registration, headers, target);
|
|
2636
2691
|
if (recent.length > 0) {
|
|
2692
|
+
const unconsumedCounterparty = recent.filter(
|
|
2693
|
+
(message) => !isSelfAuthoredMessage(registration, message) && !isMessageModelSeen(coordinator, target, message)
|
|
2694
|
+
);
|
|
2637
2695
|
const boundary2 = resolveFreshnessBoundary(recent);
|
|
2638
2696
|
if (!boundary2.ok) {
|
|
2639
2697
|
recordFreshnessDecision(coordinator, {
|
|
@@ -2647,6 +2705,39 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2647
2705
|
});
|
|
2648
2706
|
return { bodyText: JSON.stringify(body), target };
|
|
2649
2707
|
}
|
|
2708
|
+
if (unconsumedCounterparty.length === 0) {
|
|
2709
|
+
const { seenUpToSeq: seenUpToSeq2 } = boundary2;
|
|
2710
|
+
if (action === "send") body.seenUpToSeq = seenUpToSeq2;
|
|
2711
|
+
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq2, source: "side_effect_preflight_context" });
|
|
2712
|
+
recordFreshnessDecision(coordinator, {
|
|
2713
|
+
action,
|
|
2714
|
+
decision: "forward",
|
|
2715
|
+
target,
|
|
2716
|
+
inboxTrustState: "untrusted",
|
|
2717
|
+
reason: "target_first_touch_recent_context_already_seen",
|
|
2718
|
+
pendingCount: 0,
|
|
2719
|
+
pendingMaxSeq: seenUpToSeq2,
|
|
2720
|
+
modelSeenSeq: seenUpToSeq2,
|
|
2721
|
+
heldMessageCount: 0,
|
|
2722
|
+
omittedMessageCount: 0
|
|
2723
|
+
});
|
|
2724
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2725
|
+
}
|
|
2726
|
+
const heldBoundary = resolveFreshnessBoundary(unconsumedCounterparty);
|
|
2727
|
+
if (!heldBoundary.ok) {
|
|
2728
|
+
recordFreshnessDecision(coordinator, {
|
|
2729
|
+
action,
|
|
2730
|
+
decision: "forward",
|
|
2731
|
+
target,
|
|
2732
|
+
inboxTrustState: "untrusted",
|
|
2733
|
+
reason: "target_first_touch_counterparty_context_without_seq_boundary",
|
|
2734
|
+
pendingCount: 0,
|
|
2735
|
+
modelSeenSeq: 0
|
|
2736
|
+
});
|
|
2737
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2738
|
+
}
|
|
2739
|
+
const heldMessages = latestVisibleMessages(unconsumedCounterparty, LOCAL_HELD_CONTEXT_LIMIT);
|
|
2740
|
+
const omittedMessageCount = Math.max(0, unconsumedCounterparty.length - heldMessages.length);
|
|
2650
2741
|
const { seenUpToSeq } = boundary2;
|
|
2651
2742
|
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "side_effect_preflight_context" });
|
|
2652
2743
|
const decision = {
|
|
@@ -2656,10 +2747,10 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2656
2747
|
inboxTrustState: "untrusted",
|
|
2657
2748
|
reason: "target_first_touch_recent_context",
|
|
2658
2749
|
pendingCount: 0,
|
|
2659
|
-
pendingMaxSeq: seenUpToSeq,
|
|
2750
|
+
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
2660
2751
|
modelSeenSeq: 0,
|
|
2661
|
-
heldMessageCount:
|
|
2662
|
-
omittedMessageCount
|
|
2752
|
+
heldMessageCount: heldMessages.length,
|
|
2753
|
+
omittedMessageCount
|
|
2663
2754
|
};
|
|
2664
2755
|
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
2665
2756
|
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
@@ -2669,9 +2760,9 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2669
2760
|
localResponse: projectApmHeldFreshnessEnvelope({
|
|
2670
2761
|
producerFactId,
|
|
2671
2762
|
action,
|
|
2672
|
-
heldMessages
|
|
2673
|
-
newMessageCount:
|
|
2674
|
-
omittedMessageCount
|
|
2763
|
+
heldMessages,
|
|
2764
|
+
newMessageCount: unconsumedCounterparty.length,
|
|
2765
|
+
omittedMessageCount,
|
|
2675
2766
|
seenUpToSeq
|
|
2676
2767
|
}).body
|
|
2677
2768
|
};
|
|
@@ -2777,7 +2868,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
2777
2868
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
2778
2869
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
2779
2870
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
2780
|
-
"
|
|
2871
|
+
"SLOCK_AGENT_TOKEN",
|
|
2872
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2873
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
2781
2874
|
];
|
|
2782
2875
|
var cachedOpencliBinPath;
|
|
2783
2876
|
function resolveOpencliBinPath() {
|
|
@@ -2992,7 +3085,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2992
3085
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2993
3086
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
2994
3087
|
};
|
|
2995
|
-
|
|
3088
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
2996
3089
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
2997
3090
|
delete spawnEnv[key];
|
|
2998
3091
|
}
|
|
@@ -3026,7 +3119,126 @@ function collectResultErrorDetail(message, fallback) {
|
|
|
3026
3119
|
return parts.join(" | ") || fallback;
|
|
3027
3120
|
}
|
|
3028
3121
|
function isProviderApiFailureText(value, hasToolUse) {
|
|
3029
|
-
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b5\d{2}\b/.test(value));
|
|
3122
|
+
return !hasToolUse && /^\s*API Error:/i.test(value) && (/\b(?:ECONNRESET|EPIPE|ETIMEDOUT|ECONNREFUSED|ENOTFOUND|EAI_AGAIN)\b/i.test(value) || /\bUnable to connect to API\b/i.test(value) || /\b(?:timed out|timeout)\b/i.test(value) || /\b4\d{2}\b/.test(value) || /\b5\d{2}\b/.test(value));
|
|
3123
|
+
}
|
|
3124
|
+
function finiteNumber(value) {
|
|
3125
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3126
|
+
}
|
|
3127
|
+
function finiteString(value) {
|
|
3128
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3129
|
+
}
|
|
3130
|
+
function withDefined(attrs) {
|
|
3131
|
+
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3132
|
+
}
|
|
3133
|
+
function collectNumericFields(value, fields) {
|
|
3134
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3135
|
+
const attrs = {};
|
|
3136
|
+
for (const [sourceKey, attrKey] of Object.entries(fields)) {
|
|
3137
|
+
const numberValue = finiteNumber(value[sourceKey]);
|
|
3138
|
+
if (numberValue !== void 0) attrs[attrKey] = numberValue;
|
|
3139
|
+
}
|
|
3140
|
+
return attrs;
|
|
3141
|
+
}
|
|
3142
|
+
function collectModelUsageAttrs(value) {
|
|
3143
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3144
|
+
const sanitized = {};
|
|
3145
|
+
const aggregate = {};
|
|
3146
|
+
const knownNumericFields = [
|
|
3147
|
+
"inputTokens",
|
|
3148
|
+
"outputTokens",
|
|
3149
|
+
"cacheReadInputTokens",
|
|
3150
|
+
"cacheCreationInputTokens",
|
|
3151
|
+
"webSearchRequests",
|
|
3152
|
+
"costUSD",
|
|
3153
|
+
"contextWindow",
|
|
3154
|
+
"maxOutputTokens"
|
|
3155
|
+
];
|
|
3156
|
+
for (const [modelName, rawUsage] of Object.entries(value)) {
|
|
3157
|
+
if (!rawUsage || typeof rawUsage !== "object" || Array.isArray(rawUsage)) continue;
|
|
3158
|
+
const modelUsage = {};
|
|
3159
|
+
for (const field of knownNumericFields) {
|
|
3160
|
+
const numberValue = finiteNumber(rawUsage[field]);
|
|
3161
|
+
if (numberValue === void 0) continue;
|
|
3162
|
+
modelUsage[field] = numberValue;
|
|
3163
|
+
const aggregateKey = field === "costUSD" ? "costUsd" : field;
|
|
3164
|
+
if (field === "contextWindow" || field === "maxOutputTokens") {
|
|
3165
|
+
aggregate[aggregateKey] = Math.max(aggregate[aggregateKey] ?? 0, numberValue);
|
|
3166
|
+
} else {
|
|
3167
|
+
aggregate[aggregateKey] = (aggregate[aggregateKey] ?? 0) + numberValue;
|
|
3168
|
+
}
|
|
3169
|
+
}
|
|
3170
|
+
if (Object.keys(modelUsage).length > 0) sanitized[modelName] = modelUsage;
|
|
3171
|
+
}
|
|
3172
|
+
const modelNames = Object.keys(sanitized).sort();
|
|
3173
|
+
if (modelNames.length === 0) return {};
|
|
3174
|
+
const orderedSanitized = Object.fromEntries(modelNames.map((modelName) => [modelName, sanitized[modelName]]));
|
|
3175
|
+
return withDefined({
|
|
3176
|
+
modelUsageModelCount: modelNames.length,
|
|
3177
|
+
modelUsageModels: modelNames.join(","),
|
|
3178
|
+
modelUsageJson: JSON.stringify(orderedSanitized),
|
|
3179
|
+
modelUsageInputTokens: aggregate.inputTokens,
|
|
3180
|
+
modelUsageOutputTokens: aggregate.outputTokens,
|
|
3181
|
+
modelUsageCachedInputTokens: aggregate.cacheReadInputTokens,
|
|
3182
|
+
modelUsageCacheCreationInputTokens: aggregate.cacheCreationInputTokens,
|
|
3183
|
+
modelUsageWebSearchRequests: aggregate.webSearchRequests,
|
|
3184
|
+
modelUsageCostUsd: aggregate.costUsd,
|
|
3185
|
+
modelUsageMaxContextWindow: aggregate.contextWindow,
|
|
3186
|
+
modelUsageMaxOutputTokens: aggregate.maxOutputTokens
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
function parseClaudeResultUsageTelemetry(event) {
|
|
3190
|
+
const usage = event.usage && typeof event.usage === "object" ? event.usage : void 0;
|
|
3191
|
+
const totalCostUsd = finiteNumber(event.total_cost_usd);
|
|
3192
|
+
const modelUsageAttrs = collectModelUsageAttrs(event.modelUsage);
|
|
3193
|
+
const hasTelemetrySource = usage !== void 0 || totalCostUsd !== void 0 || Object.keys(modelUsageAttrs).length > 0;
|
|
3194
|
+
if (!hasTelemetrySource) return null;
|
|
3195
|
+
const inputTokens = finiteNumber(usage?.input_tokens);
|
|
3196
|
+
const outputTokens = finiteNumber(usage?.output_tokens);
|
|
3197
|
+
const cachedInputTokens = finiteNumber(usage?.cache_read_input_tokens);
|
|
3198
|
+
const cacheCreationInputTokens = finiteNumber(usage?.cache_creation_input_tokens);
|
|
3199
|
+
const attrs = {
|
|
3200
|
+
...withDefined({
|
|
3201
|
+
totalCostUsd,
|
|
3202
|
+
durationMs: finiteNumber(event.duration_ms),
|
|
3203
|
+
durationApiMs: finiteNumber(event.duration_api_ms),
|
|
3204
|
+
numTurns: finiteNumber(event.num_turns),
|
|
3205
|
+
resultSubtype: finiteString(event.subtype),
|
|
3206
|
+
stopReason: finiteString(event.stop_reason),
|
|
3207
|
+
resultIsError: typeof event.is_error === "boolean" ? event.is_error : void 0,
|
|
3208
|
+
fastModeState: finiteString(event.fast_mode_state),
|
|
3209
|
+
permissionDenialsCount: Array.isArray(event.permission_denials) ? event.permission_denials.length : void 0,
|
|
3210
|
+
serviceTier: finiteString(usage?.service_tier),
|
|
3211
|
+
inferenceGeo: finiteString(usage?.inference_geo),
|
|
3212
|
+
usageSpeed: finiteString(usage?.speed),
|
|
3213
|
+
usageIterationsCount: Array.isArray(usage?.iterations) ? usage.iterations.length : void 0
|
|
3214
|
+
}),
|
|
3215
|
+
...collectNumericFields(usage?.server_tool_use, {
|
|
3216
|
+
web_search_requests: "serverToolUseWebSearchRequests",
|
|
3217
|
+
web_fetch_requests: "serverToolUseWebFetchRequests"
|
|
3218
|
+
}),
|
|
3219
|
+
...collectNumericFields(usage?.cache_creation, {
|
|
3220
|
+
ephemeral_1h_input_tokens: "cacheCreationEphemeral1hInputTokens",
|
|
3221
|
+
ephemeral_5m_input_tokens: "cacheCreationEphemeral5mInputTokens"
|
|
3222
|
+
}),
|
|
3223
|
+
...modelUsageAttrs
|
|
3224
|
+
};
|
|
3225
|
+
if (inputTokens !== void 0) attrs.inputTokens = inputTokens;
|
|
3226
|
+
if (outputTokens !== void 0) attrs.outputTokens = outputTokens;
|
|
3227
|
+
if (cachedInputTokens !== void 0) attrs.cachedInputTokens = cachedInputTokens;
|
|
3228
|
+
if (cacheCreationInputTokens !== void 0) attrs.cacheCreationInputTokens = cacheCreationInputTokens;
|
|
3229
|
+
const tokenComponents = [inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens];
|
|
3230
|
+
const totalTokens = tokenComponents.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
3231
|
+
if (tokenComponents.some((value) => value !== void 0)) attrs.totalTokens = totalTokens;
|
|
3232
|
+
if (Object.keys(attrs).length === 0) return null;
|
|
3233
|
+
return {
|
|
3234
|
+
kind: "telemetry",
|
|
3235
|
+
name: "token_usage",
|
|
3236
|
+
source: "claude_result_usage",
|
|
3237
|
+
usageKind: "per_turn",
|
|
3238
|
+
...typeof event.session_id === "string" && event.session_id ? { sessionId: event.session_id } : {},
|
|
3239
|
+
...typeof event.uuid === "string" && event.uuid ? { runtimeResultId: event.uuid } : {},
|
|
3240
|
+
attrs
|
|
3241
|
+
};
|
|
3030
3242
|
}
|
|
3031
3243
|
var ClaudeEventNormalizer = class {
|
|
3032
3244
|
normalizeLine(line) {
|
|
@@ -3102,6 +3314,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3102
3314
|
case "result": {
|
|
3103
3315
|
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
3104
3316
|
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
3317
|
+
const usageTelemetry = parseClaudeResultUsageTelemetry(event);
|
|
3105
3318
|
switch (subtype) {
|
|
3106
3319
|
case "success":
|
|
3107
3320
|
if (event.is_error && stopReason !== "max_tokens") {
|
|
@@ -3123,6 +3336,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3123
3336
|
pushResultError(event, "Structured output retries exceeded");
|
|
3124
3337
|
break;
|
|
3125
3338
|
}
|
|
3339
|
+
if (usageTelemetry) events.push(usageTelemetry);
|
|
3126
3340
|
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
3127
3341
|
break;
|
|
3128
3342
|
}
|
|
@@ -3291,7 +3505,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
3291
3505
|
}
|
|
3292
3506
|
function resolveCommandOnPath(command, deps = {}) {
|
|
3293
3507
|
const platform = deps.platform ?? process.platform;
|
|
3294
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3508
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3295
3509
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3296
3510
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
3297
3511
|
if (platform === "win32") {
|
|
@@ -3317,7 +3531,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
3317
3531
|
return null;
|
|
3318
3532
|
}
|
|
3319
3533
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
3320
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3534
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3321
3535
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3322
3536
|
try {
|
|
3323
3537
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -3388,7 +3602,7 @@ function buildClaudeArgs(config, opts) {
|
|
|
3388
3602
|
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3389
3603
|
}
|
|
3390
3604
|
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3391
|
-
args.push("--
|
|
3605
|
+
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
3392
3606
|
}
|
|
3393
3607
|
if (config.sessionId) {
|
|
3394
3608
|
args.push("--resume", config.sessionId);
|
|
@@ -3546,53 +3760,64 @@ var RuntimeTurnState = class {
|
|
|
3546
3760
|
};
|
|
3547
3761
|
|
|
3548
3762
|
// src/drivers/codexTelemetrySidecar.ts
|
|
3549
|
-
function
|
|
3763
|
+
function finiteNumber2(value) {
|
|
3550
3764
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3551
3765
|
}
|
|
3552
|
-
function
|
|
3766
|
+
function finiteString2(value) {
|
|
3553
3767
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3554
3768
|
}
|
|
3555
3769
|
function ratio(numerator, denominator) {
|
|
3556
3770
|
if (numerator === void 0 || denominator === void 0 || denominator <= 0) return void 0;
|
|
3557
3771
|
return Number((numerator / denominator).toFixed(6));
|
|
3558
3772
|
}
|
|
3559
|
-
function
|
|
3773
|
+
function withDefined2(attrs) {
|
|
3560
3774
|
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3561
3775
|
}
|
|
3562
3776
|
function parseTokenUsageTelemetry(message) {
|
|
3563
3777
|
const usage = message.params?.tokenUsage;
|
|
3564
3778
|
const total = usage?.total;
|
|
3565
3779
|
if (!total || typeof total !== "object") return null;
|
|
3566
|
-
const inputTokens =
|
|
3567
|
-
const cachedInputTokens =
|
|
3568
|
-
const totalTokens =
|
|
3569
|
-
const modelContextWindow =
|
|
3570
|
-
const attrs =
|
|
3780
|
+
const inputTokens = finiteNumber2(total.inputTokens);
|
|
3781
|
+
const cachedInputTokens = finiteNumber2(total.cachedInputTokens);
|
|
3782
|
+
const totalTokens = finiteNumber2(total.totalTokens);
|
|
3783
|
+
const modelContextWindow = finiteNumber2(usage.modelContextWindow);
|
|
3784
|
+
const attrs = withDefined2({
|
|
3571
3785
|
totalTokens,
|
|
3572
3786
|
inputTokens,
|
|
3573
3787
|
cachedInputTokens,
|
|
3574
|
-
outputTokens:
|
|
3575
|
-
reasoningOutputTokens:
|
|
3788
|
+
outputTokens: finiteNumber2(total.outputTokens),
|
|
3789
|
+
reasoningOutputTokens: finiteNumber2(total.reasoningOutputTokens),
|
|
3576
3790
|
modelContextWindow,
|
|
3577
3791
|
cachedInputRatio: ratio(cachedInputTokens, inputTokens),
|
|
3578
3792
|
contextUtilization: ratio(totalTokens, modelContextWindow)
|
|
3579
3793
|
});
|
|
3580
3794
|
if (Object.keys(attrs).length === 0) return null;
|
|
3581
|
-
return {
|
|
3795
|
+
return {
|
|
3796
|
+
kind: "telemetry",
|
|
3797
|
+
name: "token_usage",
|
|
3798
|
+
source: "codex_thread_token_usage_updated",
|
|
3799
|
+
usageKind: "cumulative_session",
|
|
3800
|
+
attrs
|
|
3801
|
+
};
|
|
3582
3802
|
}
|
|
3583
3803
|
function parseRateLimitTelemetry(message) {
|
|
3584
3804
|
const rateLimits = message.params?.rateLimits;
|
|
3585
3805
|
const primary = rateLimits?.primary;
|
|
3586
3806
|
if (!rateLimits || typeof rateLimits !== "object" || !primary || typeof primary !== "object") return null;
|
|
3587
|
-
const attrs =
|
|
3588
|
-
limitId:
|
|
3589
|
-
planType:
|
|
3590
|
-
usedPercent:
|
|
3591
|
-
windowDurationMins:
|
|
3592
|
-
resetsAt:
|
|
3807
|
+
const attrs = withDefined2({
|
|
3808
|
+
limitId: finiteString2(rateLimits.limitId),
|
|
3809
|
+
planType: finiteString2(rateLimits.planType),
|
|
3810
|
+
usedPercent: finiteNumber2(primary.usedPercent),
|
|
3811
|
+
windowDurationMins: finiteNumber2(primary.windowDurationMins),
|
|
3812
|
+
resetsAt: finiteNumber2(primary.resetsAt)
|
|
3593
3813
|
});
|
|
3594
3814
|
if (Object.keys(attrs).length === 0) return null;
|
|
3595
|
-
return {
|
|
3815
|
+
return {
|
|
3816
|
+
kind: "telemetry",
|
|
3817
|
+
name: "rate_limits",
|
|
3818
|
+
source: "codex_account_rate_limits_updated",
|
|
3819
|
+
attrs
|
|
3820
|
+
};
|
|
3596
3821
|
}
|
|
3597
3822
|
function parseCodexTelemetryEvent(message) {
|
|
3598
3823
|
switch (message.method) {
|
|
@@ -3699,7 +3924,11 @@ var CodexEventNormalizer = class {
|
|
|
3699
3924
|
}
|
|
3700
3925
|
const telemetry = parseCodexTelemetryEvent(message);
|
|
3701
3926
|
if (telemetry) {
|
|
3702
|
-
events.push(
|
|
3927
|
+
events.push({
|
|
3928
|
+
...telemetry,
|
|
3929
|
+
...this.currentThreadId ? { sessionId: this.currentThreadId } : {},
|
|
3930
|
+
...this.turnState.activeTurnId ? { turnId: this.turnState.activeTurnId } : {}
|
|
3931
|
+
});
|
|
3703
3932
|
return { events };
|
|
3704
3933
|
}
|
|
3705
3934
|
const rawProgress = rawResponseItemProgressEvent(message);
|
|
@@ -4052,6 +4281,7 @@ var CodexDriver = class {
|
|
|
4052
4281
|
cwd: ctx.workingDirectory,
|
|
4053
4282
|
approvalPolicy: "never",
|
|
4054
4283
|
sandbox: "danger-full-access",
|
|
4284
|
+
sandbox_mode: "danger-full-access",
|
|
4055
4285
|
developerInstructions: ctx.standingPrompt,
|
|
4056
4286
|
// Raw response items are used only as payload-free liveness signals in
|
|
4057
4287
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
@@ -4748,11 +4978,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
4748
4978
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
4749
4979
|
}
|
|
4750
4980
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
4751
|
-
return withWindowsUserEnvironment({
|
|
4981
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
4752
4982
|
...deps.env ?? process.env,
|
|
4753
4983
|
FORCE_COLOR: "0",
|
|
4754
4984
|
NO_COLOR: "1"
|
|
4755
|
-
}, deps);
|
|
4985
|
+
}, deps));
|
|
4756
4986
|
}
|
|
4757
4987
|
function runCursorModelsCommand() {
|
|
4758
4988
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -4808,7 +5038,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4808
5038
|
}
|
|
4809
5039
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4810
5040
|
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4811
|
-
const env = deps.env ?? process.env;
|
|
5041
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
4812
5042
|
const winPath = path8.win32;
|
|
4813
5043
|
let geminiEntry = null;
|
|
4814
5044
|
try {
|
|
@@ -4980,13 +5210,16 @@ var GeminiDriver = class {
|
|
|
4980
5210
|
// src/drivers/kimi.ts
|
|
4981
5211
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4982
5212
|
import { spawn as spawn7 } from "child_process";
|
|
4983
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
5213
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4984
5214
|
import os3 from "os";
|
|
4985
5215
|
import path9 from "path";
|
|
4986
5216
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4987
5217
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
4988
5218
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
4989
5219
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
5220
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
5221
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
5222
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
4990
5223
|
function parseToolArguments(raw) {
|
|
4991
5224
|
if (typeof raw !== "string") return raw;
|
|
4992
5225
|
try {
|
|
@@ -4995,6 +5228,73 @@ function parseToolArguments(raw) {
|
|
|
4995
5228
|
return raw;
|
|
4996
5229
|
}
|
|
4997
5230
|
}
|
|
5231
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
5232
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5233
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
5234
|
+
return {
|
|
5235
|
+
raw: inlineConfig,
|
|
5236
|
+
explicitPath: null,
|
|
5237
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
5238
|
+
};
|
|
5239
|
+
}
|
|
5240
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5241
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
5242
|
+
try {
|
|
5243
|
+
return {
|
|
5244
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
5245
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5246
|
+
sourcePath: configPath
|
|
5247
|
+
};
|
|
5248
|
+
} catch {
|
|
5249
|
+
return {
|
|
5250
|
+
raw: null,
|
|
5251
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5252
|
+
sourcePath: configPath
|
|
5253
|
+
};
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
5257
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
5258
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5259
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5260
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
5261
|
+
}
|
|
5262
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
5263
|
+
return {
|
|
5264
|
+
...process.env,
|
|
5265
|
+
...ctx.config.envVars || {},
|
|
5266
|
+
...overrideEnv || {}
|
|
5267
|
+
};
|
|
5268
|
+
}
|
|
5269
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
5270
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
5271
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
5272
|
+
const args = [];
|
|
5273
|
+
let configFilePath = null;
|
|
5274
|
+
let configContent = null;
|
|
5275
|
+
if (source.explicitPath) {
|
|
5276
|
+
configFilePath = source.explicitPath;
|
|
5277
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
5278
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
5279
|
+
configContent = source.raw;
|
|
5280
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
5281
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
5282
|
+
chmodSync(configFilePath, 384);
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
if (configFilePath) {
|
|
5286
|
+
args.push("--config-file", configFilePath);
|
|
5287
|
+
}
|
|
5288
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
5289
|
+
args.push("--model", ctx.config.model);
|
|
5290
|
+
}
|
|
5291
|
+
return {
|
|
5292
|
+
args,
|
|
5293
|
+
env: buildKimiSpawnEnv(env),
|
|
5294
|
+
configFilePath,
|
|
5295
|
+
configContent
|
|
5296
|
+
};
|
|
5297
|
+
}
|
|
4998
5298
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
4999
5299
|
return {
|
|
5000
5300
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5018,7 +5318,25 @@ var KimiDriver = class {
|
|
|
5018
5318
|
};
|
|
5019
5319
|
model = {
|
|
5020
5320
|
detectedModelsVerifiedAs: "launchable",
|
|
5021
|
-
toLaunchSpec: (modelId) =>
|
|
5321
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
5322
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
5323
|
+
const launchCtx = {
|
|
5324
|
+
...ctx,
|
|
5325
|
+
config: {
|
|
5326
|
+
...ctx.config,
|
|
5327
|
+
model: modelId
|
|
5328
|
+
}
|
|
5329
|
+
};
|
|
5330
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
5331
|
+
home: opts?.home,
|
|
5332
|
+
writeGeneratedConfig: false
|
|
5333
|
+
});
|
|
5334
|
+
return {
|
|
5335
|
+
args: launch.args,
|
|
5336
|
+
env: launch.env,
|
|
5337
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
5338
|
+
};
|
|
5339
|
+
}
|
|
5022
5340
|
};
|
|
5023
5341
|
supportsStdinNotification = true;
|
|
5024
5342
|
mcpToolPrefix = "";
|
|
@@ -5072,6 +5390,7 @@ var KimiDriver = class {
|
|
|
5072
5390
|
}
|
|
5073
5391
|
}
|
|
5074
5392
|
}), "utf8");
|
|
5393
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
5075
5394
|
const args = [
|
|
5076
5395
|
"--wire",
|
|
5077
5396
|
"--yolo",
|
|
@@ -5080,15 +5399,16 @@ var KimiDriver = class {
|
|
|
5080
5399
|
"--mcp-config-file",
|
|
5081
5400
|
mcpConfigPath,
|
|
5082
5401
|
"--session",
|
|
5083
|
-
this.sessionId
|
|
5402
|
+
this.sessionId,
|
|
5403
|
+
...launch.args
|
|
5084
5404
|
];
|
|
5085
5405
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5086
5406
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5087
5407
|
args.push("--model", launchRuntimeFields.model);
|
|
5088
5408
|
}
|
|
5089
5409
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5090
|
-
const
|
|
5091
|
-
const proc = spawn7(
|
|
5410
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
5411
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
5092
5412
|
cwd: ctx.workingDirectory,
|
|
5093
5413
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5094
5414
|
env: spawnEnv,
|
|
@@ -5096,7 +5416,7 @@ var KimiDriver = class {
|
|
|
5096
5416
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5097
5417
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5098
5418
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5099
|
-
shell:
|
|
5419
|
+
shell: spawnTarget.shell
|
|
5100
5420
|
});
|
|
5101
5421
|
proc.stdin?.write(JSON.stringify({
|
|
5102
5422
|
jsonrpc: "2.0",
|
|
@@ -5210,14 +5530,9 @@ var KimiDriver = class {
|
|
|
5210
5530
|
return detectKimiModels();
|
|
5211
5531
|
}
|
|
5212
5532
|
};
|
|
5213
|
-
function detectKimiModels(home = os3.homedir()) {
|
|
5214
|
-
const
|
|
5215
|
-
|
|
5216
|
-
try {
|
|
5217
|
-
raw = readFileSync3(configPath, "utf8");
|
|
5218
|
-
} catch {
|
|
5219
|
-
return null;
|
|
5220
|
-
}
|
|
5533
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
5534
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
5535
|
+
if (raw === null) return null;
|
|
5221
5536
|
const models = [];
|
|
5222
5537
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
5223
5538
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -5481,7 +5796,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
5481
5796
|
const platform = deps.platform ?? process.platform;
|
|
5482
5797
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
5483
5798
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
5484
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
5799
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
5485
5800
|
encoding: "utf8",
|
|
5486
5801
|
timeout: 5e3,
|
|
5487
5802
|
shell: platform === "win32"
|
|
@@ -5740,6 +6055,297 @@ var OpenCodeDriver = class {
|
|
|
5740
6055
|
}
|
|
5741
6056
|
};
|
|
5742
6057
|
|
|
6058
|
+
// src/drivers/pi.ts
|
|
6059
|
+
import { spawn as spawn9 } from "child_process";
|
|
6060
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
6061
|
+
import path11 from "path";
|
|
6062
|
+
import { fileURLToPath } from "url";
|
|
6063
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
6064
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
6065
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
6066
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
6067
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
6068
|
+
function parseSemver2(version) {
|
|
6069
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
6070
|
+
if (!match) return null;
|
|
6071
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
6072
|
+
}
|
|
6073
|
+
function isSupportedPiVersion(version) {
|
|
6074
|
+
if (!version) return true;
|
|
6075
|
+
const actual = parseSemver2(version);
|
|
6076
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
6077
|
+
if (!actual || !minimum) return true;
|
|
6078
|
+
for (let i = 0; i < 3; i += 1) {
|
|
6079
|
+
if (actual[i] > minimum[i]) return true;
|
|
6080
|
+
if (actual[i] < minimum[i]) return false;
|
|
6081
|
+
}
|
|
6082
|
+
return true;
|
|
6083
|
+
}
|
|
6084
|
+
function unsupportedPiVersionMessage(version) {
|
|
6085
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
6086
|
+
return `Pi SDK ${version} is unsupported; requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION}. Upgrade the daemon Pi dependency before starting this runtime.`;
|
|
6087
|
+
}
|
|
6088
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
6089
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
6090
|
+
if (unsupportedMessage) {
|
|
6091
|
+
return {
|
|
6092
|
+
available: false,
|
|
6093
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
6094
|
+
};
|
|
6095
|
+
}
|
|
6096
|
+
return { available: true, version };
|
|
6097
|
+
}
|
|
6098
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
6099
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
6100
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
6101
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
6102
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
6103
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
6104
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
6105
|
+
}
|
|
6106
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
6107
|
+
if (runnerPath.endsWith(".ts")) {
|
|
6108
|
+
return [...process.execArgv, runnerPath];
|
|
6109
|
+
}
|
|
6110
|
+
return [runnerPath];
|
|
6111
|
+
}
|
|
6112
|
+
async function buildPiLaunchOptions(ctx, opts = {}) {
|
|
6113
|
+
const command = opts.command ?? process.execPath;
|
|
6114
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
6115
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
6116
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
6117
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
6118
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
6119
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
6120
|
+
const runnerConfigPath = path11.join(
|
|
6121
|
+
piDir,
|
|
6122
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
6123
|
+
);
|
|
6124
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
6125
|
+
const runnerConfig = {
|
|
6126
|
+
cwd: ctx.workingDirectory,
|
|
6127
|
+
agentDir,
|
|
6128
|
+
sessionDir,
|
|
6129
|
+
sessionId: ctx.config.sessionId || null,
|
|
6130
|
+
standingPrompt: ctx.standingPrompt,
|
|
6131
|
+
prompt: turnPrompt,
|
|
6132
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
6133
|
+
};
|
|
6134
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
6135
|
+
`, { encoding: "utf8", mode: 384 });
|
|
6136
|
+
const args = [
|
|
6137
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
6138
|
+
"--config",
|
|
6139
|
+
runnerConfigPath
|
|
6140
|
+
];
|
|
6141
|
+
return {
|
|
6142
|
+
command,
|
|
6143
|
+
args,
|
|
6144
|
+
env: slock.spawnEnv,
|
|
6145
|
+
sessionDir,
|
|
6146
|
+
agentDir,
|
|
6147
|
+
runnerConfigPath,
|
|
6148
|
+
sdkVersion: PI_SDK_VERSION
|
|
6149
|
+
};
|
|
6150
|
+
}
|
|
6151
|
+
function isSystemFirstMessageTask2(message) {
|
|
6152
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
6153
|
+
}
|
|
6154
|
+
function buildPiSystemPrompt(config) {
|
|
6155
|
+
return buildCliTransportSystemPrompt(config, {
|
|
6156
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
6157
|
+
extraCriticalRules: [
|
|
6158
|
+
"- Runtime Profile migration controls are not available in the Pi runtime yet. If asked to acknowledge a runtime migration, explain the blocker instead of inventing a command."
|
|
6159
|
+
],
|
|
6160
|
+
postStartupNotes: [
|
|
6161
|
+
"**Pi runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
|
|
6162
|
+
],
|
|
6163
|
+
includeStdinNotificationSection: false,
|
|
6164
|
+
messageNotificationStyle: "poll"
|
|
6165
|
+
});
|
|
6166
|
+
}
|
|
6167
|
+
function contentText(content) {
|
|
6168
|
+
if (!content) return "";
|
|
6169
|
+
const chunks = [];
|
|
6170
|
+
for (const item of content) {
|
|
6171
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
6172
|
+
chunks.push(item.text);
|
|
6173
|
+
}
|
|
6174
|
+
}
|
|
6175
|
+
return chunks.join("");
|
|
6176
|
+
}
|
|
6177
|
+
function apiKeyErrorMessage(line) {
|
|
6178
|
+
const trimmed = line.trim();
|
|
6179
|
+
if (!trimmed) return null;
|
|
6180
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
6181
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
6182
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
6183
|
+
return null;
|
|
6184
|
+
}
|
|
6185
|
+
var PiDriver = class {
|
|
6186
|
+
id = "pi";
|
|
6187
|
+
lifecycle = {
|
|
6188
|
+
kind: "per_turn",
|
|
6189
|
+
start: "defer_until_concrete_message",
|
|
6190
|
+
exit: "terminate_on_turn_end",
|
|
6191
|
+
inFlightWake: "coalesce_into_pending"
|
|
6192
|
+
};
|
|
6193
|
+
communication = {
|
|
6194
|
+
chat: "slock_cli",
|
|
6195
|
+
runtimeControl: "none"
|
|
6196
|
+
};
|
|
6197
|
+
session = {
|
|
6198
|
+
recovery: "resume_or_fresh"
|
|
6199
|
+
};
|
|
6200
|
+
model = {
|
|
6201
|
+
detectedModelsVerifiedAs: "launchable",
|
|
6202
|
+
toLaunchSpec: async (modelId, ctx) => {
|
|
6203
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
6204
|
+
const launchCtx = {
|
|
6205
|
+
...ctx,
|
|
6206
|
+
config: {
|
|
6207
|
+
...ctx.config,
|
|
6208
|
+
model: modelId
|
|
6209
|
+
}
|
|
6210
|
+
};
|
|
6211
|
+
const launch = await buildPiLaunchOptions(launchCtx);
|
|
6212
|
+
return {
|
|
6213
|
+
args: launch.args,
|
|
6214
|
+
env: launch.env,
|
|
6215
|
+
configFiles: [launch.runnerConfigPath],
|
|
6216
|
+
params: {
|
|
6217
|
+
agentDir: launch.agentDir,
|
|
6218
|
+
sessionDir: launch.sessionDir,
|
|
6219
|
+
sdkVersion: launch.sdkVersion,
|
|
6220
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
6221
|
+
}
|
|
6222
|
+
};
|
|
6223
|
+
}
|
|
6224
|
+
};
|
|
6225
|
+
supportsStdinNotification = false;
|
|
6226
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
6227
|
+
busyDeliveryMode = "none";
|
|
6228
|
+
terminateProcessOnTurnEnd = true;
|
|
6229
|
+
deferSpawnUntilMessage = true;
|
|
6230
|
+
usesSlockCliForCommunication = true;
|
|
6231
|
+
sessionId = null;
|
|
6232
|
+
sessionAnnounced = false;
|
|
6233
|
+
apiKeyErrorAnnounced = false;
|
|
6234
|
+
turnEnded = false;
|
|
6235
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
6236
|
+
shouldDeferWakeMessage(message) {
|
|
6237
|
+
return isSystemFirstMessageTask2(message);
|
|
6238
|
+
}
|
|
6239
|
+
probe() {
|
|
6240
|
+
return probePi();
|
|
6241
|
+
}
|
|
6242
|
+
async detectModels() {
|
|
6243
|
+
return null;
|
|
6244
|
+
}
|
|
6245
|
+
async spawn(ctx) {
|
|
6246
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
6247
|
+
this.sessionAnnounced = false;
|
|
6248
|
+
this.apiKeyErrorAnnounced = false;
|
|
6249
|
+
this.turnEnded = false;
|
|
6250
|
+
this.assistantTextByMessageId.clear();
|
|
6251
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
6252
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
6253
|
+
const launch = await buildPiLaunchOptions(ctx);
|
|
6254
|
+
const proc = spawn9(launch.command, launch.args, {
|
|
6255
|
+
cwd: ctx.workingDirectory,
|
|
6256
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6257
|
+
env: launch.env,
|
|
6258
|
+
shell: false
|
|
6259
|
+
});
|
|
6260
|
+
proc.stdin?.end();
|
|
6261
|
+
return { process: proc };
|
|
6262
|
+
}
|
|
6263
|
+
parseLine(line) {
|
|
6264
|
+
let event;
|
|
6265
|
+
try {
|
|
6266
|
+
event = JSON.parse(line);
|
|
6267
|
+
} catch {
|
|
6268
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
6269
|
+
const message = apiKeyErrorMessage(line);
|
|
6270
|
+
if (!message) return [];
|
|
6271
|
+
this.apiKeyErrorAnnounced = true;
|
|
6272
|
+
this.turnEnded = true;
|
|
6273
|
+
return [
|
|
6274
|
+
{ kind: "error", message },
|
|
6275
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
6276
|
+
];
|
|
6277
|
+
}
|
|
6278
|
+
const events = [];
|
|
6279
|
+
if (event.type === "session" && event.id) {
|
|
6280
|
+
this.sessionId = event.id;
|
|
6281
|
+
}
|
|
6282
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
6283
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
6284
|
+
this.sessionAnnounced = true;
|
|
6285
|
+
}
|
|
6286
|
+
switch (event.type) {
|
|
6287
|
+
case "agent_start":
|
|
6288
|
+
case "turn_start":
|
|
6289
|
+
events.push({ kind: "thinking", text: "" });
|
|
6290
|
+
break;
|
|
6291
|
+
case "message_update":
|
|
6292
|
+
case "message_end":
|
|
6293
|
+
if (event.message?.role === "assistant") {
|
|
6294
|
+
const key = event.message.id || "current";
|
|
6295
|
+
const currentText = contentText(event.message.content);
|
|
6296
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
6297
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
6298
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
6299
|
+
} else if (currentText && currentText !== previousText) {
|
|
6300
|
+
events.push({ kind: "text", text: currentText });
|
|
6301
|
+
}
|
|
6302
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
6303
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
6304
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
6305
|
+
}
|
|
6306
|
+
}
|
|
6307
|
+
break;
|
|
6308
|
+
case "tool_execution_start":
|
|
6309
|
+
events.push({
|
|
6310
|
+
kind: "tool_call",
|
|
6311
|
+
name: event.toolName || "unknown_tool",
|
|
6312
|
+
input: event.args
|
|
6313
|
+
});
|
|
6314
|
+
break;
|
|
6315
|
+
case "tool_execution_end":
|
|
6316
|
+
events.push({
|
|
6317
|
+
kind: "tool_output",
|
|
6318
|
+
name: event.toolName || "unknown_tool"
|
|
6319
|
+
});
|
|
6320
|
+
if (event.isError) {
|
|
6321
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
6322
|
+
}
|
|
6323
|
+
break;
|
|
6324
|
+
case "compaction_start":
|
|
6325
|
+
events.push({ kind: "compaction_started" });
|
|
6326
|
+
break;
|
|
6327
|
+
case "compaction_end":
|
|
6328
|
+
events.push({ kind: "compaction_finished" });
|
|
6329
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
6330
|
+
break;
|
|
6331
|
+
case "turn_end":
|
|
6332
|
+
case "agent_end":
|
|
6333
|
+
if (!this.turnEnded) {
|
|
6334
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
6335
|
+
this.turnEnded = true;
|
|
6336
|
+
}
|
|
6337
|
+
break;
|
|
6338
|
+
}
|
|
6339
|
+
return events;
|
|
6340
|
+
}
|
|
6341
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
6342
|
+
return null;
|
|
6343
|
+
}
|
|
6344
|
+
buildSystemPrompt(config, _agentId) {
|
|
6345
|
+
return buildPiSystemPrompt(config);
|
|
6346
|
+
}
|
|
6347
|
+
};
|
|
6348
|
+
|
|
5743
6349
|
// src/drivers/index.ts
|
|
5744
6350
|
var driverFactories = {
|
|
5745
6351
|
claude: () => new ClaudeDriver(),
|
|
@@ -5749,7 +6355,8 @@ var driverFactories = {
|
|
|
5749
6355
|
cursor: () => new CursorDriver(),
|
|
5750
6356
|
gemini: () => new GeminiDriver(),
|
|
5751
6357
|
kimi: () => new KimiDriver(),
|
|
5752
|
-
opencode: () => new OpenCodeDriver()
|
|
6358
|
+
opencode: () => new OpenCodeDriver(),
|
|
6359
|
+
pi: () => new PiDriver()
|
|
5753
6360
|
};
|
|
5754
6361
|
function getDriver(runtimeId) {
|
|
5755
6362
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -5762,7 +6369,7 @@ function getDriver(runtimeId) {
|
|
|
5762
6369
|
|
|
5763
6370
|
// src/workspaces.ts
|
|
5764
6371
|
import { readdir, rm, stat } from "fs/promises";
|
|
5765
|
-
import
|
|
6372
|
+
import path12 from "path";
|
|
5766
6373
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
5767
6374
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
5768
6375
|
}
|
|
@@ -5770,7 +6377,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
5770
6377
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
5771
6378
|
return null;
|
|
5772
6379
|
}
|
|
5773
|
-
return
|
|
6380
|
+
return path12.join(dataDir, directoryName);
|
|
5774
6381
|
}
|
|
5775
6382
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
5776
6383
|
return {
|
|
@@ -5819,7 +6426,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
5819
6426
|
return summary;
|
|
5820
6427
|
}
|
|
5821
6428
|
const childSummaries = await Promise.all(
|
|
5822
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
6429
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
5823
6430
|
);
|
|
5824
6431
|
for (const childSummary of childSummaries) {
|
|
5825
6432
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -5838,7 +6445,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
5838
6445
|
if (!entry.isDirectory()) {
|
|
5839
6446
|
return null;
|
|
5840
6447
|
}
|
|
5841
|
-
const dirPath =
|
|
6448
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
5842
6449
|
try {
|
|
5843
6450
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
5844
6451
|
return {
|
|
@@ -6278,12 +6885,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
6278
6885
|
for (const entry of entries) {
|
|
6279
6886
|
if (++visited > maxEntries) return null;
|
|
6280
6887
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
6281
|
-
return
|
|
6888
|
+
return path13.join(dir, entry.name);
|
|
6282
6889
|
}
|
|
6283
6890
|
for (const entry of entries) {
|
|
6284
6891
|
if (++visited > maxEntries) return null;
|
|
6285
6892
|
if (!entry.isDirectory()) continue;
|
|
6286
|
-
const found = visit(
|
|
6893
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
6287
6894
|
if (found) return found;
|
|
6288
6895
|
}
|
|
6289
6896
|
return null;
|
|
@@ -6296,10 +6903,10 @@ function safeSessionFilename(value) {
|
|
|
6296
6903
|
}
|
|
6297
6904
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
6298
6905
|
try {
|
|
6299
|
-
const dir =
|
|
6300
|
-
|
|
6301
|
-
const filePath =
|
|
6302
|
-
|
|
6906
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6907
|
+
mkdirSync5(dir, { recursive: true });
|
|
6908
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6909
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
6303
6910
|
type: "runtime_session_handoff",
|
|
6304
6911
|
runtime,
|
|
6305
6912
|
sessionId,
|
|
@@ -6318,7 +6925,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
6318
6925
|
}
|
|
6319
6926
|
}
|
|
6320
6927
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6321
|
-
const directPath =
|
|
6928
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
6322
6929
|
if (directPath) {
|
|
6323
6930
|
try {
|
|
6324
6931
|
if (statSync(directPath).isFile()) {
|
|
@@ -6327,7 +6934,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
6327
6934
|
} catch {
|
|
6328
6935
|
}
|
|
6329
6936
|
}
|
|
6330
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6937
|
+
const resolvedPath = runtime === "claude" ? findSessionJsonl(path13.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path13.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
|
|
6331
6938
|
if (!resolvedPath && fallbackDir) {
|
|
6332
6939
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
6333
6940
|
if (fallback) return fallback;
|
|
@@ -6416,7 +7023,7 @@ function dynamicClaimInstruction(driver) {
|
|
|
6416
7023
|
}
|
|
6417
7024
|
function formatIncomingMessage(message, driver) {
|
|
6418
7025
|
const threadJoinPrefix = message.thread_join_context ? [
|
|
6419
|
-
`[
|
|
7026
|
+
`[Slock thread context: you were added to a new thread via @mention.]`,
|
|
6420
7027
|
`parent: ${message.thread_join_context.parent_target}`,
|
|
6421
7028
|
`thread: ${message.thread_join_context.thread_target}`,
|
|
6422
7029
|
`suggested next step: ${driver?.usesSlockCliForCommunication ? `slock message read --channel "${message.thread_join_context.suggested_read_history_target}"` : `${communicationCommand(driver, "read_history")}(channel="${message.thread_join_context.suggested_read_history_target}")`}`,
|
|
@@ -7242,7 +7849,7 @@ function getBusyDeliveryNote(driver) {
|
|
|
7242
7849
|
if (driver.busyDeliveryMode === "gated") {
|
|
7243
7850
|
return "\n\nNote: While you are busy, the daemon may write batched inbox-count notifications into your active turn at runtime-observed safe boundaries. Call check_messages to read the pending messages before context-sensitive side effects.";
|
|
7244
7851
|
}
|
|
7245
|
-
return "\n\nNote: While you are busy, you may receive [
|
|
7852
|
+
return "\n\nNote: While you are busy, you may receive [Slock inbox notice: ...] messages. Finish your current step, then call check_messages to check for messages.";
|
|
7246
7853
|
}
|
|
7247
7854
|
var NATIVE_STANDING_PROMPT_STARTUP_INPUT = (
|
|
7248
7855
|
// Claude Code 2.1.114 treats "follow your system prompt" style user turns as
|
|
@@ -7280,6 +7887,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7280
7887
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
7281
7888
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
7282
7889
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
7890
|
+
agentVisibleMessageIds = /* @__PURE__ */ new Map();
|
|
7283
7891
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
7284
7892
|
this.chatBridgePath = chatBridgePath;
|
|
7285
7893
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
@@ -7325,6 +7933,24 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7325
7933
|
getVisibleBoundary(agentId, target) {
|
|
7326
7934
|
return this.agentVisibleBoundaries.get(agentId)?.get(target);
|
|
7327
7935
|
}
|
|
7936
|
+
visibleMessageIdMap(agentId) {
|
|
7937
|
+
let map = this.agentVisibleMessageIds.get(agentId);
|
|
7938
|
+
if (!map) {
|
|
7939
|
+
map = /* @__PURE__ */ new Map();
|
|
7940
|
+
this.agentVisibleMessageIds.set(agentId, map);
|
|
7941
|
+
}
|
|
7942
|
+
return map;
|
|
7943
|
+
}
|
|
7944
|
+
getVisibleMessageIdSet(agentId, target) {
|
|
7945
|
+
return this.agentVisibleMessageIds.get(agentId)?.get(target);
|
|
7946
|
+
}
|
|
7947
|
+
isVisibleMessageModelSeen(agentId, target, message) {
|
|
7948
|
+
const seq = Number(message.seq ?? 0);
|
|
7949
|
+
const boundary = this.getVisibleBoundary(agentId, target);
|
|
7950
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= Math.floor(seq)) return true;
|
|
7951
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : "";
|
|
7952
|
+
return id.length > 0 && this.getVisibleMessageIdSet(agentId, target)?.has(id) === true;
|
|
7953
|
+
}
|
|
7328
7954
|
scheduleStdinNotification(agentId, ap, delayMs) {
|
|
7329
7955
|
return ap.notifications.schedule(() => {
|
|
7330
7956
|
this.sendStdinNotification(agentId);
|
|
@@ -7359,13 +7985,14 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7359
7985
|
};
|
|
7360
7986
|
for (const message of input.messages) {
|
|
7361
7987
|
const seq = Number(message.seq ?? 0);
|
|
7362
|
-
if (!Number.isFinite(seq) || seq <= 0) continue;
|
|
7363
7988
|
const target = input.target ?? formatVisibleMessageTarget(message);
|
|
7364
7989
|
if (!target) continue;
|
|
7365
7990
|
const bucket = ensureBucket(target);
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7991
|
+
if (Number.isFinite(seq) && seq > 0) {
|
|
7992
|
+
bucket.maxSeq = Math.max(bucket.maxSeq, Math.floor(seq));
|
|
7993
|
+
bucket.seqs.add(Math.floor(seq));
|
|
7994
|
+
}
|
|
7995
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : null;
|
|
7369
7996
|
if (id) bucket.ids.add(id);
|
|
7370
7997
|
}
|
|
7371
7998
|
if (input.target && typeof input.boundarySeq === "number" && Number.isFinite(input.boundarySeq) && input.boundarySeq > 0) {
|
|
@@ -7374,10 +8001,19 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7374
8001
|
}
|
|
7375
8002
|
if (byTarget.size === 0) return;
|
|
7376
8003
|
const boundaryMap = this.visibleBoundaryMap(agentId);
|
|
8004
|
+
const visibleIds = this.visibleMessageIdMap(agentId);
|
|
7377
8005
|
for (const [target, bucket] of byTarget) {
|
|
7378
8006
|
const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
|
|
7379
8007
|
const previous = boundaryMap.get(target) ?? 0;
|
|
7380
8008
|
boundaryMap.set(target, Math.max(previous, highWaterSeq));
|
|
8009
|
+
if (bucket.ids.size > 0) {
|
|
8010
|
+
let targetIds = visibleIds.get(target);
|
|
8011
|
+
if (!targetIds) {
|
|
8012
|
+
targetIds = /* @__PURE__ */ new Set();
|
|
8013
|
+
visibleIds.set(target, targetIds);
|
|
8014
|
+
}
|
|
8015
|
+
for (const id of bucket.ids) targetIds.add(id);
|
|
8016
|
+
}
|
|
7381
8017
|
}
|
|
7382
8018
|
const suppress = (messages) => {
|
|
7383
8019
|
if (!messages || messages.length === 0) return 0;
|
|
@@ -7410,6 +8046,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7410
8046
|
return {
|
|
7411
8047
|
getBoundary: (target) => this.getVisibleBoundary(agentId, target),
|
|
7412
8048
|
getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
|
|
8049
|
+
isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
|
|
7413
8050
|
getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
|
|
7414
8051
|
consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
|
|
7415
8052
|
recordDrainOutcome: (input) => {
|
|
@@ -7721,7 +8358,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7721
8358
|
);
|
|
7722
8359
|
wakeMessage = void 0;
|
|
7723
8360
|
}
|
|
7724
|
-
const agentDataDir =
|
|
8361
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7725
8362
|
await mkdir(agentDataDir, { recursive: true });
|
|
7726
8363
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
7727
8364
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -7735,23 +8372,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7735
8372
|
);
|
|
7736
8373
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
7737
8374
|
}
|
|
7738
|
-
const memoryMdPath =
|
|
8375
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
7739
8376
|
try {
|
|
7740
8377
|
await access(memoryMdPath);
|
|
7741
8378
|
} catch {
|
|
7742
8379
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
7743
8380
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
7744
8381
|
}
|
|
7745
|
-
const notesDir =
|
|
8382
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
7746
8383
|
await mkdir(notesDir, { recursive: true });
|
|
7747
8384
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
7748
8385
|
const seedFiles = buildOnboardingSeedFiles();
|
|
7749
8386
|
for (const { relativePath, content } of seedFiles) {
|
|
7750
|
-
const fullPath =
|
|
8387
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
7751
8388
|
try {
|
|
7752
8389
|
await access(fullPath);
|
|
7753
8390
|
} catch {
|
|
7754
|
-
await mkdir(
|
|
8391
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
7755
8392
|
await writeFile(fullPath, content);
|
|
7756
8393
|
}
|
|
7757
8394
|
}
|
|
@@ -8623,7 +9260,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8623
9260
|
return true;
|
|
8624
9261
|
}
|
|
8625
9262
|
async resetWorkspace(agentId) {
|
|
8626
|
-
const agentDataDir =
|
|
9263
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
8627
9264
|
try {
|
|
8628
9265
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
8629
9266
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -8684,7 +9321,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8684
9321
|
return result;
|
|
8685
9322
|
}
|
|
8686
9323
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
8687
|
-
const workspacePath =
|
|
9324
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
8688
9325
|
return {
|
|
8689
9326
|
agentId,
|
|
8690
9327
|
launchId,
|
|
@@ -8941,7 +9578,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8941
9578
|
}
|
|
8942
9579
|
// Workspace file browsing
|
|
8943
9580
|
async getFileTree(agentId, dirPath) {
|
|
8944
|
-
const agentDir =
|
|
9581
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8945
9582
|
try {
|
|
8946
9583
|
await stat2(agentDir);
|
|
8947
9584
|
} catch {
|
|
@@ -8949,8 +9586,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8949
9586
|
}
|
|
8950
9587
|
let targetDir = agentDir;
|
|
8951
9588
|
if (dirPath) {
|
|
8952
|
-
const resolved =
|
|
8953
|
-
if (!resolved.startsWith(agentDir +
|
|
9589
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
9590
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8954
9591
|
return [];
|
|
8955
9592
|
}
|
|
8956
9593
|
targetDir = resolved;
|
|
@@ -8958,14 +9595,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8958
9595
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
8959
9596
|
}
|
|
8960
9597
|
async readFile(agentId, filePath) {
|
|
8961
|
-
const agentDir =
|
|
8962
|
-
const resolved =
|
|
8963
|
-
if (!resolved.startsWith(agentDir +
|
|
9598
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
9599
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
9600
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8964
9601
|
throw new Error("Access denied");
|
|
8965
9602
|
}
|
|
8966
9603
|
const info = await stat2(resolved);
|
|
8967
9604
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
8968
|
-
const ext =
|
|
9605
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
8969
9606
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
8970
9607
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
8971
9608
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -9000,13 +9637,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9000
9637
|
const agent = this.agents.get(agentId);
|
|
9001
9638
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
9002
9639
|
const home = os5.homedir();
|
|
9003
|
-
const workspaceDir =
|
|
9640
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
9004
9641
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
9005
9642
|
const globalResults = await Promise.all(
|
|
9006
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
9643
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
9007
9644
|
);
|
|
9008
9645
|
const workspaceResults = await Promise.all(
|
|
9009
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
9646
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
9010
9647
|
);
|
|
9011
9648
|
const dedup = (skills) => {
|
|
9012
9649
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9035,7 +9672,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9035
9672
|
const skills = [];
|
|
9036
9673
|
for (const entry of entries) {
|
|
9037
9674
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
9038
|
-
const skillMd =
|
|
9675
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
9039
9676
|
try {
|
|
9040
9677
|
const content = await readFile(skillMd, "utf-8");
|
|
9041
9678
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -9046,7 +9683,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9046
9683
|
} else if (entry.name.endsWith(".md")) {
|
|
9047
9684
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
9048
9685
|
try {
|
|
9049
|
-
const content = await readFile(
|
|
9686
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
9050
9687
|
const skill = this.parseSkillMd(cmdName, content);
|
|
9051
9688
|
skill.sourcePath = dir;
|
|
9052
9689
|
skills.push(skill);
|
|
@@ -9606,7 +10243,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9606
10243
|
}
|
|
9607
10244
|
}
|
|
9608
10245
|
isThinkingBlockMutationError(message) {
|
|
9609
|
-
return /thinking.*redacted_thinking|redacted_thinking.*thinking/i.test(message) && /cannot be modified/i.test(message);
|
|
10246
|
+
return /thinking.*redacted_thinking|redacted_thinking.*thinking/i.test(message) && /cannot be modified/i.test(message) || /messages\.\d+\.content\.\d+\.text\.start_timestamp:\s*Extra inputs are not permitted/i.test(message);
|
|
9610
10247
|
}
|
|
9611
10248
|
markRuntimeProgressStaleIfNeeded(agentId, ap) {
|
|
9612
10249
|
if (ap.lastActivity !== "working" && ap.lastActivity !== "thinking") return false;
|
|
@@ -9935,15 +10572,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9935
10572
|
}
|
|
9936
10573
|
}
|
|
9937
10574
|
recordRuntimeTelemetry(agentId, ap, event) {
|
|
10575
|
+
const telemetryAttrs = {
|
|
10576
|
+
...event.attrs,
|
|
10577
|
+
...event.source ? { source: event.source } : {},
|
|
10578
|
+
...event.usageKind ? { usageKind: event.usageKind } : {},
|
|
10579
|
+
...event.sessionId ? { sessionId: event.sessionId } : {},
|
|
10580
|
+
...event.turnId ? { turnId: event.turnId } : {},
|
|
10581
|
+
...event.runtimeResultId ? { runtimeResultId: event.runtimeResultId } : {}
|
|
10582
|
+
};
|
|
9938
10583
|
const attrs = {
|
|
9939
10584
|
agentId,
|
|
9940
10585
|
launchId: ap.launchId || void 0,
|
|
9941
10586
|
runtime: ap.config.runtime,
|
|
9942
10587
|
model: ap.config.model,
|
|
9943
10588
|
telemetry_name: event.name,
|
|
9944
|
-
...
|
|
10589
|
+
...telemetryAttrs
|
|
9945
10590
|
};
|
|
9946
|
-
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`,
|
|
10591
|
+
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
|
|
9947
10592
|
this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
|
|
9948
10593
|
}
|
|
9949
10594
|
sendAgentStatus(agentId, status, launchId) {
|
|
@@ -10017,7 +10662,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10017
10662
|
}
|
|
10018
10663
|
const inboxCount = ap.inbox.length;
|
|
10019
10664
|
if (inboxCount === 0) return false;
|
|
10020
|
-
const notification = `[
|
|
10665
|
+
const notification = `[Slock inbox notice: You have ${inboxCount} pending inbox message${inboxCount > 1 ? "s" : ""}. Call check_messages to read ${inboxCount > 1 ? "them" : "it"} when you're ready.]`;
|
|
10021
10666
|
logger.info(`[Agent ${agentId}] Sending stdin notification: ${inboxCount} pending inbox message(s)`);
|
|
10022
10667
|
const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
|
|
10023
10668
|
if (encoded) {
|
|
@@ -10164,8 +10809,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
10164
10809
|
const nodes = [];
|
|
10165
10810
|
for (const entry of entries) {
|
|
10166
10811
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
10167
|
-
const fullPath =
|
|
10168
|
-
const relativePath =
|
|
10812
|
+
const fullPath = path13.join(dir, entry.name);
|
|
10813
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
10169
10814
|
let info;
|
|
10170
10815
|
try {
|
|
10171
10816
|
info = await stat2(fullPath);
|
|
@@ -10470,9 +11115,9 @@ var ReminderCache = class {
|
|
|
10470
11115
|
|
|
10471
11116
|
// src/machineLock.ts
|
|
10472
11117
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
10473
|
-
import { mkdirSync as
|
|
11118
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
10474
11119
|
import os6 from "os";
|
|
10475
|
-
import
|
|
11120
|
+
import path14 from "path";
|
|
10476
11121
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
10477
11122
|
var DaemonMachineLockConflictError = class extends Error {
|
|
10478
11123
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -10494,7 +11139,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
10494
11139
|
return resolveSlockHomePath("machines");
|
|
10495
11140
|
}
|
|
10496
11141
|
function ownerPath(lockDir) {
|
|
10497
|
-
return
|
|
11142
|
+
return path14.join(lockDir, "owner.json");
|
|
10498
11143
|
}
|
|
10499
11144
|
function readOwner(lockDir) {
|
|
10500
11145
|
try {
|
|
@@ -10524,13 +11169,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
10524
11169
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
10525
11170
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
10526
11171
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
10527
|
-
const machineDir =
|
|
10528
|
-
const lockDir =
|
|
11172
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
11173
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
10529
11174
|
const token = randomUUID3();
|
|
10530
|
-
|
|
11175
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
10531
11176
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
10532
11177
|
try {
|
|
10533
|
-
|
|
11178
|
+
mkdirSync6(lockDir);
|
|
10534
11179
|
const owner = {
|
|
10535
11180
|
pid: process.pid,
|
|
10536
11181
|
token,
|
|
@@ -10540,7 +11185,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
10540
11185
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
10541
11186
|
};
|
|
10542
11187
|
try {
|
|
10543
|
-
|
|
11188
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
10544
11189
|
`, { mode: 384 });
|
|
10545
11190
|
} catch (err) {
|
|
10546
11191
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -10553,7 +11198,15 @@ function acquireDaemonMachineLock(options) {
|
|
|
10553
11198
|
release: () => {
|
|
10554
11199
|
const currentOwner = readOwner(lockDir);
|
|
10555
11200
|
if (currentOwner?.pid === process.pid && currentOwner.token === token) {
|
|
10556
|
-
|
|
11201
|
+
const released = { ...currentOwner, pid: 0 };
|
|
11202
|
+
try {
|
|
11203
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(released, null, 2)}
|
|
11204
|
+
`, {
|
|
11205
|
+
mode: 384
|
|
11206
|
+
});
|
|
11207
|
+
} catch {
|
|
11208
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
11209
|
+
}
|
|
10557
11210
|
}
|
|
10558
11211
|
}
|
|
10559
11212
|
};
|
|
@@ -10577,8 +11230,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
10577
11230
|
}
|
|
10578
11231
|
|
|
10579
11232
|
// src/localTraceSink.ts
|
|
10580
|
-
import { appendFileSync, mkdirSync as
|
|
10581
|
-
import
|
|
11233
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
11234
|
+
import path15 from "path";
|
|
10582
11235
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
10583
11236
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
10584
11237
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -10615,7 +11268,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10615
11268
|
currentSize = 0;
|
|
10616
11269
|
sequence = 0;
|
|
10617
11270
|
constructor(options) {
|
|
10618
|
-
this.traceDir =
|
|
11271
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
10619
11272
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
10620
11273
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
10621
11274
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -10641,15 +11294,15 @@ var LocalRotatingTraceSink = class {
|
|
|
10641
11294
|
return this.currentFile;
|
|
10642
11295
|
}
|
|
10643
11296
|
ensureFile(nextBytes) {
|
|
10644
|
-
|
|
11297
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
10645
11298
|
const nowMs = this.nowMsProvider();
|
|
10646
11299
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
10647
11300
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
10648
|
-
this.currentFile =
|
|
11301
|
+
this.currentFile = path15.join(
|
|
10649
11302
|
this.traceDir,
|
|
10650
11303
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
10651
11304
|
);
|
|
10652
|
-
|
|
11305
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
10653
11306
|
this.currentSize = statSync3(this.currentFile).size;
|
|
10654
11307
|
this.currentFileOpenedAtMs = nowMs;
|
|
10655
11308
|
this.pruneOldFiles();
|
|
@@ -10660,7 +11313,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10660
11313
|
const excess = files.length - this.maxFiles;
|
|
10661
11314
|
if (excess <= 0) return;
|
|
10662
11315
|
for (const file of files.slice(0, excess)) {
|
|
10663
|
-
rmSync4(
|
|
11316
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
10664
11317
|
}
|
|
10665
11318
|
}
|
|
10666
11319
|
};
|
|
@@ -10751,11 +11404,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
10751
11404
|
import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
10752
11405
|
import { gzipSync } from "zlib";
|
|
10753
11406
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
10754
|
-
import
|
|
11407
|
+
import path16 from "path";
|
|
10755
11408
|
|
|
10756
11409
|
// src/directUploadCapability.ts
|
|
10757
|
-
function joinUrl(base,
|
|
10758
|
-
return `${base.replace(/\/+$/, "")}${
|
|
11410
|
+
function joinUrl(base, path18) {
|
|
11411
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
10759
11412
|
}
|
|
10760
11413
|
function jsonHeaders(apiKey) {
|
|
10761
11414
|
return {
|
|
@@ -10974,7 +11627,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
10974
11627
|
}, nextMs);
|
|
10975
11628
|
}
|
|
10976
11629
|
async findUploadCandidates() {
|
|
10977
|
-
const traceDir =
|
|
11630
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
10978
11631
|
let names;
|
|
10979
11632
|
try {
|
|
10980
11633
|
names = await readdir3(traceDir);
|
|
@@ -10986,8 +11639,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10986
11639
|
const currentFile = this.options.currentFileProvider?.();
|
|
10987
11640
|
const candidates = [];
|
|
10988
11641
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
10989
|
-
const file =
|
|
10990
|
-
if (currentFile &&
|
|
11642
|
+
const file = path16.join(traceDir, name);
|
|
11643
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
10991
11644
|
if (await this.isUploaded(file)) continue;
|
|
10992
11645
|
try {
|
|
10993
11646
|
const info = await stat3(file);
|
|
@@ -11061,8 +11714,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
11061
11714
|
}
|
|
11062
11715
|
}
|
|
11063
11716
|
uploadStatePath(file) {
|
|
11064
|
-
const stateDir =
|
|
11065
|
-
return
|
|
11717
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
11718
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
11066
11719
|
}
|
|
11067
11720
|
async isUploaded(file) {
|
|
11068
11721
|
try {
|
|
@@ -11074,9 +11727,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
11074
11727
|
}
|
|
11075
11728
|
async markUploaded(file, metadata) {
|
|
11076
11729
|
const stateFile = this.uploadStatePath(file);
|
|
11077
|
-
await mkdir2(
|
|
11730
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
11078
11731
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
11079
|
-
file:
|
|
11732
|
+
file: path16.basename(file),
|
|
11080
11733
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11081
11734
|
...metadata
|
|
11082
11735
|
}, null, 2)}
|
|
@@ -11098,7 +11751,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
11098
11751
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
11099
11752
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
11100
11753
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
11101
|
-
var DAEMON_CLI_USAGE =
|
|
11754
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
11102
11755
|
var RunnerCredentialMintError2 = class extends Error {
|
|
11103
11756
|
code;
|
|
11104
11757
|
retryable;
|
|
@@ -11134,9 +11787,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
11134
11787
|
async function waitForRunnerCredentialRetry2() {
|
|
11135
11788
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
11136
11789
|
}
|
|
11137
|
-
function parseDaemonCliArgs(args) {
|
|
11790
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
11138
11791
|
let serverUrl = "";
|
|
11139
|
-
let apiKey = "";
|
|
11792
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
11140
11793
|
for (let i = 0; i < args.length; i++) {
|
|
11141
11794
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
11142
11795
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -11153,23 +11806,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
11153
11806
|
}
|
|
11154
11807
|
}
|
|
11155
11808
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
11156
|
-
const dirname =
|
|
11157
|
-
const jsPath =
|
|
11809
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
11810
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
11158
11811
|
try {
|
|
11159
11812
|
accessSync(jsPath);
|
|
11160
11813
|
return jsPath;
|
|
11161
11814
|
} catch {
|
|
11162
|
-
return
|
|
11815
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
11163
11816
|
}
|
|
11164
11817
|
}
|
|
11165
11818
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
11166
|
-
const thisDir =
|
|
11167
|
-
const bundledDistPath =
|
|
11819
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
11820
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
11168
11821
|
try {
|
|
11169
11822
|
accessSync(bundledDistPath);
|
|
11170
11823
|
return bundledDistPath;
|
|
11171
11824
|
} catch {
|
|
11172
|
-
const workspaceDistPath =
|
|
11825
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
11173
11826
|
accessSync(workspaceDistPath);
|
|
11174
11827
|
return workspaceDistPath;
|
|
11175
11828
|
}
|
|
@@ -11299,6 +11952,11 @@ function summarizeIncomingMessage(msg) {
|
|
|
11299
11952
|
var DaemonCore = class {
|
|
11300
11953
|
options;
|
|
11301
11954
|
daemonVersion;
|
|
11955
|
+
// When this runner is launched by a managed Computer service, the
|
|
11956
|
+
// service exports SLOCK_COMPUTER_VERSION (the `@slock-ai/computer`
|
|
11957
|
+
// bundle version). Reported in `ready` so the server can surface the
|
|
11958
|
+
// Computer's own version (distinct from the underlying daemonVersion).
|
|
11959
|
+
computerVersion;
|
|
11302
11960
|
chatBridgePath;
|
|
11303
11961
|
slockCliPath;
|
|
11304
11962
|
slockHome;
|
|
@@ -11314,6 +11972,7 @@ var DaemonCore = class {
|
|
|
11314
11972
|
constructor(options) {
|
|
11315
11973
|
this.options = options;
|
|
11316
11974
|
this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
|
|
11975
|
+
this.computerVersion = (process.env.SLOCK_COMPUTER_VERSION || "").trim() || null;
|
|
11317
11976
|
this.chatBridgePath = options.chatBridgePath ?? resolveChatBridgePath();
|
|
11318
11977
|
this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
|
|
11319
11978
|
this.slockHome = resolveSlockHome();
|
|
@@ -11348,7 +12007,7 @@ var DaemonCore = class {
|
|
|
11348
12007
|
}
|
|
11349
12008
|
resolveMachineStateRoot() {
|
|
11350
12009
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
11351
|
-
if (this.options.dataDir) return
|
|
12010
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
11352
12011
|
return resolveDefaultMachineStateRoot();
|
|
11353
12012
|
}
|
|
11354
12013
|
shouldEnableLocalTrace() {
|
|
@@ -11374,7 +12033,7 @@ var DaemonCore = class {
|
|
|
11374
12033
|
sink: this.localTraceSink
|
|
11375
12034
|
});
|
|
11376
12035
|
this.agentManager.setTracer(this.tracer);
|
|
11377
|
-
this.agentManager.setCliTransportTraceDir(
|
|
12036
|
+
this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
|
|
11378
12037
|
}
|
|
11379
12038
|
installTraceBundleUploader(machineDir) {
|
|
11380
12039
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -11773,6 +12432,26 @@ var DaemonCore = class {
|
|
|
11773
12432
|
case "ping":
|
|
11774
12433
|
this.connection.send({ type: "pong" });
|
|
11775
12434
|
break;
|
|
12435
|
+
case "computer:restart":
|
|
12436
|
+
case "computer:upgrade": {
|
|
12437
|
+
const action = msg.type === "computer:restart" ? "restart" : "upgrade";
|
|
12438
|
+
this.recordDaemonTrace("daemon.computer_control.received", {
|
|
12439
|
+
action,
|
|
12440
|
+
handled: Boolean(this.options.onComputerControl)
|
|
12441
|
+
});
|
|
12442
|
+
if (this.options.onComputerControl) {
|
|
12443
|
+
try {
|
|
12444
|
+
this.options.onComputerControl(action);
|
|
12445
|
+
} catch (err) {
|
|
12446
|
+
logger.error(
|
|
12447
|
+
`[Daemon] computer:${action} control handler failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12448
|
+
);
|
|
12449
|
+
}
|
|
12450
|
+
} else {
|
|
12451
|
+
logger.info(`[Daemon] Ignoring computer:${action} \u2014 not launched by a Computer service.`);
|
|
12452
|
+
}
|
|
12453
|
+
break;
|
|
12454
|
+
}
|
|
11776
12455
|
}
|
|
11777
12456
|
}
|
|
11778
12457
|
onReminderFire(job) {
|
|
@@ -11799,7 +12478,8 @@ var DaemonCore = class {
|
|
|
11799
12478
|
runningAgents: runningAgentIds,
|
|
11800
12479
|
hostname: this.options.hostname ?? os7.hostname(),
|
|
11801
12480
|
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
11802
|
-
daemonVersion: this.daemonVersion
|
|
12481
|
+
daemonVersion: this.daemonVersion,
|
|
12482
|
+
...this.computerVersion ? { computerVersion: this.computerVersion } : {}
|
|
11803
12483
|
});
|
|
11804
12484
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
11805
12485
|
runtimes_count: runtimes.length,
|
|
@@ -11860,6 +12540,8 @@ var DaemonCore = class {
|
|
|
11860
12540
|
};
|
|
11861
12541
|
|
|
11862
12542
|
export {
|
|
12543
|
+
DAEMON_API_KEY_ENV,
|
|
12544
|
+
scrubDaemonAuthEnv,
|
|
11863
12545
|
resolveWorkspaceDirectoryPath,
|
|
11864
12546
|
scanWorkspaceDirectories,
|
|
11865
12547
|
deleteWorkspaceDirectory,
|