@slock-ai/daemon 0.55.2 → 0.55.3-play.20260531155023
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-3EAZBKTI.js} +626 -113
- package/dist/cli/index.js +1 -0
- 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 },
|
|
@@ -1042,6 +1043,13 @@ var RUNTIME_MODELS = {
|
|
|
1042
1043
|
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
1043
1044
|
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
1044
1045
|
],
|
|
1046
|
+
pi: [
|
|
1047
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro" },
|
|
1048
|
+
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash" },
|
|
1049
|
+
{ id: "kimi-coding/kimi-for-coding", label: "Kimi for Coding" },
|
|
1050
|
+
{ id: "zai/glm-5.1", label: "GLM-5.1" },
|
|
1051
|
+
{ id: "zai/glm-4.7", label: "GLM-4.7" }
|
|
1052
|
+
],
|
|
1045
1053
|
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
1046
1054
|
// built-in option is to defer to whatever default model the CLI already uses.
|
|
1047
1055
|
kimi: [
|
|
@@ -1190,10 +1198,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1190
1198
|
};
|
|
1191
1199
|
|
|
1192
1200
|
// src/agentProcessManager.ts
|
|
1193
|
-
import { mkdirSync as
|
|
1201
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync8 } from "fs";
|
|
1194
1202
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1195
1203
|
import { createHash as createHash3 } from "crypto";
|
|
1196
|
-
import
|
|
1204
|
+
import path13 from "path";
|
|
1197
1205
|
import os5 from "os";
|
|
1198
1206
|
|
|
1199
1207
|
// src/drivers/claude.ts
|
|
@@ -1778,6 +1786,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1778
1786
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1779
1787
|
}
|
|
1780
1788
|
|
|
1789
|
+
// src/authEnv.ts
|
|
1790
|
+
var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
|
|
1791
|
+
var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
|
|
1792
|
+
function scrubDaemonAuthEnv(env) {
|
|
1793
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1794
|
+
return env;
|
|
1795
|
+
}
|
|
1796
|
+
function scrubDaemonChildEnv(env) {
|
|
1797
|
+
delete env[DAEMON_API_KEY_ENV];
|
|
1798
|
+
delete env[SLOCK_AGENT_TOKEN_ENV];
|
|
1799
|
+
return env;
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1781
1802
|
// src/agentCredentialProxy.ts
|
|
1782
1803
|
import { randomBytes } from "crypto";
|
|
1783
1804
|
import http from "http";
|
|
@@ -2172,7 +2193,7 @@ async function handleProxyRequest(req, res) {
|
|
|
2172
2193
|
const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
|
|
2173
2194
|
new Uint8Array(bodyBuffer).set(rawBodyBuffer);
|
|
2174
2195
|
body = bodyBuffer;
|
|
2175
|
-
headers.
|
|
2196
|
+
headers.delete("content-length");
|
|
2176
2197
|
}
|
|
2177
2198
|
let sendTarget;
|
|
2178
2199
|
const sideEffectAction = agentApiSideEffectAction(target.pathname);
|
|
@@ -2196,7 +2217,7 @@ async function handleProxyRequest(req, res) {
|
|
|
2196
2217
|
body = prepared.bodyText;
|
|
2197
2218
|
if (sideEffectAction === "send") sendTarget = prepared.target;
|
|
2198
2219
|
headers.set("content-type", "application/json");
|
|
2199
|
-
headers.
|
|
2220
|
+
headers.delete("content-length");
|
|
2200
2221
|
}
|
|
2201
2222
|
const upstream = await daemonFetch(target, {
|
|
2202
2223
|
method,
|
|
@@ -2385,6 +2406,20 @@ function maxMessageSeq(messages) {
|
|
|
2385
2406
|
}
|
|
2386
2407
|
return maxSeq > 0 ? maxSeq : void 0;
|
|
2387
2408
|
}
|
|
2409
|
+
function messageSenderId(message) {
|
|
2410
|
+
if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
|
|
2411
|
+
if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
|
|
2412
|
+
return void 0;
|
|
2413
|
+
}
|
|
2414
|
+
function isSelfAuthoredMessage(registration, message) {
|
|
2415
|
+
return messageSenderId(message) === registration.agentId;
|
|
2416
|
+
}
|
|
2417
|
+
function isMessageModelSeen(coordinator, target, message) {
|
|
2418
|
+
const seq = Math.floor(messageSeq(message));
|
|
2419
|
+
const boundary = coordinator.getBoundary(target);
|
|
2420
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
|
|
2421
|
+
return coordinator.isMessageModelSeen?.({ target, message }) === true;
|
|
2422
|
+
}
|
|
2388
2423
|
function resolveFreshnessBoundary(messages) {
|
|
2389
2424
|
const seenUpToSeq = maxMessageSeq(messages);
|
|
2390
2425
|
return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
|
|
@@ -2525,7 +2560,7 @@ function parseTargetFields(target) {
|
|
|
2525
2560
|
}
|
|
2526
2561
|
function normalizeVisibleMessage(message, target) {
|
|
2527
2562
|
const targetFields = target ? parseTargetFields(target) : {};
|
|
2528
|
-
|
|
2563
|
+
const normalized = {
|
|
2529
2564
|
...targetFields,
|
|
2530
2565
|
...message,
|
|
2531
2566
|
message_id: message.message_id ?? message.id,
|
|
@@ -2534,6 +2569,9 @@ function normalizeVisibleMessage(message, target) {
|
|
|
2534
2569
|
sender_name: message.sender_name ?? message.senderName,
|
|
2535
2570
|
sender_description: message.sender_description ?? message.senderDescription ?? null
|
|
2536
2571
|
};
|
|
2572
|
+
const senderId = messageSenderId(message);
|
|
2573
|
+
if (senderId) normalized.sender_id = senderId;
|
|
2574
|
+
return normalized;
|
|
2537
2575
|
}
|
|
2538
2576
|
function normalizeVisibleMessages(messages, target) {
|
|
2539
2577
|
return messages.map((message) => normalizeVisibleMessage(message, target));
|
|
@@ -2634,6 +2672,9 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2634
2672
|
}
|
|
2635
2673
|
const recent = await loadRecentTargetMessages(registration, headers, target);
|
|
2636
2674
|
if (recent.length > 0) {
|
|
2675
|
+
const unconsumedCounterparty = recent.filter(
|
|
2676
|
+
(message) => !isSelfAuthoredMessage(registration, message) && !isMessageModelSeen(coordinator, target, message)
|
|
2677
|
+
);
|
|
2637
2678
|
const boundary2 = resolveFreshnessBoundary(recent);
|
|
2638
2679
|
if (!boundary2.ok) {
|
|
2639
2680
|
recordFreshnessDecision(coordinator, {
|
|
@@ -2647,6 +2688,39 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2647
2688
|
});
|
|
2648
2689
|
return { bodyText: JSON.stringify(body), target };
|
|
2649
2690
|
}
|
|
2691
|
+
if (unconsumedCounterparty.length === 0) {
|
|
2692
|
+
const { seenUpToSeq: seenUpToSeq2 } = boundary2;
|
|
2693
|
+
if (action === "send") body.seenUpToSeq = seenUpToSeq2;
|
|
2694
|
+
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq2, source: "side_effect_preflight_context" });
|
|
2695
|
+
recordFreshnessDecision(coordinator, {
|
|
2696
|
+
action,
|
|
2697
|
+
decision: "forward",
|
|
2698
|
+
target,
|
|
2699
|
+
inboxTrustState: "untrusted",
|
|
2700
|
+
reason: "target_first_touch_recent_context_already_seen",
|
|
2701
|
+
pendingCount: 0,
|
|
2702
|
+
pendingMaxSeq: seenUpToSeq2,
|
|
2703
|
+
modelSeenSeq: seenUpToSeq2,
|
|
2704
|
+
heldMessageCount: 0,
|
|
2705
|
+
omittedMessageCount: 0
|
|
2706
|
+
});
|
|
2707
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2708
|
+
}
|
|
2709
|
+
const heldBoundary = resolveFreshnessBoundary(unconsumedCounterparty);
|
|
2710
|
+
if (!heldBoundary.ok) {
|
|
2711
|
+
recordFreshnessDecision(coordinator, {
|
|
2712
|
+
action,
|
|
2713
|
+
decision: "forward",
|
|
2714
|
+
target,
|
|
2715
|
+
inboxTrustState: "untrusted",
|
|
2716
|
+
reason: "target_first_touch_counterparty_context_without_seq_boundary",
|
|
2717
|
+
pendingCount: 0,
|
|
2718
|
+
modelSeenSeq: 0
|
|
2719
|
+
});
|
|
2720
|
+
return { bodyText: JSON.stringify(body), target };
|
|
2721
|
+
}
|
|
2722
|
+
const heldMessages = latestVisibleMessages(unconsumedCounterparty, LOCAL_HELD_CONTEXT_LIMIT);
|
|
2723
|
+
const omittedMessageCount = Math.max(0, unconsumedCounterparty.length - heldMessages.length);
|
|
2650
2724
|
const { seenUpToSeq } = boundary2;
|
|
2651
2725
|
coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "side_effect_preflight_context" });
|
|
2652
2726
|
const decision = {
|
|
@@ -2656,10 +2730,10 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2656
2730
|
inboxTrustState: "untrusted",
|
|
2657
2731
|
reason: "target_first_touch_recent_context",
|
|
2658
2732
|
pendingCount: 0,
|
|
2659
|
-
pendingMaxSeq: seenUpToSeq,
|
|
2733
|
+
pendingMaxSeq: heldBoundary.seenUpToSeq,
|
|
2660
2734
|
modelSeenSeq: 0,
|
|
2661
|
-
heldMessageCount:
|
|
2662
|
-
omittedMessageCount
|
|
2735
|
+
heldMessageCount: heldMessages.length,
|
|
2736
|
+
omittedMessageCount
|
|
2663
2737
|
};
|
|
2664
2738
|
const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
|
|
2665
2739
|
recordFreshnessDecision(coordinator, { ...decision, producerFactId });
|
|
@@ -2669,9 +2743,9 @@ async function prepareAgentApiSideEffectForward(registration, headers, rawBody,
|
|
|
2669
2743
|
localResponse: projectApmHeldFreshnessEnvelope({
|
|
2670
2744
|
producerFactId,
|
|
2671
2745
|
action,
|
|
2672
|
-
heldMessages
|
|
2673
|
-
newMessageCount:
|
|
2674
|
-
omittedMessageCount
|
|
2746
|
+
heldMessages,
|
|
2747
|
+
newMessageCount: unconsumedCounterparty.length,
|
|
2748
|
+
omittedMessageCount,
|
|
2675
2749
|
seenUpToSeq
|
|
2676
2750
|
}).body
|
|
2677
2751
|
};
|
|
@@ -2777,7 +2851,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
2777
2851
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
2778
2852
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
2779
2853
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
2780
|
-
"
|
|
2854
|
+
"SLOCK_AGENT_TOKEN",
|
|
2855
|
+
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2856
|
+
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
2781
2857
|
];
|
|
2782
2858
|
var cachedOpencliBinPath;
|
|
2783
2859
|
function resolveOpencliBinPath() {
|
|
@@ -2992,7 +3068,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
2992
3068
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
2993
3069
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
2994
3070
|
};
|
|
2995
|
-
|
|
3071
|
+
scrubDaemonChildEnv(spawnEnv);
|
|
2996
3072
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
2997
3073
|
delete spawnEnv[key];
|
|
2998
3074
|
}
|
|
@@ -3026,7 +3102,7 @@ function collectResultErrorDetail(message, fallback) {
|
|
|
3026
3102
|
return parts.join(" | ") || fallback;
|
|
3027
3103
|
}
|
|
3028
3104
|
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));
|
|
3105
|
+
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));
|
|
3030
3106
|
}
|
|
3031
3107
|
var ClaudeEventNormalizer = class {
|
|
3032
3108
|
normalizeLine(line) {
|
|
@@ -3291,7 +3367,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
3291
3367
|
}
|
|
3292
3368
|
function resolveCommandOnPath(command, deps = {}) {
|
|
3293
3369
|
const platform = deps.platform ?? process.platform;
|
|
3294
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3370
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3295
3371
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3296
3372
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
3297
3373
|
if (platform === "win32") {
|
|
@@ -3317,7 +3393,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
3317
3393
|
return null;
|
|
3318
3394
|
}
|
|
3319
3395
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
3320
|
-
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3396
|
+
const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
|
|
3321
3397
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3322
3398
|
try {
|
|
3323
3399
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -3388,7 +3464,7 @@ function buildClaudeArgs(config, opts) {
|
|
|
3388
3464
|
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3389
3465
|
}
|
|
3390
3466
|
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3391
|
-
args.push("--
|
|
3467
|
+
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
3392
3468
|
}
|
|
3393
3469
|
if (config.sessionId) {
|
|
3394
3470
|
args.push("--resume", config.sessionId);
|
|
@@ -4052,6 +4128,7 @@ var CodexDriver = class {
|
|
|
4052
4128
|
cwd: ctx.workingDirectory,
|
|
4053
4129
|
approvalPolicy: "never",
|
|
4054
4130
|
sandbox: "danger-full-access",
|
|
4131
|
+
sandbox_mode: "danger-full-access",
|
|
4055
4132
|
developerInstructions: ctx.standingPrompt,
|
|
4056
4133
|
// Raw response items are used only as payload-free liveness signals in
|
|
4057
4134
|
// the daemon. They replace the previous transcript-mtime heuristic.
|
|
@@ -4748,11 +4825,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
4748
4825
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
4749
4826
|
}
|
|
4750
4827
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
4751
|
-
return withWindowsUserEnvironment({
|
|
4828
|
+
return scrubDaemonChildEnv(withWindowsUserEnvironment({
|
|
4752
4829
|
...deps.env ?? process.env,
|
|
4753
4830
|
FORCE_COLOR: "0",
|
|
4754
4831
|
NO_COLOR: "1"
|
|
4755
|
-
}, deps);
|
|
4832
|
+
}, deps));
|
|
4756
4833
|
}
|
|
4757
4834
|
function runCursorModelsCommand() {
|
|
4758
4835
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -4808,7 +4885,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4808
4885
|
}
|
|
4809
4886
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4810
4887
|
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4811
|
-
const env = deps.env ?? process.env;
|
|
4888
|
+
const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
|
|
4812
4889
|
const winPath = path8.win32;
|
|
4813
4890
|
let geminiEntry = null;
|
|
4814
4891
|
try {
|
|
@@ -4980,13 +5057,16 @@ var GeminiDriver = class {
|
|
|
4980
5057
|
// src/drivers/kimi.ts
|
|
4981
5058
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
4982
5059
|
import { spawn as spawn7 } from "child_process";
|
|
4983
|
-
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
5060
|
+
import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
4984
5061
|
import os3 from "os";
|
|
4985
5062
|
import path9 from "path";
|
|
4986
5063
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
4987
5064
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
4988
5065
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
4989
5066
|
var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
|
|
5067
|
+
var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
|
|
5068
|
+
var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
|
|
5069
|
+
var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
|
|
4990
5070
|
function parseToolArguments(raw) {
|
|
4991
5071
|
if (typeof raw !== "string") return raw;
|
|
4992
5072
|
try {
|
|
@@ -4995,6 +5075,73 @@ function parseToolArguments(raw) {
|
|
|
4995
5075
|
return raw;
|
|
4996
5076
|
}
|
|
4997
5077
|
}
|
|
5078
|
+
function readKimiConfigSource(home = os3.homedir(), env = process.env) {
|
|
5079
|
+
const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5080
|
+
if (inlineConfig && inlineConfig.trim()) {
|
|
5081
|
+
return {
|
|
5082
|
+
raw: inlineConfig,
|
|
5083
|
+
explicitPath: null,
|
|
5084
|
+
sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
|
|
5085
|
+
};
|
|
5086
|
+
}
|
|
5087
|
+
const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5088
|
+
const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
|
|
5089
|
+
try {
|
|
5090
|
+
return {
|
|
5091
|
+
raw: readFileSync3(configPath, "utf8"),
|
|
5092
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5093
|
+
sourcePath: configPath
|
|
5094
|
+
};
|
|
5095
|
+
} catch {
|
|
5096
|
+
return {
|
|
5097
|
+
raw: null,
|
|
5098
|
+
explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
|
|
5099
|
+
sourcePath: configPath
|
|
5100
|
+
};
|
|
5101
|
+
}
|
|
5102
|
+
}
|
|
5103
|
+
function buildKimiSpawnEnv(env = process.env) {
|
|
5104
|
+
const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
|
|
5105
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
|
|
5106
|
+
delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
|
|
5107
|
+
return scrubDaemonChildEnv(spawnEnv);
|
|
5108
|
+
}
|
|
5109
|
+
function buildKimiEffectiveEnv(ctx, overrideEnv) {
|
|
5110
|
+
return {
|
|
5111
|
+
...process.env,
|
|
5112
|
+
...ctx.config.envVars || {},
|
|
5113
|
+
...overrideEnv || {}
|
|
5114
|
+
};
|
|
5115
|
+
}
|
|
5116
|
+
function buildKimiLaunchOptions(ctx, opts = {}) {
|
|
5117
|
+
const env = buildKimiEffectiveEnv(ctx, opts.env);
|
|
5118
|
+
const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
|
|
5119
|
+
const args = [];
|
|
5120
|
+
let configFilePath = null;
|
|
5121
|
+
let configContent = null;
|
|
5122
|
+
if (source.explicitPath) {
|
|
5123
|
+
configFilePath = source.explicitPath;
|
|
5124
|
+
} else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
|
|
5125
|
+
configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
|
|
5126
|
+
configContent = source.raw;
|
|
5127
|
+
if (opts.writeGeneratedConfig !== false) {
|
|
5128
|
+
writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
|
|
5129
|
+
chmodSync(configFilePath, 384);
|
|
5130
|
+
}
|
|
5131
|
+
}
|
|
5132
|
+
if (configFilePath) {
|
|
5133
|
+
args.push("--config-file", configFilePath);
|
|
5134
|
+
}
|
|
5135
|
+
if (ctx.config.model && ctx.config.model !== "default") {
|
|
5136
|
+
args.push("--model", ctx.config.model);
|
|
5137
|
+
}
|
|
5138
|
+
return {
|
|
5139
|
+
args,
|
|
5140
|
+
env: buildKimiSpawnEnv(env),
|
|
5141
|
+
configFilePath,
|
|
5142
|
+
configContent
|
|
5143
|
+
};
|
|
5144
|
+
}
|
|
4998
5145
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
4999
5146
|
return {
|
|
5000
5147
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5018,7 +5165,25 @@ var KimiDriver = class {
|
|
|
5018
5165
|
};
|
|
5019
5166
|
model = {
|
|
5020
5167
|
detectedModelsVerifiedAs: "launchable",
|
|
5021
|
-
toLaunchSpec: (modelId) =>
|
|
5168
|
+
toLaunchSpec: (modelId, ctx, opts) => {
|
|
5169
|
+
if (!ctx) return { args: ["--model", modelId] };
|
|
5170
|
+
const launchCtx = {
|
|
5171
|
+
...ctx,
|
|
5172
|
+
config: {
|
|
5173
|
+
...ctx.config,
|
|
5174
|
+
model: modelId
|
|
5175
|
+
}
|
|
5176
|
+
};
|
|
5177
|
+
const launch = buildKimiLaunchOptions(launchCtx, {
|
|
5178
|
+
home: opts?.home,
|
|
5179
|
+
writeGeneratedConfig: false
|
|
5180
|
+
});
|
|
5181
|
+
return {
|
|
5182
|
+
args: launch.args,
|
|
5183
|
+
env: launch.env,
|
|
5184
|
+
configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
|
|
5185
|
+
};
|
|
5186
|
+
}
|
|
5022
5187
|
};
|
|
5023
5188
|
supportsStdinNotification = true;
|
|
5024
5189
|
mcpToolPrefix = "";
|
|
@@ -5072,6 +5237,7 @@ var KimiDriver = class {
|
|
|
5072
5237
|
}
|
|
5073
5238
|
}
|
|
5074
5239
|
}), "utf8");
|
|
5240
|
+
const launch = buildKimiLaunchOptions(ctx);
|
|
5075
5241
|
const args = [
|
|
5076
5242
|
"--wire",
|
|
5077
5243
|
"--yolo",
|
|
@@ -5080,15 +5246,16 @@ var KimiDriver = class {
|
|
|
5080
5246
|
"--mcp-config-file",
|
|
5081
5247
|
mcpConfigPath,
|
|
5082
5248
|
"--session",
|
|
5083
|
-
this.sessionId
|
|
5249
|
+
this.sessionId,
|
|
5250
|
+
...launch.args
|
|
5084
5251
|
];
|
|
5085
5252
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5086
5253
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5087
5254
|
args.push("--model", launchRuntimeFields.model);
|
|
5088
5255
|
}
|
|
5089
5256
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5090
|
-
const
|
|
5091
|
-
const proc = spawn7(
|
|
5257
|
+
const spawnTarget = resolveKimiSpawn(args);
|
|
5258
|
+
const proc = spawn7(spawnTarget.command, spawnTarget.args, {
|
|
5092
5259
|
cwd: ctx.workingDirectory,
|
|
5093
5260
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5094
5261
|
env: spawnEnv,
|
|
@@ -5096,7 +5263,7 @@ var KimiDriver = class {
|
|
|
5096
5263
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5097
5264
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5098
5265
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5099
|
-
shell:
|
|
5266
|
+
shell: spawnTarget.shell
|
|
5100
5267
|
});
|
|
5101
5268
|
proc.stdin?.write(JSON.stringify({
|
|
5102
5269
|
jsonrpc: "2.0",
|
|
@@ -5210,14 +5377,9 @@ var KimiDriver = class {
|
|
|
5210
5377
|
return detectKimiModels();
|
|
5211
5378
|
}
|
|
5212
5379
|
};
|
|
5213
|
-
function detectKimiModels(home = os3.homedir()) {
|
|
5214
|
-
const
|
|
5215
|
-
|
|
5216
|
-
try {
|
|
5217
|
-
raw = readFileSync3(configPath, "utf8");
|
|
5218
|
-
} catch {
|
|
5219
|
-
return null;
|
|
5220
|
-
}
|
|
5380
|
+
function detectKimiModels(home = os3.homedir(), opts = {}) {
|
|
5381
|
+
const raw = readKimiConfigSource(home, opts.env).raw;
|
|
5382
|
+
if (raw === null) return null;
|
|
5221
5383
|
const models = [];
|
|
5222
5384
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
5223
5385
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -5481,7 +5643,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
5481
5643
|
const platform = deps.platform ?? process.platform;
|
|
5482
5644
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
5483
5645
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
5484
|
-
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
5646
|
+
env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
|
|
5485
5647
|
encoding: "utf8",
|
|
5486
5648
|
timeout: 5e3,
|
|
5487
5649
|
shell: platform === "win32"
|
|
@@ -5740,6 +5902,297 @@ var OpenCodeDriver = class {
|
|
|
5740
5902
|
}
|
|
5741
5903
|
};
|
|
5742
5904
|
|
|
5905
|
+
// src/drivers/pi.ts
|
|
5906
|
+
import { spawn as spawn9 } from "child_process";
|
|
5907
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
|
|
5908
|
+
import path11 from "path";
|
|
5909
|
+
import { fileURLToPath } from "url";
|
|
5910
|
+
import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
|
|
5911
|
+
var CHAT_MCP_TOOL_PREFIX2 = "chat_";
|
|
5912
|
+
var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
|
|
5913
|
+
var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
|
|
5914
|
+
var MIN_SUPPORTED_PI_VERSION = "0.74.0";
|
|
5915
|
+
function parseSemver2(version) {
|
|
5916
|
+
const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
|
|
5917
|
+
if (!match) return null;
|
|
5918
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
5919
|
+
}
|
|
5920
|
+
function isSupportedPiVersion(version) {
|
|
5921
|
+
if (!version) return true;
|
|
5922
|
+
const actual = parseSemver2(version);
|
|
5923
|
+
const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
|
|
5924
|
+
if (!actual || !minimum) return true;
|
|
5925
|
+
for (let i = 0; i < 3; i += 1) {
|
|
5926
|
+
if (actual[i] > minimum[i]) return true;
|
|
5927
|
+
if (actual[i] < minimum[i]) return false;
|
|
5928
|
+
}
|
|
5929
|
+
return true;
|
|
5930
|
+
}
|
|
5931
|
+
function unsupportedPiVersionMessage(version) {
|
|
5932
|
+
if (!version || isSupportedPiVersion(version)) return null;
|
|
5933
|
+
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.`;
|
|
5934
|
+
}
|
|
5935
|
+
function probePi(version = PI_SDK_VERSION) {
|
|
5936
|
+
const unsupportedMessage = unsupportedPiVersionMessage(version);
|
|
5937
|
+
if (unsupportedMessage) {
|
|
5938
|
+
return {
|
|
5939
|
+
available: false,
|
|
5940
|
+
version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
|
|
5941
|
+
};
|
|
5942
|
+
}
|
|
5943
|
+
return { available: true, version };
|
|
5944
|
+
}
|
|
5945
|
+
function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
|
|
5946
|
+
const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
|
|
5947
|
+
const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
|
|
5948
|
+
if (existsSync8(sourceSibling)) return sourceSibling;
|
|
5949
|
+
const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
|
|
5950
|
+
if (existsSync8(bundledEntry)) return bundledEntry;
|
|
5951
|
+
return path11.join(moduleDir, "piSdkRunner.js");
|
|
5952
|
+
}
|
|
5953
|
+
function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
|
|
5954
|
+
if (runnerPath.endsWith(".ts")) {
|
|
5955
|
+
return [...process.execArgv, runnerPath];
|
|
5956
|
+
}
|
|
5957
|
+
return [runnerPath];
|
|
5958
|
+
}
|
|
5959
|
+
async function buildPiLaunchOptions(ctx, opts = {}) {
|
|
5960
|
+
const command = opts.command ?? process.execPath;
|
|
5961
|
+
const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
|
|
5962
|
+
const sessionDir = path11.join(piDir, "sessions");
|
|
5963
|
+
mkdirSync4(sessionDir, { recursive: true });
|
|
5964
|
+
const slock = await prepareCliTransport(ctx, { NO_COLOR: "1" });
|
|
5965
|
+
const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
|
|
5966
|
+
const agentDir = opts.agentDir ?? getAgentDir();
|
|
5967
|
+
const runnerConfigPath = path11.join(
|
|
5968
|
+
piDir,
|
|
5969
|
+
`sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
|
|
5970
|
+
);
|
|
5971
|
+
const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
|
|
5972
|
+
const runnerConfig = {
|
|
5973
|
+
cwd: ctx.workingDirectory,
|
|
5974
|
+
agentDir,
|
|
5975
|
+
sessionDir,
|
|
5976
|
+
sessionId: ctx.config.sessionId || null,
|
|
5977
|
+
standingPrompt: ctx.standingPrompt,
|
|
5978
|
+
prompt: turnPrompt,
|
|
5979
|
+
model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
|
|
5980
|
+
};
|
|
5981
|
+
writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
|
|
5982
|
+
`, { encoding: "utf8", mode: 384 });
|
|
5983
|
+
const args = [
|
|
5984
|
+
...buildPiSdkNodeArgs(runnerPath),
|
|
5985
|
+
"--config",
|
|
5986
|
+
runnerConfigPath
|
|
5987
|
+
];
|
|
5988
|
+
return {
|
|
5989
|
+
command,
|
|
5990
|
+
args,
|
|
5991
|
+
env: slock.spawnEnv,
|
|
5992
|
+
sessionDir,
|
|
5993
|
+
agentDir,
|
|
5994
|
+
runnerConfigPath,
|
|
5995
|
+
sdkVersion: PI_SDK_VERSION
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
function isSystemFirstMessageTask2(message) {
|
|
5999
|
+
return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
|
|
6000
|
+
}
|
|
6001
|
+
function buildPiSystemPrompt(config) {
|
|
6002
|
+
return buildCliTransportSystemPrompt(config, {
|
|
6003
|
+
toolPrefix: CHAT_MCP_TOOL_PREFIX2,
|
|
6004
|
+
extraCriticalRules: [
|
|
6005
|
+
"- 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."
|
|
6006
|
+
],
|
|
6007
|
+
postStartupNotes: [
|
|
6008
|
+
"**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."
|
|
6009
|
+
],
|
|
6010
|
+
includeStdinNotificationSection: false,
|
|
6011
|
+
messageNotificationStyle: "poll"
|
|
6012
|
+
});
|
|
6013
|
+
}
|
|
6014
|
+
function contentText(content) {
|
|
6015
|
+
if (!content) return "";
|
|
6016
|
+
const chunks = [];
|
|
6017
|
+
for (const item of content) {
|
|
6018
|
+
if (item.type === "text" && typeof item.text === "string") {
|
|
6019
|
+
chunks.push(item.text);
|
|
6020
|
+
}
|
|
6021
|
+
}
|
|
6022
|
+
return chunks.join("");
|
|
6023
|
+
}
|
|
6024
|
+
function apiKeyErrorMessage(line) {
|
|
6025
|
+
const trimmed = line.trim();
|
|
6026
|
+
if (!trimmed) return null;
|
|
6027
|
+
if (/no api key found/i.test(trimmed)) return trimmed;
|
|
6028
|
+
if (/api key.+required/i.test(trimmed)) return trimmed;
|
|
6029
|
+
if (/no models available/i.test(trimmed)) return trimmed;
|
|
6030
|
+
return null;
|
|
6031
|
+
}
|
|
6032
|
+
var PiDriver = class {
|
|
6033
|
+
id = "pi";
|
|
6034
|
+
lifecycle = {
|
|
6035
|
+
kind: "per_turn",
|
|
6036
|
+
start: "defer_until_concrete_message",
|
|
6037
|
+
exit: "terminate_on_turn_end",
|
|
6038
|
+
inFlightWake: "coalesce_into_pending"
|
|
6039
|
+
};
|
|
6040
|
+
communication = {
|
|
6041
|
+
chat: "slock_cli",
|
|
6042
|
+
runtimeControl: "none"
|
|
6043
|
+
};
|
|
6044
|
+
session = {
|
|
6045
|
+
recovery: "resume_or_fresh"
|
|
6046
|
+
};
|
|
6047
|
+
model = {
|
|
6048
|
+
detectedModelsVerifiedAs: "launchable",
|
|
6049
|
+
toLaunchSpec: async (modelId, ctx) => {
|
|
6050
|
+
if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
|
|
6051
|
+
const launchCtx = {
|
|
6052
|
+
...ctx,
|
|
6053
|
+
config: {
|
|
6054
|
+
...ctx.config,
|
|
6055
|
+
model: modelId
|
|
6056
|
+
}
|
|
6057
|
+
};
|
|
6058
|
+
const launch = await buildPiLaunchOptions(launchCtx);
|
|
6059
|
+
return {
|
|
6060
|
+
args: launch.args,
|
|
6061
|
+
env: launch.env,
|
|
6062
|
+
configFiles: [launch.runnerConfigPath],
|
|
6063
|
+
params: {
|
|
6064
|
+
agentDir: launch.agentDir,
|
|
6065
|
+
sessionDir: launch.sessionDir,
|
|
6066
|
+
sdkVersion: launch.sdkVersion,
|
|
6067
|
+
resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
|
|
6068
|
+
}
|
|
6069
|
+
};
|
|
6070
|
+
}
|
|
6071
|
+
};
|
|
6072
|
+
supportsStdinNotification = false;
|
|
6073
|
+
mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
|
|
6074
|
+
busyDeliveryMode = "none";
|
|
6075
|
+
terminateProcessOnTurnEnd = true;
|
|
6076
|
+
deferSpawnUntilMessage = true;
|
|
6077
|
+
usesSlockCliForCommunication = true;
|
|
6078
|
+
sessionId = null;
|
|
6079
|
+
sessionAnnounced = false;
|
|
6080
|
+
apiKeyErrorAnnounced = false;
|
|
6081
|
+
turnEnded = false;
|
|
6082
|
+
assistantTextByMessageId = /* @__PURE__ */ new Map();
|
|
6083
|
+
shouldDeferWakeMessage(message) {
|
|
6084
|
+
return isSystemFirstMessageTask2(message);
|
|
6085
|
+
}
|
|
6086
|
+
probe() {
|
|
6087
|
+
return probePi();
|
|
6088
|
+
}
|
|
6089
|
+
async detectModels() {
|
|
6090
|
+
return null;
|
|
6091
|
+
}
|
|
6092
|
+
async spawn(ctx) {
|
|
6093
|
+
this.sessionId = ctx.config.sessionId || null;
|
|
6094
|
+
this.sessionAnnounced = false;
|
|
6095
|
+
this.apiKeyErrorAnnounced = false;
|
|
6096
|
+
this.turnEnded = false;
|
|
6097
|
+
this.assistantTextByMessageId.clear();
|
|
6098
|
+
const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
|
|
6099
|
+
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
6100
|
+
const launch = await buildPiLaunchOptions(ctx);
|
|
6101
|
+
const proc = spawn9(launch.command, launch.args, {
|
|
6102
|
+
cwd: ctx.workingDirectory,
|
|
6103
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6104
|
+
env: launch.env,
|
|
6105
|
+
shell: false
|
|
6106
|
+
});
|
|
6107
|
+
proc.stdin?.end();
|
|
6108
|
+
return { process: proc };
|
|
6109
|
+
}
|
|
6110
|
+
parseLine(line) {
|
|
6111
|
+
let event;
|
|
6112
|
+
try {
|
|
6113
|
+
event = JSON.parse(line);
|
|
6114
|
+
} catch {
|
|
6115
|
+
if (this.apiKeyErrorAnnounced) return [];
|
|
6116
|
+
const message = apiKeyErrorMessage(line);
|
|
6117
|
+
if (!message) return [];
|
|
6118
|
+
this.apiKeyErrorAnnounced = true;
|
|
6119
|
+
this.turnEnded = true;
|
|
6120
|
+
return [
|
|
6121
|
+
{ kind: "error", message },
|
|
6122
|
+
{ kind: "turn_end", sessionId: this.sessionId || void 0 }
|
|
6123
|
+
];
|
|
6124
|
+
}
|
|
6125
|
+
const events = [];
|
|
6126
|
+
if (event.type === "session" && event.id) {
|
|
6127
|
+
this.sessionId = event.id;
|
|
6128
|
+
}
|
|
6129
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
6130
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
6131
|
+
this.sessionAnnounced = true;
|
|
6132
|
+
}
|
|
6133
|
+
switch (event.type) {
|
|
6134
|
+
case "agent_start":
|
|
6135
|
+
case "turn_start":
|
|
6136
|
+
events.push({ kind: "thinking", text: "" });
|
|
6137
|
+
break;
|
|
6138
|
+
case "message_update":
|
|
6139
|
+
case "message_end":
|
|
6140
|
+
if (event.message?.role === "assistant") {
|
|
6141
|
+
const key = event.message.id || "current";
|
|
6142
|
+
const currentText = contentText(event.message.content);
|
|
6143
|
+
const previousText = this.assistantTextByMessageId.get(key) ?? "";
|
|
6144
|
+
if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
|
|
6145
|
+
events.push({ kind: "text", text: currentText.slice(previousText.length) });
|
|
6146
|
+
} else if (currentText && currentText !== previousText) {
|
|
6147
|
+
events.push({ kind: "text", text: currentText });
|
|
6148
|
+
}
|
|
6149
|
+
this.assistantTextByMessageId.set(key, currentText);
|
|
6150
|
+
if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
|
|
6151
|
+
events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
|
|
6152
|
+
}
|
|
6153
|
+
}
|
|
6154
|
+
break;
|
|
6155
|
+
case "tool_execution_start":
|
|
6156
|
+
events.push({
|
|
6157
|
+
kind: "tool_call",
|
|
6158
|
+
name: event.toolName || "unknown_tool",
|
|
6159
|
+
input: event.args
|
|
6160
|
+
});
|
|
6161
|
+
break;
|
|
6162
|
+
case "tool_execution_end":
|
|
6163
|
+
events.push({
|
|
6164
|
+
kind: "tool_output",
|
|
6165
|
+
name: event.toolName || "unknown_tool"
|
|
6166
|
+
});
|
|
6167
|
+
if (event.isError) {
|
|
6168
|
+
events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
|
|
6169
|
+
}
|
|
6170
|
+
break;
|
|
6171
|
+
case "compaction_start":
|
|
6172
|
+
events.push({ kind: "compaction_started" });
|
|
6173
|
+
break;
|
|
6174
|
+
case "compaction_end":
|
|
6175
|
+
events.push({ kind: "compaction_finished" });
|
|
6176
|
+
if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
|
|
6177
|
+
break;
|
|
6178
|
+
case "turn_end":
|
|
6179
|
+
case "agent_end":
|
|
6180
|
+
if (!this.turnEnded) {
|
|
6181
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
6182
|
+
this.turnEnded = true;
|
|
6183
|
+
}
|
|
6184
|
+
break;
|
|
6185
|
+
}
|
|
6186
|
+
return events;
|
|
6187
|
+
}
|
|
6188
|
+
encodeStdinMessage(_text, _sessionId, _opts) {
|
|
6189
|
+
return null;
|
|
6190
|
+
}
|
|
6191
|
+
buildSystemPrompt(config, _agentId) {
|
|
6192
|
+
return buildPiSystemPrompt(config);
|
|
6193
|
+
}
|
|
6194
|
+
};
|
|
6195
|
+
|
|
5743
6196
|
// src/drivers/index.ts
|
|
5744
6197
|
var driverFactories = {
|
|
5745
6198
|
claude: () => new ClaudeDriver(),
|
|
@@ -5749,7 +6202,8 @@ var driverFactories = {
|
|
|
5749
6202
|
cursor: () => new CursorDriver(),
|
|
5750
6203
|
gemini: () => new GeminiDriver(),
|
|
5751
6204
|
kimi: () => new KimiDriver(),
|
|
5752
|
-
opencode: () => new OpenCodeDriver()
|
|
6205
|
+
opencode: () => new OpenCodeDriver(),
|
|
6206
|
+
pi: () => new PiDriver()
|
|
5753
6207
|
};
|
|
5754
6208
|
function getDriver(runtimeId) {
|
|
5755
6209
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -5762,7 +6216,7 @@ function getDriver(runtimeId) {
|
|
|
5762
6216
|
|
|
5763
6217
|
// src/workspaces.ts
|
|
5764
6218
|
import { readdir, rm, stat } from "fs/promises";
|
|
5765
|
-
import
|
|
6219
|
+
import path12 from "path";
|
|
5766
6220
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
5767
6221
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
5768
6222
|
}
|
|
@@ -5770,7 +6224,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
5770
6224
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
5771
6225
|
return null;
|
|
5772
6226
|
}
|
|
5773
|
-
return
|
|
6227
|
+
return path12.join(dataDir, directoryName);
|
|
5774
6228
|
}
|
|
5775
6229
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
5776
6230
|
return {
|
|
@@ -5819,7 +6273,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
5819
6273
|
return summary;
|
|
5820
6274
|
}
|
|
5821
6275
|
const childSummaries = await Promise.all(
|
|
5822
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
6276
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
5823
6277
|
);
|
|
5824
6278
|
for (const childSummary of childSummaries) {
|
|
5825
6279
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -5838,7 +6292,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
5838
6292
|
if (!entry.isDirectory()) {
|
|
5839
6293
|
return null;
|
|
5840
6294
|
}
|
|
5841
|
-
const dirPath =
|
|
6295
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
5842
6296
|
try {
|
|
5843
6297
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
5844
6298
|
return {
|
|
@@ -6278,12 +6732,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
6278
6732
|
for (const entry of entries) {
|
|
6279
6733
|
if (++visited > maxEntries) return null;
|
|
6280
6734
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
6281
|
-
return
|
|
6735
|
+
return path13.join(dir, entry.name);
|
|
6282
6736
|
}
|
|
6283
6737
|
for (const entry of entries) {
|
|
6284
6738
|
if (++visited > maxEntries) return null;
|
|
6285
6739
|
if (!entry.isDirectory()) continue;
|
|
6286
|
-
const found = visit(
|
|
6740
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
6287
6741
|
if (found) return found;
|
|
6288
6742
|
}
|
|
6289
6743
|
return null;
|
|
@@ -6296,10 +6750,10 @@ function safeSessionFilename(value) {
|
|
|
6296
6750
|
}
|
|
6297
6751
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
6298
6752
|
try {
|
|
6299
|
-
const dir =
|
|
6300
|
-
|
|
6301
|
-
const filePath =
|
|
6302
|
-
|
|
6753
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6754
|
+
mkdirSync5(dir, { recursive: true });
|
|
6755
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6756
|
+
writeFileSync8(filePath, JSON.stringify({
|
|
6303
6757
|
type: "runtime_session_handoff",
|
|
6304
6758
|
runtime,
|
|
6305
6759
|
sessionId,
|
|
@@ -6318,7 +6772,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
6318
6772
|
}
|
|
6319
6773
|
}
|
|
6320
6774
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6321
|
-
const directPath =
|
|
6775
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
6322
6776
|
if (directPath) {
|
|
6323
6777
|
try {
|
|
6324
6778
|
if (statSync(directPath).isFile()) {
|
|
@@ -6327,7 +6781,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
6327
6781
|
} catch {
|
|
6328
6782
|
}
|
|
6329
6783
|
}
|
|
6330
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6784
|
+
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
6785
|
if (!resolvedPath && fallbackDir) {
|
|
6332
6786
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
6333
6787
|
if (fallback) return fallback;
|
|
@@ -7280,6 +7734,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7280
7734
|
deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
|
|
7281
7735
|
processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
|
|
7282
7736
|
agentVisibleBoundaries = /* @__PURE__ */ new Map();
|
|
7737
|
+
agentVisibleMessageIds = /* @__PURE__ */ new Map();
|
|
7283
7738
|
constructor(chatBridgePath, sendToServer, daemonApiKey, opts) {
|
|
7284
7739
|
this.chatBridgePath = chatBridgePath;
|
|
7285
7740
|
this.slockCliPath = opts.slockCliPath ?? "";
|
|
@@ -7325,6 +7780,24 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7325
7780
|
getVisibleBoundary(agentId, target) {
|
|
7326
7781
|
return this.agentVisibleBoundaries.get(agentId)?.get(target);
|
|
7327
7782
|
}
|
|
7783
|
+
visibleMessageIdMap(agentId) {
|
|
7784
|
+
let map = this.agentVisibleMessageIds.get(agentId);
|
|
7785
|
+
if (!map) {
|
|
7786
|
+
map = /* @__PURE__ */ new Map();
|
|
7787
|
+
this.agentVisibleMessageIds.set(agentId, map);
|
|
7788
|
+
}
|
|
7789
|
+
return map;
|
|
7790
|
+
}
|
|
7791
|
+
getVisibleMessageIdSet(agentId, target) {
|
|
7792
|
+
return this.agentVisibleMessageIds.get(agentId)?.get(target);
|
|
7793
|
+
}
|
|
7794
|
+
isVisibleMessageModelSeen(agentId, target, message) {
|
|
7795
|
+
const seq = Number(message.seq ?? 0);
|
|
7796
|
+
const boundary = this.getVisibleBoundary(agentId, target);
|
|
7797
|
+
if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= Math.floor(seq)) return true;
|
|
7798
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : "";
|
|
7799
|
+
return id.length > 0 && this.getVisibleMessageIdSet(agentId, target)?.has(id) === true;
|
|
7800
|
+
}
|
|
7328
7801
|
scheduleStdinNotification(agentId, ap, delayMs) {
|
|
7329
7802
|
return ap.notifications.schedule(() => {
|
|
7330
7803
|
this.sendStdinNotification(agentId);
|
|
@@ -7359,13 +7832,14 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7359
7832
|
};
|
|
7360
7833
|
for (const message of input.messages) {
|
|
7361
7834
|
const seq = Number(message.seq ?? 0);
|
|
7362
|
-
if (!Number.isFinite(seq) || seq <= 0) continue;
|
|
7363
7835
|
const target = input.target ?? formatVisibleMessageTarget(message);
|
|
7364
7836
|
if (!target) continue;
|
|
7365
7837
|
const bucket = ensureBucket(target);
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7838
|
+
if (Number.isFinite(seq) && seq > 0) {
|
|
7839
|
+
bucket.maxSeq = Math.max(bucket.maxSeq, Math.floor(seq));
|
|
7840
|
+
bucket.seqs.add(Math.floor(seq));
|
|
7841
|
+
}
|
|
7842
|
+
const id = typeof message.message_id === "string" ? message.message_id : typeof message.id === "string" ? message.id : null;
|
|
7369
7843
|
if (id) bucket.ids.add(id);
|
|
7370
7844
|
}
|
|
7371
7845
|
if (input.target && typeof input.boundarySeq === "number" && Number.isFinite(input.boundarySeq) && input.boundarySeq > 0) {
|
|
@@ -7374,10 +7848,19 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7374
7848
|
}
|
|
7375
7849
|
if (byTarget.size === 0) return;
|
|
7376
7850
|
const boundaryMap = this.visibleBoundaryMap(agentId);
|
|
7851
|
+
const visibleIds = this.visibleMessageIdMap(agentId);
|
|
7377
7852
|
for (const [target, bucket] of byTarget) {
|
|
7378
7853
|
const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
|
|
7379
7854
|
const previous = boundaryMap.get(target) ?? 0;
|
|
7380
7855
|
boundaryMap.set(target, Math.max(previous, highWaterSeq));
|
|
7856
|
+
if (bucket.ids.size > 0) {
|
|
7857
|
+
let targetIds = visibleIds.get(target);
|
|
7858
|
+
if (!targetIds) {
|
|
7859
|
+
targetIds = /* @__PURE__ */ new Set();
|
|
7860
|
+
visibleIds.set(target, targetIds);
|
|
7861
|
+
}
|
|
7862
|
+
for (const id of bucket.ids) targetIds.add(id);
|
|
7863
|
+
}
|
|
7381
7864
|
}
|
|
7382
7865
|
const suppress = (messages) => {
|
|
7383
7866
|
if (!messages || messages.length === 0) return 0;
|
|
@@ -7410,6 +7893,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7410
7893
|
return {
|
|
7411
7894
|
getBoundary: (target) => this.getVisibleBoundary(agentId, target),
|
|
7412
7895
|
getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
|
|
7896
|
+
isMessageModelSeen: ({ target, message }) => this.isVisibleMessageModelSeen(agentId, target, message),
|
|
7413
7897
|
getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
|
|
7414
7898
|
consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
|
|
7415
7899
|
recordDrainOutcome: (input) => {
|
|
@@ -7721,7 +8205,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7721
8205
|
);
|
|
7722
8206
|
wakeMessage = void 0;
|
|
7723
8207
|
}
|
|
7724
|
-
const agentDataDir =
|
|
8208
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7725
8209
|
await mkdir(agentDataDir, { recursive: true });
|
|
7726
8210
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
7727
8211
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -7735,23 +8219,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7735
8219
|
);
|
|
7736
8220
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
7737
8221
|
}
|
|
7738
|
-
const memoryMdPath =
|
|
8222
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
7739
8223
|
try {
|
|
7740
8224
|
await access(memoryMdPath);
|
|
7741
8225
|
} catch {
|
|
7742
8226
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
7743
8227
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
7744
8228
|
}
|
|
7745
|
-
const notesDir =
|
|
8229
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
7746
8230
|
await mkdir(notesDir, { recursive: true });
|
|
7747
8231
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
7748
8232
|
const seedFiles = buildOnboardingSeedFiles();
|
|
7749
8233
|
for (const { relativePath, content } of seedFiles) {
|
|
7750
|
-
const fullPath =
|
|
8234
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
7751
8235
|
try {
|
|
7752
8236
|
await access(fullPath);
|
|
7753
8237
|
} catch {
|
|
7754
|
-
await mkdir(
|
|
8238
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
7755
8239
|
await writeFile(fullPath, content);
|
|
7756
8240
|
}
|
|
7757
8241
|
}
|
|
@@ -8623,7 +9107,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8623
9107
|
return true;
|
|
8624
9108
|
}
|
|
8625
9109
|
async resetWorkspace(agentId) {
|
|
8626
|
-
const agentDataDir =
|
|
9110
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
8627
9111
|
try {
|
|
8628
9112
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
8629
9113
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -8684,7 +9168,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8684
9168
|
return result;
|
|
8685
9169
|
}
|
|
8686
9170
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
8687
|
-
const workspacePath =
|
|
9171
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
8688
9172
|
return {
|
|
8689
9173
|
agentId,
|
|
8690
9174
|
launchId,
|
|
@@ -8941,7 +9425,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8941
9425
|
}
|
|
8942
9426
|
// Workspace file browsing
|
|
8943
9427
|
async getFileTree(agentId, dirPath) {
|
|
8944
|
-
const agentDir =
|
|
9428
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
8945
9429
|
try {
|
|
8946
9430
|
await stat2(agentDir);
|
|
8947
9431
|
} catch {
|
|
@@ -8949,8 +9433,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8949
9433
|
}
|
|
8950
9434
|
let targetDir = agentDir;
|
|
8951
9435
|
if (dirPath) {
|
|
8952
|
-
const resolved =
|
|
8953
|
-
if (!resolved.startsWith(agentDir +
|
|
9436
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
9437
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8954
9438
|
return [];
|
|
8955
9439
|
}
|
|
8956
9440
|
targetDir = resolved;
|
|
@@ -8958,14 +9442,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8958
9442
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
8959
9443
|
}
|
|
8960
9444
|
async readFile(agentId, filePath) {
|
|
8961
|
-
const agentDir =
|
|
8962
|
-
const resolved =
|
|
8963
|
-
if (!resolved.startsWith(agentDir +
|
|
9445
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
9446
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
9447
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
8964
9448
|
throw new Error("Access denied");
|
|
8965
9449
|
}
|
|
8966
9450
|
const info = await stat2(resolved);
|
|
8967
9451
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
8968
|
-
const ext =
|
|
9452
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
8969
9453
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
8970
9454
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
8971
9455
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -9000,13 +9484,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9000
9484
|
const agent = this.agents.get(agentId);
|
|
9001
9485
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
9002
9486
|
const home = os5.homedir();
|
|
9003
|
-
const workspaceDir =
|
|
9487
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
9004
9488
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
9005
9489
|
const globalResults = await Promise.all(
|
|
9006
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
9490
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
9007
9491
|
);
|
|
9008
9492
|
const workspaceResults = await Promise.all(
|
|
9009
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
9493
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
9010
9494
|
);
|
|
9011
9495
|
const dedup = (skills) => {
|
|
9012
9496
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9035,7 +9519,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9035
9519
|
const skills = [];
|
|
9036
9520
|
for (const entry of entries) {
|
|
9037
9521
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
9038
|
-
const skillMd =
|
|
9522
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
9039
9523
|
try {
|
|
9040
9524
|
const content = await readFile(skillMd, "utf-8");
|
|
9041
9525
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -9046,7 +9530,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9046
9530
|
} else if (entry.name.endsWith(".md")) {
|
|
9047
9531
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
9048
9532
|
try {
|
|
9049
|
-
const content = await readFile(
|
|
9533
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
9050
9534
|
const skill = this.parseSkillMd(cmdName, content);
|
|
9051
9535
|
skill.sourcePath = dir;
|
|
9052
9536
|
skills.push(skill);
|
|
@@ -9606,7 +10090,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9606
10090
|
}
|
|
9607
10091
|
}
|
|
9608
10092
|
isThinkingBlockMutationError(message) {
|
|
9609
|
-
return /thinking.*redacted_thinking|redacted_thinking.*thinking/i.test(message) && /cannot be modified/i.test(message);
|
|
10093
|
+
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
10094
|
}
|
|
9611
10095
|
markRuntimeProgressStaleIfNeeded(agentId, ap) {
|
|
9612
10096
|
if (ap.lastActivity !== "working" && ap.lastActivity !== "thinking") return false;
|
|
@@ -10164,8 +10648,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
10164
10648
|
const nodes = [];
|
|
10165
10649
|
for (const entry of entries) {
|
|
10166
10650
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
10167
|
-
const fullPath =
|
|
10168
|
-
const relativePath =
|
|
10651
|
+
const fullPath = path13.join(dir, entry.name);
|
|
10652
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
10169
10653
|
let info;
|
|
10170
10654
|
try {
|
|
10171
10655
|
info = await stat2(fullPath);
|
|
@@ -10470,9 +10954,9 @@ var ReminderCache = class {
|
|
|
10470
10954
|
|
|
10471
10955
|
// src/machineLock.ts
|
|
10472
10956
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "crypto";
|
|
10473
|
-
import { mkdirSync as
|
|
10957
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync9 } from "fs";
|
|
10474
10958
|
import os6 from "os";
|
|
10475
|
-
import
|
|
10959
|
+
import path14 from "path";
|
|
10476
10960
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
10477
10961
|
var DaemonMachineLockConflictError = class extends Error {
|
|
10478
10962
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -10494,7 +10978,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
10494
10978
|
return resolveSlockHomePath("machines");
|
|
10495
10979
|
}
|
|
10496
10980
|
function ownerPath(lockDir) {
|
|
10497
|
-
return
|
|
10981
|
+
return path14.join(lockDir, "owner.json");
|
|
10498
10982
|
}
|
|
10499
10983
|
function readOwner(lockDir) {
|
|
10500
10984
|
try {
|
|
@@ -10524,13 +11008,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
10524
11008
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
10525
11009
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
10526
11010
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
10527
|
-
const machineDir =
|
|
10528
|
-
const lockDir =
|
|
11011
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
11012
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
10529
11013
|
const token = randomUUID3();
|
|
10530
|
-
|
|
11014
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
10531
11015
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
10532
11016
|
try {
|
|
10533
|
-
|
|
11017
|
+
mkdirSync6(lockDir);
|
|
10534
11018
|
const owner = {
|
|
10535
11019
|
pid: process.pid,
|
|
10536
11020
|
token,
|
|
@@ -10540,7 +11024,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
10540
11024
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
10541
11025
|
};
|
|
10542
11026
|
try {
|
|
10543
|
-
|
|
11027
|
+
writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
10544
11028
|
`, { mode: 384 });
|
|
10545
11029
|
} catch (err) {
|
|
10546
11030
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -10577,8 +11061,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
10577
11061
|
}
|
|
10578
11062
|
|
|
10579
11063
|
// src/localTraceSink.ts
|
|
10580
|
-
import { appendFileSync, mkdirSync as
|
|
10581
|
-
import
|
|
11064
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync10 } from "fs";
|
|
11065
|
+
import path15 from "path";
|
|
10582
11066
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
10583
11067
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
10584
11068
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -10615,7 +11099,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10615
11099
|
currentSize = 0;
|
|
10616
11100
|
sequence = 0;
|
|
10617
11101
|
constructor(options) {
|
|
10618
|
-
this.traceDir =
|
|
11102
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
10619
11103
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
10620
11104
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
10621
11105
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -10641,15 +11125,15 @@ var LocalRotatingTraceSink = class {
|
|
|
10641
11125
|
return this.currentFile;
|
|
10642
11126
|
}
|
|
10643
11127
|
ensureFile(nextBytes) {
|
|
10644
|
-
|
|
11128
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
10645
11129
|
const nowMs = this.nowMsProvider();
|
|
10646
11130
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
10647
11131
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
10648
|
-
this.currentFile =
|
|
11132
|
+
this.currentFile = path15.join(
|
|
10649
11133
|
this.traceDir,
|
|
10650
11134
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
10651
11135
|
);
|
|
10652
|
-
|
|
11136
|
+
writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
|
|
10653
11137
|
this.currentSize = statSync3(this.currentFile).size;
|
|
10654
11138
|
this.currentFileOpenedAtMs = nowMs;
|
|
10655
11139
|
this.pruneOldFiles();
|
|
@@ -10660,7 +11144,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10660
11144
|
const excess = files.length - this.maxFiles;
|
|
10661
11145
|
if (excess <= 0) return;
|
|
10662
11146
|
for (const file of files.slice(0, excess)) {
|
|
10663
|
-
rmSync4(
|
|
11147
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
10664
11148
|
}
|
|
10665
11149
|
}
|
|
10666
11150
|
};
|
|
@@ -10751,11 +11235,11 @@ function isDiagnosticErrorAttr(key) {
|
|
|
10751
11235
|
import { createHash as createHash6, randomUUID as randomUUID4 } from "crypto";
|
|
10752
11236
|
import { gzipSync } from "zlib";
|
|
10753
11237
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
10754
|
-
import
|
|
11238
|
+
import path16 from "path";
|
|
10755
11239
|
|
|
10756
11240
|
// src/directUploadCapability.ts
|
|
10757
|
-
function joinUrl(base,
|
|
10758
|
-
return `${base.replace(/\/+$/, "")}${
|
|
11241
|
+
function joinUrl(base, path18) {
|
|
11242
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
10759
11243
|
}
|
|
10760
11244
|
function jsonHeaders(apiKey) {
|
|
10761
11245
|
return {
|
|
@@ -10974,7 +11458,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
10974
11458
|
}, nextMs);
|
|
10975
11459
|
}
|
|
10976
11460
|
async findUploadCandidates() {
|
|
10977
|
-
const traceDir =
|
|
11461
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
10978
11462
|
let names;
|
|
10979
11463
|
try {
|
|
10980
11464
|
names = await readdir3(traceDir);
|
|
@@ -10986,8 +11470,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
10986
11470
|
const currentFile = this.options.currentFileProvider?.();
|
|
10987
11471
|
const candidates = [];
|
|
10988
11472
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
10989
|
-
const file =
|
|
10990
|
-
if (currentFile &&
|
|
11473
|
+
const file = path16.join(traceDir, name);
|
|
11474
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
10991
11475
|
if (await this.isUploaded(file)) continue;
|
|
10992
11476
|
try {
|
|
10993
11477
|
const info = await stat3(file);
|
|
@@ -11061,8 +11545,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
11061
11545
|
}
|
|
11062
11546
|
}
|
|
11063
11547
|
uploadStatePath(file) {
|
|
11064
|
-
const stateDir =
|
|
11065
|
-
return
|
|
11548
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
11549
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
11066
11550
|
}
|
|
11067
11551
|
async isUploaded(file) {
|
|
11068
11552
|
try {
|
|
@@ -11074,9 +11558,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
11074
11558
|
}
|
|
11075
11559
|
async markUploaded(file, metadata) {
|
|
11076
11560
|
const stateFile = this.uploadStatePath(file);
|
|
11077
|
-
await mkdir2(
|
|
11561
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
11078
11562
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
11079
|
-
file:
|
|
11563
|
+
file: path16.basename(file),
|
|
11080
11564
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11081
11565
|
...metadata
|
|
11082
11566
|
}, null, 2)}
|
|
@@ -11098,7 +11582,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
11098
11582
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
11099
11583
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
11100
11584
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
11101
|
-
var DAEMON_CLI_USAGE =
|
|
11585
|
+
var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
|
|
11102
11586
|
var RunnerCredentialMintError2 = class extends Error {
|
|
11103
11587
|
code;
|
|
11104
11588
|
retryable;
|
|
@@ -11134,9 +11618,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
11134
11618
|
async function waitForRunnerCredentialRetry2() {
|
|
11135
11619
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
11136
11620
|
}
|
|
11137
|
-
function parseDaemonCliArgs(args) {
|
|
11621
|
+
function parseDaemonCliArgs(args, env = {}) {
|
|
11138
11622
|
let serverUrl = "";
|
|
11139
|
-
let apiKey = "";
|
|
11623
|
+
let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
|
|
11140
11624
|
for (let i = 0; i < args.length; i++) {
|
|
11141
11625
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
11142
11626
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -11153,23 +11637,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
11153
11637
|
}
|
|
11154
11638
|
}
|
|
11155
11639
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
11156
|
-
const dirname =
|
|
11157
|
-
const jsPath =
|
|
11640
|
+
const dirname = path17.dirname(fileURLToPath2(moduleUrl));
|
|
11641
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
11158
11642
|
try {
|
|
11159
11643
|
accessSync(jsPath);
|
|
11160
11644
|
return jsPath;
|
|
11161
11645
|
} catch {
|
|
11162
|
-
return
|
|
11646
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
11163
11647
|
}
|
|
11164
11648
|
}
|
|
11165
11649
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
11166
|
-
const thisDir =
|
|
11167
|
-
const bundledDistPath =
|
|
11650
|
+
const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
|
|
11651
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
11168
11652
|
try {
|
|
11169
11653
|
accessSync(bundledDistPath);
|
|
11170
11654
|
return bundledDistPath;
|
|
11171
11655
|
} catch {
|
|
11172
|
-
const workspaceDistPath =
|
|
11656
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
11173
11657
|
accessSync(workspaceDistPath);
|
|
11174
11658
|
return workspaceDistPath;
|
|
11175
11659
|
}
|
|
@@ -11299,6 +11783,11 @@ function summarizeIncomingMessage(msg) {
|
|
|
11299
11783
|
var DaemonCore = class {
|
|
11300
11784
|
options;
|
|
11301
11785
|
daemonVersion;
|
|
11786
|
+
// When this runner is launched by a managed Computer service, the
|
|
11787
|
+
// service exports SLOCK_COMPUTER_VERSION (the `@slock-ai/computer`
|
|
11788
|
+
// bundle version). Reported in `ready` so the server can surface the
|
|
11789
|
+
// Computer's own version (distinct from the underlying daemonVersion).
|
|
11790
|
+
computerVersion;
|
|
11302
11791
|
chatBridgePath;
|
|
11303
11792
|
slockCliPath;
|
|
11304
11793
|
slockHome;
|
|
@@ -11314,6 +11803,7 @@ var DaemonCore = class {
|
|
|
11314
11803
|
constructor(options) {
|
|
11315
11804
|
this.options = options;
|
|
11316
11805
|
this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
|
|
11806
|
+
this.computerVersion = (process.env.SLOCK_COMPUTER_VERSION || "").trim() || null;
|
|
11317
11807
|
this.chatBridgePath = options.chatBridgePath ?? resolveChatBridgePath();
|
|
11318
11808
|
this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
|
|
11319
11809
|
this.slockHome = resolveSlockHome();
|
|
@@ -11348,7 +11838,7 @@ var DaemonCore = class {
|
|
|
11348
11838
|
}
|
|
11349
11839
|
resolveMachineStateRoot() {
|
|
11350
11840
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
11351
|
-
if (this.options.dataDir) return
|
|
11841
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
11352
11842
|
return resolveDefaultMachineStateRoot();
|
|
11353
11843
|
}
|
|
11354
11844
|
shouldEnableLocalTrace() {
|
|
@@ -11374,7 +11864,7 @@ var DaemonCore = class {
|
|
|
11374
11864
|
sink: this.localTraceSink
|
|
11375
11865
|
});
|
|
11376
11866
|
this.agentManager.setTracer(this.tracer);
|
|
11377
|
-
this.agentManager.setCliTransportTraceDir(
|
|
11867
|
+
this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
|
|
11378
11868
|
}
|
|
11379
11869
|
installTraceBundleUploader(machineDir) {
|
|
11380
11870
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -11773,6 +12263,26 @@ var DaemonCore = class {
|
|
|
11773
12263
|
case "ping":
|
|
11774
12264
|
this.connection.send({ type: "pong" });
|
|
11775
12265
|
break;
|
|
12266
|
+
case "computer:restart":
|
|
12267
|
+
case "computer:upgrade": {
|
|
12268
|
+
const action = msg.type === "computer:restart" ? "restart" : "upgrade";
|
|
12269
|
+
this.recordDaemonTrace("daemon.computer_control.received", {
|
|
12270
|
+
action,
|
|
12271
|
+
handled: Boolean(this.options.onComputerControl)
|
|
12272
|
+
});
|
|
12273
|
+
if (this.options.onComputerControl) {
|
|
12274
|
+
try {
|
|
12275
|
+
this.options.onComputerControl(action);
|
|
12276
|
+
} catch (err) {
|
|
12277
|
+
logger.error(
|
|
12278
|
+
`[Daemon] computer:${action} control handler failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12279
|
+
);
|
|
12280
|
+
}
|
|
12281
|
+
} else {
|
|
12282
|
+
logger.info(`[Daemon] Ignoring computer:${action} \u2014 not launched by a Computer service.`);
|
|
12283
|
+
}
|
|
12284
|
+
break;
|
|
12285
|
+
}
|
|
11776
12286
|
}
|
|
11777
12287
|
}
|
|
11778
12288
|
onReminderFire(job) {
|
|
@@ -11799,7 +12309,8 @@ var DaemonCore = class {
|
|
|
11799
12309
|
runningAgents: runningAgentIds,
|
|
11800
12310
|
hostname: this.options.hostname ?? os7.hostname(),
|
|
11801
12311
|
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
11802
|
-
daemonVersion: this.daemonVersion
|
|
12312
|
+
daemonVersion: this.daemonVersion,
|
|
12313
|
+
...this.computerVersion ? { computerVersion: this.computerVersion } : {}
|
|
11803
12314
|
});
|
|
11804
12315
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
11805
12316
|
runtimes_count: runtimes.length,
|
|
@@ -11860,6 +12371,8 @@ var DaemonCore = class {
|
|
|
11860
12371
|
};
|
|
11861
12372
|
|
|
11862
12373
|
export {
|
|
12374
|
+
DAEMON_API_KEY_ENV,
|
|
12375
|
+
scrubDaemonAuthEnv,
|
|
11863
12376
|
resolveWorkspaceDirectoryPath,
|
|
11864
12377
|
scanWorkspaceDirectories,
|
|
11865
12378
|
deleteWorkspaceDirectory,
|