@slock-ai/daemon 0.55.3-play.20260531155023 → 0.55.4
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-3EAZBKTI.js → chunk-S5AEBH3V.js} +465 -416
- package/dist/cli/index.js +7 -5
- package/dist/cli/package.json +1 -1
- package/dist/core.js +2 -6
- package/dist/index.js +3 -5
- package/package.json +1 -2
- package/dist/drivers/piSdkRunner.js +0 -96
|
@@ -12,7 +12,7 @@ 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
|
|
15
|
+
import { fileURLToPath } 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,19 +990,24 @@ 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 },
|
|
994
993
|
{ id: "antigravity", displayName: "Antigravity CLI", binary: "agy", supported: true },
|
|
995
994
|
{ id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
|
|
996
995
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
997
996
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
998
997
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
999
|
-
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true }
|
|
998
|
+
{ id: "opencode", displayName: "OpenCode", binary: "opencode", supported: true },
|
|
999
|
+
{ id: "pi", displayName: "Pi CLI", binary: "pi", supported: true }
|
|
1000
1000
|
];
|
|
1001
1001
|
var RUNTIME_MODELS = {
|
|
1002
1002
|
claude: [
|
|
1003
|
-
{ id: "opus", label: "Opus" },
|
|
1004
|
-
{ id: "
|
|
1005
|
-
{ 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" }
|
|
1006
1011
|
],
|
|
1007
1012
|
codex: [
|
|
1008
1013
|
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
@@ -1044,11 +1049,9 @@ var RUNTIME_MODELS = {
|
|
|
1044
1049
|
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
1045
1050
|
],
|
|
1046
1051
|
pi: [
|
|
1047
|
-
{ id: "
|
|
1048
|
-
{ id: "deepseek/deepseek-v4-
|
|
1049
|
-
{ id: "
|
|
1050
|
-
{ id: "zai/glm-5.1", label: "GLM-5.1" },
|
|
1051
|
-
{ id: "zai/glm-4.7", label: "GLM-4.7" }
|
|
1052
|
+
{ id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
|
|
1053
|
+
{ id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (Pi)", verified: "suggestion_only" },
|
|
1054
|
+
{ id: "deepseek/deepseek-v4-flash", label: "DeepSeek V4 Flash (Pi)", verified: "suggestion_only" }
|
|
1052
1055
|
],
|
|
1053
1056
|
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
1054
1057
|
// built-in option is to defer to whatever default model the CLI already uses.
|
|
@@ -1094,22 +1097,28 @@ function isPresetRuntimeModel(runtime, model) {
|
|
|
1094
1097
|
function modelConfigFromLegacy(runtime, model) {
|
|
1095
1098
|
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
1096
1099
|
}
|
|
1097
|
-
function parseProviderConfig(runtime, value) {
|
|
1100
|
+
function parseProviderConfig(runtime, value, legacyApiUrl, legacyApiKey) {
|
|
1098
1101
|
if (runtime !== "claude") return void 0;
|
|
1099
1102
|
if (!isPlainRecord(value)) return { kind: "default" };
|
|
1100
1103
|
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
1101
1104
|
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
1102
1105
|
}
|
|
1106
|
+
if (value.kind === "custom" && legacyApiUrl?.trim() && legacyApiKey?.trim()) {
|
|
1107
|
+
return { kind: "custom", apiUrl: legacyApiUrl.trim(), apiKey: legacyApiKey.trim() };
|
|
1108
|
+
}
|
|
1103
1109
|
return { kind: "default" };
|
|
1104
1110
|
}
|
|
1105
|
-
function parseModelConfig(runtime, value, fallback) {
|
|
1111
|
+
function parseModelConfig(runtime, value, fallback, provider, legacyCustomModel) {
|
|
1106
1112
|
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
1107
1113
|
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
1108
1114
|
return { kind: "custom", name: value.name.trim() };
|
|
1109
1115
|
}
|
|
1110
|
-
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim()) {
|
|
1116
|
+
if (value.kind === "preset" && typeof value.id === "string" && value.id.trim() && isPresetRuntimeModel(runtime, value.id.trim())) {
|
|
1111
1117
|
return { kind: "preset", id: value.id.trim() };
|
|
1112
1118
|
}
|
|
1119
|
+
if (provider?.kind === "custom" && runtime === "claude" && legacyCustomModel?.trim()) {
|
|
1120
|
+
return { kind: "custom", name: legacyCustomModel.trim() };
|
|
1121
|
+
}
|
|
1113
1122
|
return modelConfigFromLegacy(runtime, fallback);
|
|
1114
1123
|
}
|
|
1115
1124
|
function parseModeConfig(value) {
|
|
@@ -1125,12 +1134,13 @@ function hydrateRuntimeConfig(input) {
|
|
|
1125
1134
|
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
1126
1135
|
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
1127
1136
|
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
1128
|
-
const
|
|
1137
|
+
const legacyClaudeCustomModel = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_CUSTOM_MODEL_OPTION : void 0;
|
|
1138
|
+
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;
|
|
1129
1139
|
return {
|
|
1130
1140
|
version: RUNTIME_CONFIG_VERSION,
|
|
1131
1141
|
runtime,
|
|
1132
1142
|
...provider ? { provider } : {},
|
|
1133
|
-
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1143
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel, provider, legacyClaudeCustomModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1134
1144
|
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
1135
1145
|
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
1136
1146
|
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
@@ -1198,7 +1208,7 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1198
1208
|
};
|
|
1199
1209
|
|
|
1200
1210
|
// src/agentProcessManager.ts
|
|
1201
|
-
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as
|
|
1211
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
1202
1212
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1203
1213
|
import { createHash as createHash3 } from "crypto";
|
|
1204
1214
|
import path13 from "path";
|
|
@@ -1382,19 +1392,19 @@ If Slock says a message was not sent and was saved as a draft, choose one path:
|
|
|
1382
1392
|
|
|
1383
1393
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1384
1394
|
|
|
1385
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1395
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1386
1396
|
- 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.
|
|
1387
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1397
|
+
- **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.
|
|
1388
1398
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1389
|
-
- You can read thread history: \`slock message read --channel "#general:
|
|
1390
|
-
- You can stop receiving ordinary delivery for a thread with \`slock thread unfollow --target "#general:
|
|
1399
|
+
- You can read thread history: \`slock message read --channel "#general:00000000"\`
|
|
1400
|
+
- 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.
|
|
1391
1401
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.` : `### Threads
|
|
1392
1402
|
|
|
1393
1403
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1394
1404
|
|
|
1395
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1405
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1396
1406
|
- 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.
|
|
1397
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1407
|
+
- **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.
|
|
1398
1408
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1399
1409
|
- You can read thread history via ${readCmd} with the same thread target.
|
|
1400
1410
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
|
|
@@ -1560,13 +1570,18 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
1560
1570
|
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
1561
1571
|
|
|
1562
1572
|
\`\`\`
|
|
1563
|
-
[target=#general msg=
|
|
1564
|
-
[target=#general msg=
|
|
1565
|
-
[target=dm:@richard msg=
|
|
1566
|
-
[target=#general:
|
|
1567
|
-
[target=dm:@richard:
|
|
1573
|
+
[target=#general msg=00000000 time=2026-03-15T01:00:00 type=human] @richard: hello everyone
|
|
1574
|
+
[target=#general msg=11111111 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
|
|
1575
|
+
[target=dm:@richard msg=22222222 time=2026-03-15T01:00:02 type=human] @richard: hey, can you help?
|
|
1576
|
+
[target=#general:00000000 msg=33333333 time=2026-03-15T01:00:03 type=human] @richard: thread reply
|
|
1577
|
+
[target=dm:@richard:22222222 msg=44444444 time=2026-03-15T01:00:04 type=human] @richard: DM thread reply
|
|
1568
1578
|
\`\`\`
|
|
1569
1579
|
|
|
1580
|
+
Prompt examples use obvious placeholder IDs such as \`00000000\`, \`11111111\`,
|
|
1581
|
+
and \`22222222\`. They show the shape of a real message ID but are not actual
|
|
1582
|
+
messages. Do not cite them as evidence; use only IDs from messages you actually
|
|
1583
|
+
received or read.
|
|
1584
|
+
|
|
1570
1585
|
Header fields:
|
|
1571
1586
|
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
1572
1587
|
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
@@ -1638,9 +1653,11 @@ Slock auto-renders these inline tokens as interactive links whenever they appear
|
|
|
1638
1653
|
|
|
1639
1654
|
Write them inline as plain words in your sentence \u2014 the same way you'd type any other word \u2014 and Slock turns them into clickable references.
|
|
1640
1655
|
|
|
1656
|
+
Markdown markup expresses presentation semantics; do not mix markup delimiters into literal payloads. Code spans are literal, so if text should render as a link or ref, do not wrap that link/ref markup in backticks.
|
|
1657
|
+
|
|
1641
1658
|
### Formatting \u2014 URLs in non-English text
|
|
1642
1659
|
|
|
1643
|
-
When writing a URL next to non-ASCII punctuation (Chinese, Japanese, etc.),
|
|
1660
|
+
When writing a URL next to non-ASCII punctuation (Chinese, Japanese, etc.) and the URL should render as a link, wrap the URL in angle brackets or use markdown link syntax. Otherwise the punctuation may be rendered as part of the URL.
|
|
1644
1661
|
|
|
1645
1662
|
- **Wrong**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1Ahttp://localhost:3000\uFF0C\u8BF7\u67E5\u770B\` (the \`\uFF0C\` gets swallowed into the link)
|
|
1646
1663
|
- **Correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A<http://localhost:3000>\uFF0C\u8BF7\u67E5\u770B\`
|
|
@@ -1720,12 +1737,12 @@ How to handle these:
|
|
|
1720
1737
|
- Call ${checkCmd} at the next safe breakpoint to materialize the pending messages before taking side-effect actions that depend on current context.
|
|
1721
1738
|
- If the new message is higher priority, pivot after reading it. If not, continue your current work.`;
|
|
1722
1739
|
} else {
|
|
1723
|
-
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.]\``;
|
|
1724
1741
|
prompt += `
|
|
1725
1742
|
|
|
1726
1743
|
## Message Notifications
|
|
1727
1744
|
|
|
1728
|
-
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:
|
|
1729
1746
|
|
|
1730
1747
|
${notifyExample}
|
|
1731
1748
|
|
|
@@ -1786,19 +1803,6 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
|
|
|
1786
1803
|
return candidates.filter((candidate) => existsSync(candidate.path));
|
|
1787
1804
|
}
|
|
1788
1805
|
|
|
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
|
-
|
|
1802
1806
|
// src/agentCredentialProxy.ts
|
|
1803
1807
|
import { randomBytes } from "crypto";
|
|
1804
1808
|
import http from "http";
|
|
@@ -2851,9 +2855,7 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
|
|
|
2851
2855
|
var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
|
|
2852
2856
|
var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
2853
2857
|
var RAW_CREDENTIAL_ENV_DENYLIST = [
|
|
2854
|
-
"
|
|
2855
|
-
"SLOCK_AGENT_CREDENTIAL_KEY",
|
|
2856
|
-
"SLOCK_AGENT_CREDENTIAL_KEY_FILE"
|
|
2858
|
+
"SLOCK_AGENT_CREDENTIAL_KEY"
|
|
2857
2859
|
];
|
|
2858
2860
|
var cachedOpencliBinPath;
|
|
2859
2861
|
function resolveOpencliBinPath() {
|
|
@@ -3068,7 +3070,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
|
|
|
3068
3070
|
...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
|
|
3069
3071
|
PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
|
|
3070
3072
|
};
|
|
3071
|
-
|
|
3073
|
+
delete spawnEnv.SLOCK_AGENT_TOKEN;
|
|
3072
3074
|
for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
|
|
3073
3075
|
delete spawnEnv[key];
|
|
3074
3076
|
}
|
|
@@ -3104,6 +3106,125 @@ function collectResultErrorDetail(message, fallback) {
|
|
|
3104
3106
|
function isProviderApiFailureText(value, hasToolUse) {
|
|
3105
3107
|
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));
|
|
3106
3108
|
}
|
|
3109
|
+
function finiteNumber(value) {
|
|
3110
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3111
|
+
}
|
|
3112
|
+
function finiteString(value) {
|
|
3113
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3114
|
+
}
|
|
3115
|
+
function withDefined(attrs) {
|
|
3116
|
+
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3117
|
+
}
|
|
3118
|
+
function collectNumericFields(value, fields) {
|
|
3119
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3120
|
+
const attrs = {};
|
|
3121
|
+
for (const [sourceKey, attrKey] of Object.entries(fields)) {
|
|
3122
|
+
const numberValue = finiteNumber(value[sourceKey]);
|
|
3123
|
+
if (numberValue !== void 0) attrs[attrKey] = numberValue;
|
|
3124
|
+
}
|
|
3125
|
+
return attrs;
|
|
3126
|
+
}
|
|
3127
|
+
function collectModelUsageAttrs(value) {
|
|
3128
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3129
|
+
const sanitized = {};
|
|
3130
|
+
const aggregate = {};
|
|
3131
|
+
const knownNumericFields = [
|
|
3132
|
+
"inputTokens",
|
|
3133
|
+
"outputTokens",
|
|
3134
|
+
"cacheReadInputTokens",
|
|
3135
|
+
"cacheCreationInputTokens",
|
|
3136
|
+
"webSearchRequests",
|
|
3137
|
+
"costUSD",
|
|
3138
|
+
"contextWindow",
|
|
3139
|
+
"maxOutputTokens"
|
|
3140
|
+
];
|
|
3141
|
+
for (const [modelName, rawUsage] of Object.entries(value)) {
|
|
3142
|
+
if (!rawUsage || typeof rawUsage !== "object" || Array.isArray(rawUsage)) continue;
|
|
3143
|
+
const modelUsage = {};
|
|
3144
|
+
for (const field of knownNumericFields) {
|
|
3145
|
+
const numberValue = finiteNumber(rawUsage[field]);
|
|
3146
|
+
if (numberValue === void 0) continue;
|
|
3147
|
+
modelUsage[field] = numberValue;
|
|
3148
|
+
const aggregateKey = field === "costUSD" ? "costUsd" : field;
|
|
3149
|
+
if (field === "contextWindow" || field === "maxOutputTokens") {
|
|
3150
|
+
aggregate[aggregateKey] = Math.max(aggregate[aggregateKey] ?? 0, numberValue);
|
|
3151
|
+
} else {
|
|
3152
|
+
aggregate[aggregateKey] = (aggregate[aggregateKey] ?? 0) + numberValue;
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
if (Object.keys(modelUsage).length > 0) sanitized[modelName] = modelUsage;
|
|
3156
|
+
}
|
|
3157
|
+
const modelNames = Object.keys(sanitized).sort();
|
|
3158
|
+
if (modelNames.length === 0) return {};
|
|
3159
|
+
const orderedSanitized = Object.fromEntries(modelNames.map((modelName) => [modelName, sanitized[modelName]]));
|
|
3160
|
+
return withDefined({
|
|
3161
|
+
modelUsageModelCount: modelNames.length,
|
|
3162
|
+
modelUsageModels: modelNames.join(","),
|
|
3163
|
+
modelUsageJson: JSON.stringify(orderedSanitized),
|
|
3164
|
+
modelUsageInputTokens: aggregate.inputTokens,
|
|
3165
|
+
modelUsageOutputTokens: aggregate.outputTokens,
|
|
3166
|
+
modelUsageCachedInputTokens: aggregate.cacheReadInputTokens,
|
|
3167
|
+
modelUsageCacheCreationInputTokens: aggregate.cacheCreationInputTokens,
|
|
3168
|
+
modelUsageWebSearchRequests: aggregate.webSearchRequests,
|
|
3169
|
+
modelUsageCostUsd: aggregate.costUsd,
|
|
3170
|
+
modelUsageMaxContextWindow: aggregate.contextWindow,
|
|
3171
|
+
modelUsageMaxOutputTokens: aggregate.maxOutputTokens
|
|
3172
|
+
});
|
|
3173
|
+
}
|
|
3174
|
+
function parseClaudeResultUsageTelemetry(event) {
|
|
3175
|
+
const usage = event.usage && typeof event.usage === "object" ? event.usage : void 0;
|
|
3176
|
+
const totalCostUsd = finiteNumber(event.total_cost_usd);
|
|
3177
|
+
const modelUsageAttrs = collectModelUsageAttrs(event.modelUsage);
|
|
3178
|
+
const hasTelemetrySource = usage !== void 0 || totalCostUsd !== void 0 || Object.keys(modelUsageAttrs).length > 0;
|
|
3179
|
+
if (!hasTelemetrySource) return null;
|
|
3180
|
+
const inputTokens = finiteNumber(usage?.input_tokens);
|
|
3181
|
+
const outputTokens = finiteNumber(usage?.output_tokens);
|
|
3182
|
+
const cachedInputTokens = finiteNumber(usage?.cache_read_input_tokens);
|
|
3183
|
+
const cacheCreationInputTokens = finiteNumber(usage?.cache_creation_input_tokens);
|
|
3184
|
+
const attrs = {
|
|
3185
|
+
...withDefined({
|
|
3186
|
+
totalCostUsd,
|
|
3187
|
+
durationMs: finiteNumber(event.duration_ms),
|
|
3188
|
+
durationApiMs: finiteNumber(event.duration_api_ms),
|
|
3189
|
+
numTurns: finiteNumber(event.num_turns),
|
|
3190
|
+
resultSubtype: finiteString(event.subtype),
|
|
3191
|
+
stopReason: finiteString(event.stop_reason),
|
|
3192
|
+
resultIsError: typeof event.is_error === "boolean" ? event.is_error : void 0,
|
|
3193
|
+
fastModeState: finiteString(event.fast_mode_state),
|
|
3194
|
+
permissionDenialsCount: Array.isArray(event.permission_denials) ? event.permission_denials.length : void 0,
|
|
3195
|
+
serviceTier: finiteString(usage?.service_tier),
|
|
3196
|
+
inferenceGeo: finiteString(usage?.inference_geo),
|
|
3197
|
+
usageSpeed: finiteString(usage?.speed),
|
|
3198
|
+
usageIterationsCount: Array.isArray(usage?.iterations) ? usage.iterations.length : void 0
|
|
3199
|
+
}),
|
|
3200
|
+
...collectNumericFields(usage?.server_tool_use, {
|
|
3201
|
+
web_search_requests: "serverToolUseWebSearchRequests",
|
|
3202
|
+
web_fetch_requests: "serverToolUseWebFetchRequests"
|
|
3203
|
+
}),
|
|
3204
|
+
...collectNumericFields(usage?.cache_creation, {
|
|
3205
|
+
ephemeral_1h_input_tokens: "cacheCreationEphemeral1hInputTokens",
|
|
3206
|
+
ephemeral_5m_input_tokens: "cacheCreationEphemeral5mInputTokens"
|
|
3207
|
+
}),
|
|
3208
|
+
...modelUsageAttrs
|
|
3209
|
+
};
|
|
3210
|
+
if (inputTokens !== void 0) attrs.inputTokens = inputTokens;
|
|
3211
|
+
if (outputTokens !== void 0) attrs.outputTokens = outputTokens;
|
|
3212
|
+
if (cachedInputTokens !== void 0) attrs.cachedInputTokens = cachedInputTokens;
|
|
3213
|
+
if (cacheCreationInputTokens !== void 0) attrs.cacheCreationInputTokens = cacheCreationInputTokens;
|
|
3214
|
+
const tokenComponents = [inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens];
|
|
3215
|
+
const totalTokens = tokenComponents.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
3216
|
+
if (tokenComponents.some((value) => value !== void 0)) attrs.totalTokens = totalTokens;
|
|
3217
|
+
if (Object.keys(attrs).length === 0) return null;
|
|
3218
|
+
return {
|
|
3219
|
+
kind: "telemetry",
|
|
3220
|
+
name: "token_usage",
|
|
3221
|
+
source: "claude_result_usage",
|
|
3222
|
+
usageKind: "per_turn",
|
|
3223
|
+
...typeof event.session_id === "string" && event.session_id ? { sessionId: event.session_id } : {},
|
|
3224
|
+
...typeof event.uuid === "string" && event.uuid ? { runtimeResultId: event.uuid } : {},
|
|
3225
|
+
attrs
|
|
3226
|
+
};
|
|
3227
|
+
}
|
|
3107
3228
|
var ClaudeEventNormalizer = class {
|
|
3108
3229
|
normalizeLine(line) {
|
|
3109
3230
|
let event;
|
|
@@ -3178,6 +3299,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3178
3299
|
case "result": {
|
|
3179
3300
|
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
3180
3301
|
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
3302
|
+
const usageTelemetry = parseClaudeResultUsageTelemetry(event);
|
|
3181
3303
|
switch (subtype) {
|
|
3182
3304
|
case "success":
|
|
3183
3305
|
if (event.is_error && stopReason !== "max_tokens") {
|
|
@@ -3199,6 +3321,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3199
3321
|
pushResultError(event, "Structured output retries exceeded");
|
|
3200
3322
|
break;
|
|
3201
3323
|
}
|
|
3324
|
+
if (usageTelemetry) events.push(usageTelemetry);
|
|
3202
3325
|
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
3203
3326
|
break;
|
|
3204
3327
|
}
|
|
@@ -3367,7 +3490,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
|
|
|
3367
3490
|
}
|
|
3368
3491
|
function resolveCommandOnPath(command, deps = {}) {
|
|
3369
3492
|
const platform = deps.platform ?? process.platform;
|
|
3370
|
-
const env =
|
|
3493
|
+
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3371
3494
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3372
3495
|
const existsSyncFn = deps.existsSyncFn ?? existsSync2;
|
|
3373
3496
|
if (platform === "win32") {
|
|
@@ -3393,7 +3516,7 @@ function firstExistingPath(candidates, deps = {}) {
|
|
|
3393
3516
|
return null;
|
|
3394
3517
|
}
|
|
3395
3518
|
function readCommandVersion(command, args = [], deps = {}) {
|
|
3396
|
-
const env =
|
|
3519
|
+
const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
|
|
3397
3520
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
|
|
3398
3521
|
try {
|
|
3399
3522
|
const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
|
|
@@ -3622,53 +3745,64 @@ var RuntimeTurnState = class {
|
|
|
3622
3745
|
};
|
|
3623
3746
|
|
|
3624
3747
|
// src/drivers/codexTelemetrySidecar.ts
|
|
3625
|
-
function
|
|
3748
|
+
function finiteNumber2(value) {
|
|
3626
3749
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3627
3750
|
}
|
|
3628
|
-
function
|
|
3751
|
+
function finiteString2(value) {
|
|
3629
3752
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3630
3753
|
}
|
|
3631
3754
|
function ratio(numerator, denominator) {
|
|
3632
3755
|
if (numerator === void 0 || denominator === void 0 || denominator <= 0) return void 0;
|
|
3633
3756
|
return Number((numerator / denominator).toFixed(6));
|
|
3634
3757
|
}
|
|
3635
|
-
function
|
|
3758
|
+
function withDefined2(attrs) {
|
|
3636
3759
|
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3637
3760
|
}
|
|
3638
3761
|
function parseTokenUsageTelemetry(message) {
|
|
3639
3762
|
const usage = message.params?.tokenUsage;
|
|
3640
3763
|
const total = usage?.total;
|
|
3641
3764
|
if (!total || typeof total !== "object") return null;
|
|
3642
|
-
const inputTokens =
|
|
3643
|
-
const cachedInputTokens =
|
|
3644
|
-
const totalTokens =
|
|
3645
|
-
const modelContextWindow =
|
|
3646
|
-
const attrs =
|
|
3765
|
+
const inputTokens = finiteNumber2(total.inputTokens);
|
|
3766
|
+
const cachedInputTokens = finiteNumber2(total.cachedInputTokens);
|
|
3767
|
+
const totalTokens = finiteNumber2(total.totalTokens);
|
|
3768
|
+
const modelContextWindow = finiteNumber2(usage.modelContextWindow);
|
|
3769
|
+
const attrs = withDefined2({
|
|
3647
3770
|
totalTokens,
|
|
3648
3771
|
inputTokens,
|
|
3649
3772
|
cachedInputTokens,
|
|
3650
|
-
outputTokens:
|
|
3651
|
-
reasoningOutputTokens:
|
|
3773
|
+
outputTokens: finiteNumber2(total.outputTokens),
|
|
3774
|
+
reasoningOutputTokens: finiteNumber2(total.reasoningOutputTokens),
|
|
3652
3775
|
modelContextWindow,
|
|
3653
3776
|
cachedInputRatio: ratio(cachedInputTokens, inputTokens),
|
|
3654
3777
|
contextUtilization: ratio(totalTokens, modelContextWindow)
|
|
3655
3778
|
});
|
|
3656
3779
|
if (Object.keys(attrs).length === 0) return null;
|
|
3657
|
-
return {
|
|
3780
|
+
return {
|
|
3781
|
+
kind: "telemetry",
|
|
3782
|
+
name: "token_usage",
|
|
3783
|
+
source: "codex_thread_token_usage_updated",
|
|
3784
|
+
usageKind: "cumulative_session",
|
|
3785
|
+
attrs
|
|
3786
|
+
};
|
|
3658
3787
|
}
|
|
3659
3788
|
function parseRateLimitTelemetry(message) {
|
|
3660
3789
|
const rateLimits = message.params?.rateLimits;
|
|
3661
3790
|
const primary = rateLimits?.primary;
|
|
3662
3791
|
if (!rateLimits || typeof rateLimits !== "object" || !primary || typeof primary !== "object") return null;
|
|
3663
|
-
const attrs =
|
|
3664
|
-
limitId:
|
|
3665
|
-
planType:
|
|
3666
|
-
usedPercent:
|
|
3667
|
-
windowDurationMins:
|
|
3668
|
-
resetsAt:
|
|
3792
|
+
const attrs = withDefined2({
|
|
3793
|
+
limitId: finiteString2(rateLimits.limitId),
|
|
3794
|
+
planType: finiteString2(rateLimits.planType),
|
|
3795
|
+
usedPercent: finiteNumber2(primary.usedPercent),
|
|
3796
|
+
windowDurationMins: finiteNumber2(primary.windowDurationMins),
|
|
3797
|
+
resetsAt: finiteNumber2(primary.resetsAt)
|
|
3669
3798
|
});
|
|
3670
3799
|
if (Object.keys(attrs).length === 0) return null;
|
|
3671
|
-
return {
|
|
3800
|
+
return {
|
|
3801
|
+
kind: "telemetry",
|
|
3802
|
+
name: "rate_limits",
|
|
3803
|
+
source: "codex_account_rate_limits_updated",
|
|
3804
|
+
attrs
|
|
3805
|
+
};
|
|
3672
3806
|
}
|
|
3673
3807
|
function parseCodexTelemetryEvent(message) {
|
|
3674
3808
|
switch (message.method) {
|
|
@@ -3775,7 +3909,11 @@ var CodexEventNormalizer = class {
|
|
|
3775
3909
|
}
|
|
3776
3910
|
const telemetry = parseCodexTelemetryEvent(message);
|
|
3777
3911
|
if (telemetry) {
|
|
3778
|
-
events.push(
|
|
3912
|
+
events.push({
|
|
3913
|
+
...telemetry,
|
|
3914
|
+
...this.currentThreadId ? { sessionId: this.currentThreadId } : {},
|
|
3915
|
+
...this.turnState.activeTurnId ? { turnId: this.turnState.activeTurnId } : {}
|
|
3916
|
+
});
|
|
3779
3917
|
return { events };
|
|
3780
3918
|
}
|
|
3781
3919
|
const rawProgress = rawResponseItemProgressEvent(message);
|
|
@@ -4825,11 +4963,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
|
|
|
4825
4963
|
return parseCursorModelsOutput(String(result.stdout || ""));
|
|
4826
4964
|
}
|
|
4827
4965
|
function buildCursorModelProbeEnv(deps = {}) {
|
|
4828
|
-
return
|
|
4966
|
+
return withWindowsUserEnvironment({
|
|
4829
4967
|
...deps.env ?? process.env,
|
|
4830
4968
|
FORCE_COLOR: "0",
|
|
4831
4969
|
NO_COLOR: "1"
|
|
4832
|
-
}, deps)
|
|
4970
|
+
}, deps);
|
|
4833
4971
|
}
|
|
4834
4972
|
function runCursorModelsCommand() {
|
|
4835
4973
|
return spawnSync("cursor-agent", ["models"], {
|
|
@@ -4885,7 +5023,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
|
|
|
4885
5023
|
}
|
|
4886
5024
|
const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
|
|
4887
5025
|
const existsSyncFn = deps.existsSyncFn ?? existsSync5;
|
|
4888
|
-
const env =
|
|
5026
|
+
const env = deps.env ?? process.env;
|
|
4889
5027
|
const winPath = path8.win32;
|
|
4890
5028
|
let geminiEntry = null;
|
|
4891
5029
|
try {
|
|
@@ -5057,16 +5195,13 @@ var GeminiDriver = class {
|
|
|
5057
5195
|
// src/drivers/kimi.ts
|
|
5058
5196
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
5059
5197
|
import { spawn as spawn7 } from "child_process";
|
|
5060
|
-
import {
|
|
5198
|
+
import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
|
|
5061
5199
|
import os3 from "os";
|
|
5062
5200
|
import path9 from "path";
|
|
5063
5201
|
var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
|
|
5064
5202
|
var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
|
|
5065
5203
|
var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
|
|
5066
5204
|
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";
|
|
5070
5205
|
function parseToolArguments(raw) {
|
|
5071
5206
|
if (typeof raw !== "string") return raw;
|
|
5072
5207
|
try {
|
|
@@ -5075,73 +5210,6 @@ function parseToolArguments(raw) {
|
|
|
5075
5210
|
return raw;
|
|
5076
5211
|
}
|
|
5077
5212
|
}
|
|
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
|
-
}
|
|
5145
5213
|
function resolveKimiSpawn(commandArgs, deps = {}) {
|
|
5146
5214
|
return {
|
|
5147
5215
|
command: resolveCommandOnPath("kimi", deps) ?? "kimi",
|
|
@@ -5165,25 +5233,7 @@ var KimiDriver = class {
|
|
|
5165
5233
|
};
|
|
5166
5234
|
model = {
|
|
5167
5235
|
detectedModelsVerifiedAs: "launchable",
|
|
5168
|
-
toLaunchSpec: (modelId
|
|
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
|
-
}
|
|
5236
|
+
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
5187
5237
|
};
|
|
5188
5238
|
supportsStdinNotification = true;
|
|
5189
5239
|
mcpToolPrefix = "";
|
|
@@ -5237,7 +5287,6 @@ var KimiDriver = class {
|
|
|
5237
5287
|
}
|
|
5238
5288
|
}
|
|
5239
5289
|
}), "utf8");
|
|
5240
|
-
const launch = buildKimiLaunchOptions(ctx);
|
|
5241
5290
|
const args = [
|
|
5242
5291
|
"--wire",
|
|
5243
5292
|
"--yolo",
|
|
@@ -5246,16 +5295,15 @@ var KimiDriver = class {
|
|
|
5246
5295
|
"--mcp-config-file",
|
|
5247
5296
|
mcpConfigPath,
|
|
5248
5297
|
"--session",
|
|
5249
|
-
this.sessionId
|
|
5250
|
-
...launch.args
|
|
5298
|
+
this.sessionId
|
|
5251
5299
|
];
|
|
5252
5300
|
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5253
5301
|
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5254
5302
|
args.push("--model", launchRuntimeFields.model);
|
|
5255
5303
|
}
|
|
5256
5304
|
const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5257
|
-
const
|
|
5258
|
-
const proc = spawn7(
|
|
5305
|
+
const launch = resolveKimiSpawn(args);
|
|
5306
|
+
const proc = spawn7(launch.command, launch.args, {
|
|
5259
5307
|
cwd: ctx.workingDirectory,
|
|
5260
5308
|
stdio: ["pipe", "pipe", "pipe"],
|
|
5261
5309
|
env: spawnEnv,
|
|
@@ -5263,7 +5311,7 @@ var KimiDriver = class {
|
|
|
5263
5311
|
// and has an 8191-character command-line limit. Kimi's official
|
|
5264
5312
|
// installer/uv entrypoint is an executable, so launch it directly and
|
|
5265
5313
|
// keep prompts on stdin / files instead of routing through cmd.exe.
|
|
5266
|
-
shell:
|
|
5314
|
+
shell: launch.shell
|
|
5267
5315
|
});
|
|
5268
5316
|
proc.stdin?.write(JSON.stringify({
|
|
5269
5317
|
jsonrpc: "2.0",
|
|
@@ -5377,9 +5425,14 @@ var KimiDriver = class {
|
|
|
5377
5425
|
return detectKimiModels();
|
|
5378
5426
|
}
|
|
5379
5427
|
};
|
|
5380
|
-
function detectKimiModels(home = os3.homedir()
|
|
5381
|
-
const
|
|
5382
|
-
|
|
5428
|
+
function detectKimiModels(home = os3.homedir()) {
|
|
5429
|
+
const configPath = path9.join(home, ".kimi", "config.toml");
|
|
5430
|
+
let raw;
|
|
5431
|
+
try {
|
|
5432
|
+
raw = readFileSync3(configPath, "utf8");
|
|
5433
|
+
} catch {
|
|
5434
|
+
return null;
|
|
5435
|
+
}
|
|
5383
5436
|
const models = [];
|
|
5384
5437
|
const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
|
|
5385
5438
|
const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
|
|
@@ -5643,7 +5696,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
|
|
|
5643
5696
|
const platform = deps.platform ?? process.platform;
|
|
5644
5697
|
const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
|
|
5645
5698
|
const result = spawnSyncFn("opencode", ["models"], {
|
|
5646
|
-
env:
|
|
5699
|
+
env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
5647
5700
|
encoding: "utf8",
|
|
5648
5701
|
timeout: 5e3,
|
|
5649
5702
|
shell: platform === "win32"
|
|
@@ -5903,139 +5956,123 @@ var OpenCodeDriver = class {
|
|
|
5903
5956
|
};
|
|
5904
5957
|
|
|
5905
5958
|
// src/drivers/pi.ts
|
|
5906
|
-
import {
|
|
5907
|
-
import {
|
|
5959
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5960
|
+
import { spawn as spawn9, spawnSync as spawnSync3 } from "child_process";
|
|
5961
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
5908
5962
|
import path11 from "path";
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5913
|
-
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
|
|
5918
|
-
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
5963
|
+
var PI_SESSION_DIR = ".pi-sessions";
|
|
5964
|
+
var PI_PROVIDER_LABELS = {
|
|
5965
|
+
deepseek: "DeepSeek",
|
|
5966
|
+
google: "Google",
|
|
5967
|
+
openai: "OpenAI",
|
|
5968
|
+
openrouter: "OpenRouter"
|
|
5969
|
+
};
|
|
5970
|
+
function buildPiSessionDir(workingDirectory) {
|
|
5971
|
+
return path11.join(workingDirectory, PI_SESSION_DIR);
|
|
5919
5972
|
}
|
|
5920
|
-
function
|
|
5921
|
-
|
|
5922
|
-
const
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5973
|
+
function buildPiRpcArgs(ctx, sessionId) {
|
|
5974
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5975
|
+
const args = [
|
|
5976
|
+
"--mode",
|
|
5977
|
+
"rpc",
|
|
5978
|
+
"--session-dir",
|
|
5979
|
+
buildPiSessionDir(ctx.workingDirectory),
|
|
5980
|
+
"--system-prompt",
|
|
5981
|
+
ctx.standingPrompt
|
|
5982
|
+
];
|
|
5983
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5984
|
+
args.push("--model", launchRuntimeFields.model);
|
|
5928
5985
|
}
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
if (
|
|
5933
|
-
|
|
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
|
-
};
|
|
5986
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
5987
|
+
args.push("--thinking", launchRuntimeFields.reasoningEffort);
|
|
5988
|
+
}
|
|
5989
|
+
if (sessionId) {
|
|
5990
|
+
args.push("--session-id", sessionId);
|
|
5942
5991
|
}
|
|
5943
|
-
return
|
|
5992
|
+
return args;
|
|
5944
5993
|
}
|
|
5945
|
-
function
|
|
5946
|
-
|
|
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");
|
|
5994
|
+
async function buildPiSpawnEnv(ctx) {
|
|
5995
|
+
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
5952
5996
|
}
|
|
5953
|
-
function
|
|
5954
|
-
|
|
5955
|
-
|
|
5997
|
+
function parsePiModelsOutput(output) {
|
|
5998
|
+
const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
5999
|
+
const models = [];
|
|
6000
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6001
|
+
for (const rawLine of stripAnsi(output).split(/\r?\n/)) {
|
|
6002
|
+
const line = rawLine.trim();
|
|
6003
|
+
if (!line || /^provider\s+model\s+/i.test(line) || /^[-\s]+$/.test(line)) continue;
|
|
6004
|
+
const columns = line.split(/\s+/);
|
|
6005
|
+
const provider = columns[0];
|
|
6006
|
+
const model = columns[1];
|
|
6007
|
+
if (!provider || !model || provider.startsWith("-") || model.startsWith("-")) continue;
|
|
6008
|
+
if (/^(yes|no)$/i.test(model)) continue;
|
|
6009
|
+
const id = `${provider}/${model}`;
|
|
6010
|
+
if (seen.has(id)) continue;
|
|
6011
|
+
seen.add(id);
|
|
6012
|
+
models.push({
|
|
6013
|
+
id,
|
|
6014
|
+
label: `${humanizePiSegment(model)} \xB7 ${PI_PROVIDER_LABELS[provider] || humanizePiSegment(provider)}`,
|
|
6015
|
+
verified: "launchable"
|
|
6016
|
+
});
|
|
5956
6017
|
}
|
|
5957
|
-
return
|
|
6018
|
+
return models.length > 0 ? { models } : null;
|
|
5958
6019
|
}
|
|
5959
|
-
|
|
5960
|
-
const
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
const
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
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
|
-
];
|
|
6020
|
+
function detectPiModels(runCommand = runPiModelsCommand) {
|
|
6021
|
+
const result = runCommand();
|
|
6022
|
+
if (result.error || result.status !== 0) return null;
|
|
6023
|
+
return parsePiModelsOutput(result.stdout);
|
|
6024
|
+
}
|
|
6025
|
+
function runPiModelsCommand() {
|
|
6026
|
+
const result = spawnSync3("pi", ["--list-models"], {
|
|
6027
|
+
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
6028
|
+
encoding: "utf8",
|
|
6029
|
+
timeout: 5e3
|
|
6030
|
+
});
|
|
5988
6031
|
return {
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
|
|
5992
|
-
sessionDir,
|
|
5993
|
-
agentDir,
|
|
5994
|
-
runnerConfigPath,
|
|
5995
|
-
sdkVersion: PI_SDK_VERSION
|
|
6032
|
+
status: result.status,
|
|
6033
|
+
stdout: String(result.stdout || ""),
|
|
6034
|
+
error: result.error
|
|
5996
6035
|
};
|
|
5997
6036
|
}
|
|
5998
|
-
function
|
|
5999
|
-
return
|
|
6037
|
+
function humanizePiSegment(value) {
|
|
6038
|
+
return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
|
|
6000
6039
|
}
|
|
6001
|
-
function
|
|
6002
|
-
|
|
6003
|
-
|
|
6004
|
-
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6040
|
+
function formatPiLabelToken(token) {
|
|
6041
|
+
const normalized = token.toLowerCase();
|
|
6042
|
+
const specialCases = {
|
|
6043
|
+
ai: "AI",
|
|
6044
|
+
api: "API",
|
|
6045
|
+
deepseek: "DeepSeek",
|
|
6046
|
+
flash: "Flash",
|
|
6047
|
+
gpt: "GPT",
|
|
6048
|
+
pro: "Pro",
|
|
6049
|
+
v4: "V4"
|
|
6050
|
+
};
|
|
6051
|
+
if (specialCases[normalized]) return specialCases[normalized];
|
|
6052
|
+
if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
|
|
6053
|
+
if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
|
|
6054
|
+
if (/^\d/.test(token)) return token;
|
|
6055
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
6013
6056
|
}
|
|
6014
|
-
function
|
|
6015
|
-
if (
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
if (
|
|
6019
|
-
|
|
6057
|
+
function piErrorMessage(error) {
|
|
6058
|
+
if (typeof error === "string" && error.trim()) return error.trim();
|
|
6059
|
+
if (error && typeof error === "object") {
|
|
6060
|
+
const record = error;
|
|
6061
|
+
if (typeof record.message === "string" && record.message.trim()) return record.message.trim();
|
|
6062
|
+
try {
|
|
6063
|
+
return JSON.stringify(error);
|
|
6064
|
+
} catch {
|
|
6020
6065
|
}
|
|
6021
6066
|
}
|
|
6022
|
-
return
|
|
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;
|
|
6067
|
+
return "Unknown Pi error";
|
|
6031
6068
|
}
|
|
6032
6069
|
var PiDriver = class {
|
|
6033
6070
|
id = "pi";
|
|
6071
|
+
supportsNativeStandingPrompt = true;
|
|
6034
6072
|
lifecycle = {
|
|
6035
|
-
kind: "
|
|
6036
|
-
|
|
6037
|
-
|
|
6038
|
-
inFlightWake: "coalesce_into_pending"
|
|
6073
|
+
kind: "persistent",
|
|
6074
|
+
stdin: "direct",
|
|
6075
|
+
inFlightWake: "steer"
|
|
6039
6076
|
};
|
|
6040
6077
|
communication = {
|
|
6041
6078
|
chat: "slock_cli",
|
|
@@ -6046,65 +6083,42 @@ var PiDriver = class {
|
|
|
6046
6083
|
};
|
|
6047
6084
|
model = {
|
|
6048
6085
|
detectedModelsVerifiedAs: "launchable",
|
|
6049
|
-
toLaunchSpec:
|
|
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
|
-
}
|
|
6086
|
+
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
6071
6087
|
};
|
|
6072
|
-
supportsStdinNotification =
|
|
6073
|
-
mcpToolPrefix =
|
|
6074
|
-
busyDeliveryMode = "
|
|
6075
|
-
terminateProcessOnTurnEnd = true;
|
|
6076
|
-
deferSpawnUntilMessage = true;
|
|
6088
|
+
supportsStdinNotification = true;
|
|
6089
|
+
mcpToolPrefix = "";
|
|
6090
|
+
busyDeliveryMode = "direct";
|
|
6077
6091
|
usesSlockCliForCommunication = true;
|
|
6078
6092
|
sessionId = null;
|
|
6079
6093
|
sessionAnnounced = false;
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
shouldDeferWakeMessage(message) {
|
|
6084
|
-
return isSystemFirstMessageTask2(message);
|
|
6085
|
-
}
|
|
6094
|
+
sawTextDelta = false;
|
|
6095
|
+
requestId = 0;
|
|
6096
|
+
process = null;
|
|
6086
6097
|
probe() {
|
|
6087
|
-
|
|
6098
|
+
const command = resolveCommandOnPath("pi");
|
|
6099
|
+
if (!command) return { available: false };
|
|
6100
|
+
return {
|
|
6101
|
+
available: true,
|
|
6102
|
+
version: readCommandVersion(command) ?? void 0
|
|
6103
|
+
};
|
|
6088
6104
|
}
|
|
6089
6105
|
async detectModels() {
|
|
6090
|
-
return
|
|
6106
|
+
return detectPiModels();
|
|
6091
6107
|
}
|
|
6092
6108
|
async spawn(ctx) {
|
|
6093
|
-
this.sessionId = ctx.config.sessionId ||
|
|
6109
|
+
this.sessionId = ctx.config.sessionId || randomUUID3();
|
|
6094
6110
|
this.sessionAnnounced = false;
|
|
6095
|
-
this.
|
|
6096
|
-
this.
|
|
6097
|
-
|
|
6098
|
-
const
|
|
6099
|
-
if (unsupportedMessage) throw new Error(unsupportedMessage);
|
|
6100
|
-
const launch = await buildPiLaunchOptions(ctx);
|
|
6101
|
-
const proc = spawn9(launch.command, launch.args, {
|
|
6111
|
+
this.sawTextDelta = false;
|
|
6112
|
+
this.requestId = 0;
|
|
6113
|
+
mkdirSync4(buildPiSessionDir(ctx.workingDirectory), { recursive: true });
|
|
6114
|
+
const proc = spawn9(resolveCommandOnPath("pi") ?? "pi", buildPiRpcArgs(ctx, this.sessionId), {
|
|
6102
6115
|
cwd: ctx.workingDirectory,
|
|
6103
6116
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6104
|
-
env:
|
|
6105
|
-
shell:
|
|
6117
|
+
env: await buildPiSpawnEnv(ctx),
|
|
6118
|
+
shell: process.platform === "win32"
|
|
6106
6119
|
});
|
|
6107
|
-
proc
|
|
6120
|
+
this.process = proc;
|
|
6121
|
+
this.sendRpcCommand("prompt", { message: ctx.prompt });
|
|
6108
6122
|
return { process: proc };
|
|
6109
6123
|
}
|
|
6110
6124
|
parseLine(line) {
|
|
@@ -6112,84 +6126,103 @@ var PiDriver = class {
|
|
|
6112
6126
|
try {
|
|
6113
6127
|
event = JSON.parse(line);
|
|
6114
6128
|
} catch {
|
|
6115
|
-
|
|
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
|
-
];
|
|
6129
|
+
return [];
|
|
6124
6130
|
}
|
|
6125
6131
|
const events = [];
|
|
6126
6132
|
if (event.type === "session" && event.id) {
|
|
6127
6133
|
this.sessionId = event.id;
|
|
6134
|
+
if (!this.sessionAnnounced) {
|
|
6135
|
+
this.sessionAnnounced = true;
|
|
6136
|
+
events.push({ kind: "session_init", sessionId: event.id });
|
|
6137
|
+
}
|
|
6138
|
+
return events;
|
|
6128
6139
|
}
|
|
6129
6140
|
if (!this.sessionAnnounced && this.sessionId) {
|
|
6130
6141
|
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
6131
6142
|
this.sessionAnnounced = true;
|
|
6132
6143
|
}
|
|
6133
|
-
|
|
6134
|
-
|
|
6135
|
-
|
|
6136
|
-
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6144
|
+
if (event.type === "response") {
|
|
6145
|
+
if (event.data?.sessionId && event.data.sessionId !== this.sessionId) {
|
|
6146
|
+
this.sessionId = event.data.sessionId;
|
|
6147
|
+
}
|
|
6148
|
+
if (event.success === false) {
|
|
6149
|
+
events.push({ kind: "error", message: piErrorMessage(event.error) });
|
|
6150
|
+
}
|
|
6151
|
+
return events;
|
|
6152
|
+
}
|
|
6153
|
+
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
6154
|
+
this.sawTextDelta = false;
|
|
6155
|
+
return events;
|
|
6156
|
+
}
|
|
6157
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
6158
|
+
if (event.type === "message_update" && assistantEvent) {
|
|
6159
|
+
switch (assistantEvent.type) {
|
|
6160
|
+
case "thinking_delta":
|
|
6161
|
+
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6162
|
+
events.push({ kind: "thinking", text: assistantEvent.delta });
|
|
6148
6163
|
}
|
|
6149
|
-
|
|
6150
|
-
|
|
6151
|
-
|
|
6164
|
+
break;
|
|
6165
|
+
case "text_delta":
|
|
6166
|
+
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6167
|
+
this.sawTextDelta = true;
|
|
6168
|
+
events.push({ kind: "text", text: assistantEvent.delta });
|
|
6152
6169
|
}
|
|
6153
|
-
|
|
6154
|
-
|
|
6155
|
-
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
|
|
6176
|
-
|
|
6177
|
-
|
|
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;
|
|
6170
|
+
break;
|
|
6171
|
+
case "thinking_start":
|
|
6172
|
+
case "text_start":
|
|
6173
|
+
break;
|
|
6174
|
+
case "tool_use":
|
|
6175
|
+
case "tool_call":
|
|
6176
|
+
case "tool_start":
|
|
6177
|
+
events.push({
|
|
6178
|
+
kind: "tool_call",
|
|
6179
|
+
name: assistantEvent.name || assistantEvent.toolName || "unknown_tool",
|
|
6180
|
+
input: assistantEvent.input ?? assistantEvent.parameters ?? {}
|
|
6181
|
+
});
|
|
6182
|
+
break;
|
|
6183
|
+
case "text_end":
|
|
6184
|
+
if (!this.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0) {
|
|
6185
|
+
events.push({ kind: "text", text: assistantEvent.content });
|
|
6186
|
+
}
|
|
6187
|
+
break;
|
|
6188
|
+
}
|
|
6189
|
+
return events;
|
|
6190
|
+
}
|
|
6191
|
+
if (event.type === "agent_end") {
|
|
6192
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
6193
|
+
} else if (event.type === "error") {
|
|
6194
|
+
events.push({ kind: "error", message: piErrorMessage(event.error ?? event.message) });
|
|
6185
6195
|
}
|
|
6186
6196
|
return events;
|
|
6187
6197
|
}
|
|
6188
|
-
encodeStdinMessage(
|
|
6189
|
-
return
|
|
6198
|
+
encodeStdinMessage(text, _sessionId, opts) {
|
|
6199
|
+
return JSON.stringify({
|
|
6200
|
+
id: this.nextRequestId(),
|
|
6201
|
+
type: opts?.mode === "idle" ? "prompt" : "steer",
|
|
6202
|
+
message: text
|
|
6203
|
+
});
|
|
6190
6204
|
}
|
|
6191
6205
|
buildSystemPrompt(config, _agentId) {
|
|
6192
|
-
return
|
|
6206
|
+
return buildCliTransportSystemPrompt(config, {
|
|
6207
|
+
toolPrefix: "",
|
|
6208
|
+
extraCriticalRules: [],
|
|
6209
|
+
postStartupNotes: [
|
|
6210
|
+
"**Pi runtime note:** Slock keeps Pi running in RPC mode. While you are working, Slock may send inbox-count notifications into the current turn; call `slock message check` at natural breakpoints."
|
|
6211
|
+
],
|
|
6212
|
+
includeStdinNotificationSection: true,
|
|
6213
|
+
messageNotificationStyle: "direct"
|
|
6214
|
+
});
|
|
6215
|
+
}
|
|
6216
|
+
nextRequestId() {
|
|
6217
|
+
this.requestId += 1;
|
|
6218
|
+
return String(this.requestId);
|
|
6219
|
+
}
|
|
6220
|
+
sendRpcCommand(type, params) {
|
|
6221
|
+
this.process?.stdin?.write(JSON.stringify({
|
|
6222
|
+
id: this.nextRequestId(),
|
|
6223
|
+
type,
|
|
6224
|
+
...params
|
|
6225
|
+
}) + "\n");
|
|
6193
6226
|
}
|
|
6194
6227
|
};
|
|
6195
6228
|
|
|
@@ -6468,6 +6501,8 @@ function runtimeDisplayName(runtimeId) {
|
|
|
6468
6501
|
return "Kimi CLI";
|
|
6469
6502
|
case "opencode":
|
|
6470
6503
|
return "OpenCode";
|
|
6504
|
+
case "pi":
|
|
6505
|
+
return "Pi CLI";
|
|
6471
6506
|
default:
|
|
6472
6507
|
return runtimeId || "This runtime";
|
|
6473
6508
|
}
|
|
@@ -6753,7 +6788,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
6753
6788
|
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6754
6789
|
mkdirSync5(dir, { recursive: true });
|
|
6755
6790
|
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6756
|
-
|
|
6791
|
+
writeFileSync7(filePath, JSON.stringify({
|
|
6757
6792
|
type: "runtime_session_handoff",
|
|
6758
6793
|
runtime,
|
|
6759
6794
|
sessionId,
|
|
@@ -6870,7 +6905,7 @@ function dynamicClaimInstruction(driver) {
|
|
|
6870
6905
|
}
|
|
6871
6906
|
function formatIncomingMessage(message, driver) {
|
|
6872
6907
|
const threadJoinPrefix = message.thread_join_context ? [
|
|
6873
|
-
`[
|
|
6908
|
+
`[Slock thread context: you were added to a new thread via @mention.]`,
|
|
6874
6909
|
`parent: ${message.thread_join_context.parent_target}`,
|
|
6875
6910
|
`thread: ${message.thread_join_context.thread_target}`,
|
|
6876
6911
|
`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}")`}`,
|
|
@@ -7696,7 +7731,7 @@ function getBusyDeliveryNote(driver) {
|
|
|
7696
7731
|
if (driver.busyDeliveryMode === "gated") {
|
|
7697
7732
|
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.";
|
|
7698
7733
|
}
|
|
7699
|
-
return "\n\nNote: While you are busy, you may receive [
|
|
7734
|
+
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.";
|
|
7700
7735
|
}
|
|
7701
7736
|
var NATIVE_STANDING_PROMPT_STARTUP_INPUT = (
|
|
7702
7737
|
// Claude Code 2.1.114 treats "follow your system prompt" style user turns as
|
|
@@ -10419,15 +10454,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10419
10454
|
}
|
|
10420
10455
|
}
|
|
10421
10456
|
recordRuntimeTelemetry(agentId, ap, event) {
|
|
10457
|
+
const telemetryAttrs = {
|
|
10458
|
+
...event.attrs,
|
|
10459
|
+
...event.source ? { source: event.source } : {},
|
|
10460
|
+
...event.usageKind ? { usageKind: event.usageKind } : {},
|
|
10461
|
+
...event.sessionId ? { sessionId: event.sessionId } : {},
|
|
10462
|
+
...event.turnId ? { turnId: event.turnId } : {},
|
|
10463
|
+
...event.runtimeResultId ? { runtimeResultId: event.runtimeResultId } : {}
|
|
10464
|
+
};
|
|
10422
10465
|
const attrs = {
|
|
10423
10466
|
agentId,
|
|
10424
10467
|
launchId: ap.launchId || void 0,
|
|
10425
10468
|
runtime: ap.config.runtime,
|
|
10426
10469
|
model: ap.config.model,
|
|
10427
10470
|
telemetry_name: event.name,
|
|
10428
|
-
...
|
|
10471
|
+
...telemetryAttrs
|
|
10429
10472
|
};
|
|
10430
|
-
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`,
|
|
10473
|
+
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
|
|
10431
10474
|
this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
|
|
10432
10475
|
}
|
|
10433
10476
|
sendAgentStatus(agentId, status, launchId) {
|
|
@@ -10501,7 +10544,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10501
10544
|
}
|
|
10502
10545
|
const inboxCount = ap.inbox.length;
|
|
10503
10546
|
if (inboxCount === 0) return false;
|
|
10504
|
-
const notification = `[
|
|
10547
|
+
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.]`;
|
|
10505
10548
|
logger.info(`[Agent ${agentId}] Sending stdin notification: ${inboxCount} pending inbox message(s)`);
|
|
10506
10549
|
const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
|
|
10507
10550
|
if (encoded) {
|
|
@@ -10953,8 +10996,8 @@ var ReminderCache = class {
|
|
|
10953
10996
|
};
|
|
10954
10997
|
|
|
10955
10998
|
// src/machineLock.ts
|
|
10956
|
-
import { createHash as createHash4, randomUUID as
|
|
10957
|
-
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as
|
|
10999
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
11000
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
10958
11001
|
import os6 from "os";
|
|
10959
11002
|
import path14 from "path";
|
|
10960
11003
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
@@ -11010,7 +11053,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
11010
11053
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
11011
11054
|
const machineDir = path14.join(rootDir, lockId);
|
|
11012
11055
|
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
11013
|
-
const token =
|
|
11056
|
+
const token = randomUUID4();
|
|
11014
11057
|
mkdirSync6(machineDir, { recursive: true });
|
|
11015
11058
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
11016
11059
|
try {
|
|
@@ -11024,7 +11067,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
11024
11067
|
apiKeyFingerprint: fingerprint.slice(0, 16)
|
|
11025
11068
|
};
|
|
11026
11069
|
try {
|
|
11027
|
-
|
|
11070
|
+
writeFileSync8(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
|
|
11028
11071
|
`, { mode: 384 });
|
|
11029
11072
|
} catch (err) {
|
|
11030
11073
|
rmSync3(lockDir, { recursive: true, force: true });
|
|
@@ -11037,7 +11080,15 @@ function acquireDaemonMachineLock(options) {
|
|
|
11037
11080
|
release: () => {
|
|
11038
11081
|
const currentOwner = readOwner(lockDir);
|
|
11039
11082
|
if (currentOwner?.pid === process.pid && currentOwner.token === token) {
|
|
11040
|
-
|
|
11083
|
+
const released = { ...currentOwner, pid: 0 };
|
|
11084
|
+
try {
|
|
11085
|
+
writeFileSync8(ownerPath(lockDir), `${JSON.stringify(released, null, 2)}
|
|
11086
|
+
`, {
|
|
11087
|
+
mode: 384
|
|
11088
|
+
});
|
|
11089
|
+
} catch {
|
|
11090
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
11091
|
+
}
|
|
11041
11092
|
}
|
|
11042
11093
|
}
|
|
11043
11094
|
};
|
|
@@ -11061,7 +11112,7 @@ function acquireDaemonMachineLock(options) {
|
|
|
11061
11112
|
}
|
|
11062
11113
|
|
|
11063
11114
|
// src/localTraceSink.ts
|
|
11064
|
-
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as
|
|
11115
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
11065
11116
|
import path15 from "path";
|
|
11066
11117
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
11067
11118
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
@@ -11133,7 +11184,7 @@ var LocalRotatingTraceSink = class {
|
|
|
11133
11184
|
this.traceDir,
|
|
11134
11185
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
11135
11186
|
);
|
|
11136
|
-
|
|
11187
|
+
writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
|
|
11137
11188
|
this.currentSize = statSync3(this.currentFile).size;
|
|
11138
11189
|
this.currentFileOpenedAtMs = nowMs;
|
|
11139
11190
|
this.pruneOldFiles();
|
|
@@ -11232,7 +11283,7 @@ function isDiagnosticErrorAttr(key) {
|
|
|
11232
11283
|
}
|
|
11233
11284
|
|
|
11234
11285
|
// src/traceBundleUpload.ts
|
|
11235
|
-
import { createHash as createHash6, randomUUID as
|
|
11286
|
+
import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
|
|
11236
11287
|
import { gzipSync } from "zlib";
|
|
11237
11288
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
11238
11289
|
import path16 from "path";
|
|
@@ -11503,7 +11554,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
11503
11554
|
}
|
|
11504
11555
|
const gzipped = gzipSync(raw);
|
|
11505
11556
|
const bundleSha256 = sha256Hex(gzipped);
|
|
11506
|
-
const bundleId =
|
|
11557
|
+
const bundleId = randomUUID5();
|
|
11507
11558
|
await uploadWithSignedCapability({
|
|
11508
11559
|
serverUrl: this.options.serverUrl,
|
|
11509
11560
|
apiKey: this.options.apiKey,
|
|
@@ -11582,7 +11633,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
|
|
|
11582
11633
|
var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
|
|
11583
11634
|
var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
|
|
11584
11635
|
var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
|
|
11585
|
-
var DAEMON_CLI_USAGE =
|
|
11636
|
+
var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
|
|
11586
11637
|
var RunnerCredentialMintError2 = class extends Error {
|
|
11587
11638
|
code;
|
|
11588
11639
|
retryable;
|
|
@@ -11618,9 +11669,9 @@ function runnerCredentialErrorDetail2(error) {
|
|
|
11618
11669
|
async function waitForRunnerCredentialRetry2() {
|
|
11619
11670
|
await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
|
|
11620
11671
|
}
|
|
11621
|
-
function parseDaemonCliArgs(args
|
|
11672
|
+
function parseDaemonCliArgs(args) {
|
|
11622
11673
|
let serverUrl = "";
|
|
11623
|
-
let apiKey =
|
|
11674
|
+
let apiKey = "";
|
|
11624
11675
|
for (let i = 0; i < args.length; i++) {
|
|
11625
11676
|
if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
|
|
11626
11677
|
if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
|
|
@@ -11637,7 +11688,7 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
11637
11688
|
}
|
|
11638
11689
|
}
|
|
11639
11690
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
11640
|
-
const dirname = path17.dirname(
|
|
11691
|
+
const dirname = path17.dirname(fileURLToPath(moduleUrl));
|
|
11641
11692
|
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
11642
11693
|
try {
|
|
11643
11694
|
accessSync(jsPath);
|
|
@@ -11647,7 +11698,7 @@ function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
|
11647
11698
|
}
|
|
11648
11699
|
}
|
|
11649
11700
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
11650
|
-
const thisDir = path17.dirname(
|
|
11701
|
+
const thisDir = path17.dirname(fileURLToPath(moduleUrl));
|
|
11651
11702
|
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
11652
11703
|
try {
|
|
11653
11704
|
accessSync(bundledDistPath);
|
|
@@ -12371,8 +12422,6 @@ var DaemonCore = class {
|
|
|
12371
12422
|
};
|
|
12372
12423
|
|
|
12373
12424
|
export {
|
|
12374
|
-
DAEMON_API_KEY_ENV,
|
|
12375
|
-
scrubDaemonAuthEnv,
|
|
12376
12425
|
resolveWorkspaceDirectoryPath,
|
|
12377
12426
|
scanWorkspaceDirectories,
|
|
12378
12427
|
deleteWorkspaceDirectory,
|