@slock-ai/daemon 0.55.3 → 0.55.5
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-DLB2KVD7.js → chunk-NXNXMFLC.js} +626 -144
- package/dist/cli/index.js +117 -15
- package/dist/cli/package.json +1 -1
- package/dist/core.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@ 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";
|
|
@@ -995,13 +995,19 @@ var RUNTIMES = [
|
|
|
995
995
|
{ id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
|
|
996
996
|
{ id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
|
|
997
997
|
{ id: "gemini", displayName: "Gemini CLI", binary: "gemini", supported: true },
|
|
998
|
-
{ 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 }
|
|
999
1000
|
];
|
|
1000
1001
|
var RUNTIME_MODELS = {
|
|
1001
1002
|
claude: [
|
|
1002
|
-
{ id: "opus", label: "Opus" },
|
|
1003
|
-
{ id: "
|
|
1004
|
-
{ id: "
|
|
1003
|
+
{ id: "opus", label: "Claude Opus" },
|
|
1004
|
+
{ id: "claude-opus-4-8", label: "Claude Opus 4.8" },
|
|
1005
|
+
{ id: "claude-opus-4-7", label: "Claude Opus 4.7" },
|
|
1006
|
+
{ id: "claude-opus-4-6", label: "Claude Opus 4.6" },
|
|
1007
|
+
{ id: "sonnet", label: "Claude Sonnet" },
|
|
1008
|
+
{ id: "claude-sonnet-4-6", label: "Claude Sonnet 4.6" },
|
|
1009
|
+
{ id: "haiku", label: "Claude Haiku" },
|
|
1010
|
+
{ id: "claude-haiku-4-5", label: "Claude Haiku 4.5" }
|
|
1005
1011
|
],
|
|
1006
1012
|
codex: [
|
|
1007
1013
|
{ id: "gpt-5.5", label: "GPT-5.5" },
|
|
@@ -1042,6 +1048,11 @@ var RUNTIME_MODELS = {
|
|
|
1042
1048
|
{ id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
|
|
1043
1049
|
{ id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
|
|
1044
1050
|
],
|
|
1051
|
+
pi: [
|
|
1052
|
+
{ id: "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" }
|
|
1055
|
+
],
|
|
1045
1056
|
// Kimi CLI resolves model keys from each user's local config, so the safest
|
|
1046
1057
|
// built-in option is to defer to whatever default model the CLI already uses.
|
|
1047
1058
|
kimi: [
|
|
@@ -1086,22 +1097,28 @@ function isPresetRuntimeModel(runtime, model) {
|
|
|
1086
1097
|
function modelConfigFromLegacy(runtime, model) {
|
|
1087
1098
|
return isPresetRuntimeModel(runtime, model) ? { kind: "preset", id: model } : { kind: "custom", name: model };
|
|
1088
1099
|
}
|
|
1089
|
-
function parseProviderConfig(runtime, value) {
|
|
1100
|
+
function parseProviderConfig(runtime, value, legacyApiUrl, legacyApiKey) {
|
|
1090
1101
|
if (runtime !== "claude") return void 0;
|
|
1091
1102
|
if (!isPlainRecord(value)) return { kind: "default" };
|
|
1092
1103
|
if (value.kind === "custom" && typeof value.apiUrl === "string" && value.apiUrl.trim() && typeof value.apiKey === "string" && value.apiKey.trim()) {
|
|
1093
1104
|
return { kind: "custom", apiUrl: value.apiUrl.trim(), apiKey: value.apiKey.trim() };
|
|
1094
1105
|
}
|
|
1106
|
+
if (value.kind === "custom" && legacyApiUrl?.trim() && legacyApiKey?.trim()) {
|
|
1107
|
+
return { kind: "custom", apiUrl: legacyApiUrl.trim(), apiKey: legacyApiKey.trim() };
|
|
1108
|
+
}
|
|
1095
1109
|
return { kind: "default" };
|
|
1096
1110
|
}
|
|
1097
|
-
function parseModelConfig(runtime, value, fallback) {
|
|
1111
|
+
function parseModelConfig(runtime, value, fallback, provider, legacyCustomModel) {
|
|
1098
1112
|
if (!isPlainRecord(value)) return modelConfigFromLegacy(runtime, fallback);
|
|
1099
1113
|
if (value.kind === "custom" && typeof value.name === "string" && value.name.trim()) {
|
|
1100
1114
|
return { kind: "custom", name: value.name.trim() };
|
|
1101
1115
|
}
|
|
1102
|
-
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())) {
|
|
1103
1117
|
return { kind: "preset", id: value.id.trim() };
|
|
1104
1118
|
}
|
|
1119
|
+
if (provider?.kind === "custom" && runtime === "claude" && legacyCustomModel?.trim()) {
|
|
1120
|
+
return { kind: "custom", name: legacyCustomModel.trim() };
|
|
1121
|
+
}
|
|
1105
1122
|
return modelConfigFromLegacy(runtime, fallback);
|
|
1106
1123
|
}
|
|
1107
1124
|
function parseModeConfig(value) {
|
|
@@ -1117,12 +1134,13 @@ function hydrateRuntimeConfig(input) {
|
|
|
1117
1134
|
const storedEnvVars = normalizeEnvVars(stored?.envVars);
|
|
1118
1135
|
const legacyClaudeApiUrl = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_BASE_URL : void 0;
|
|
1119
1136
|
const legacyClaudeApiKey = runtime === "claude" ? legacyEnvVars?.ANTHROPIC_API_KEY : void 0;
|
|
1120
|
-
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;
|
|
1121
1139
|
return {
|
|
1122
1140
|
version: RUNTIME_CONFIG_VERSION,
|
|
1123
1141
|
runtime,
|
|
1124
1142
|
...provider ? { provider } : {},
|
|
1125
|
-
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1143
|
+
model: stored ? parseModelConfig(runtime, stored.model, fallbackModel, provider, legacyClaudeCustomModel) : modelConfigFromLegacy(runtime, fallbackModel),
|
|
1126
1144
|
mode: stored ? parseModeConfig(stored.mode) : { kind: "default" },
|
|
1127
1145
|
reasoningEffort: stored?.reasoningEffort ?? input.reasoningEffort ?? null,
|
|
1128
1146
|
envVars: stripControlledRuntimeEnvVars(runtime, stored ? storedEnvVars : legacyEnvVars)
|
|
@@ -1190,10 +1208,10 @@ var DISPLAY_PLAN_CONFIG = {
|
|
|
1190
1208
|
};
|
|
1191
1209
|
|
|
1192
1210
|
// src/agentProcessManager.ts
|
|
1193
|
-
import { mkdirSync as
|
|
1211
|
+
import { mkdirSync as mkdirSync5, readdirSync, statSync, writeFileSync as writeFileSync7 } from "fs";
|
|
1194
1212
|
import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
|
|
1195
1213
|
import { createHash as createHash3 } from "crypto";
|
|
1196
|
-
import
|
|
1214
|
+
import path13 from "path";
|
|
1197
1215
|
import os5 from "os";
|
|
1198
1216
|
|
|
1199
1217
|
// src/drivers/claude.ts
|
|
@@ -1285,28 +1303,29 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
|
|
|
1285
1303
|
5. **\`slock channel join\`** \u2014 Join a visible public channel. This only affects your own agent membership.
|
|
1286
1304
|
6. **\`slock channel leave\`** \u2014 Leave a regular channel you have joined. This only affects your own agent membership.
|
|
1287
1305
|
7. **\`slock thread unfollow\`** \u2014 Stop receiving ordinary delivery for a thread you no longer need to follow. This only affects your own agent attention state.
|
|
1288
|
-
8. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\`
|
|
1306
|
+
8. **\`slock message read\`** \u2014 Read past messages from a channel, DM, or thread. Supports \`before\` / \`after\` anchors and \`around\` for centered context.
|
|
1289
1307
|
9. **\`slock message search\`** \u2014 Search messages visible to you, then inspect a hit with \`slock message read\`.
|
|
1290
|
-
10. **\`slock message
|
|
1291
|
-
11. **\`slock
|
|
1292
|
-
12. **\`slock task
|
|
1293
|
-
13. **\`slock task
|
|
1294
|
-
14. **\`slock task
|
|
1295
|
-
15. **\`slock task
|
|
1296
|
-
16. **\`slock
|
|
1297
|
-
17. **\`slock attachment
|
|
1298
|
-
18. **\`slock
|
|
1299
|
-
19. **\`slock profile
|
|
1300
|
-
20. **\`slock
|
|
1301
|
-
21. **\`slock integration
|
|
1302
|
-
22. **\`slock integration
|
|
1303
|
-
23. **\`slock
|
|
1304
|
-
24. **\`slock reminder
|
|
1305
|
-
25. **\`slock reminder
|
|
1306
|
-
26. **\`slock reminder
|
|
1307
|
-
27. **\`slock reminder
|
|
1308
|
-
28. **\`slock reminder
|
|
1309
|
-
29. **\`slock
|
|
1308
|
+
10. **\`slock message resolve\`** \u2014 Verify that a cited message id exists exactly and print its canonical message row. Use this when checking whether a referenced id is real; \`read --around\` is for context, not proof.
|
|
1309
|
+
11. **\`slock message react\`** \u2014 Add or remove your reaction on a message. Use sparingly: prefer acknowledgement/follow-up signals like \u{1F440}, and do not auto-react to every merge, deploy, or task completion with celebratory emoji.
|
|
1310
|
+
12. **\`slock task list\`** \u2014 View a channel's task board.
|
|
1311
|
+
13. **\`slock task create\`** \u2014 Create new task-messages in a channel (supports batch titles; equivalent to sending a new message and publishing it as a task-message, not claiming it for yourself).
|
|
1312
|
+
14. **\`slock task claim\`** \u2014 Claim tasks by number or message ID (supports batch, handles conflicts).
|
|
1313
|
+
15. **\`slock task unclaim\`** \u2014 Release your claim on a task.
|
|
1314
|
+
16. **\`slock task update\`** \u2014 Change a task's status (e.g. to in_review or done).
|
|
1315
|
+
17. **\`slock attachment upload\`** \u2014 Upload a file to attach to a message. Uses content sniffing for image previews; pass \`--mime-type\` only when you know the exact type. Returns an attachment ID to pass to \`slock message send\`.
|
|
1316
|
+
18. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
|
|
1317
|
+
19. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
|
|
1318
|
+
20. **\`slock profile update\`** \u2014 Update your own profile. Supports \`--avatar-file <path>\`, \`--avatar-url pixel:random:<seed>\`, \`--display-name <name>\`, and \`--description <text>\`. Use \`--avatar-url pixel:random:<seed>\` when you want a new pixel avatar but do not have a local image file. Values must be non-empty. Provide at least one flag per call; multiple flags can be combined.
|
|
1319
|
+
21. **\`slock integration list\`** \u2014 List built-in Slock apps, registered third-party services, and this agent's active Slock Agent Logins.
|
|
1320
|
+
22. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a built-in Slock app or registered third-party service.
|
|
1321
|
+
23. **\`slock integration env\`** \u2014 Print per-agent local CLI environment for a manifest-backed service that requires isolated HOME/XDG state.
|
|
1322
|
+
24. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
|
|
1323
|
+
25. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
|
|
1324
|
+
26. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
|
|
1325
|
+
27. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
|
|
1326
|
+
28. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
|
|
1327
|
+
29. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
|
|
1328
|
+
30. **\`slock action prepare\`** \u2014 Prepare an action card for a human to commit (B-mode quick-commit shortcut). Posts a card the human can click to execute the action under their own identity. Pass \`--target <ch>\` and pipe the action JSON on stdin (variants: \`channel:create\`, \`agent:create\`).
|
|
1310
1329
|
|
|
1311
1330
|
The CLI prints human-readable canonical text on success (matching the format you see in received messages and history). On failure it prints JSON to stderr:
|
|
1312
1331
|
- failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
|
|
@@ -1374,19 +1393,19 @@ If Slock says a message was not sent and was saved as a draft, choose one path:
|
|
|
1374
1393
|
|
|
1375
1394
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1376
1395
|
|
|
1377
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1396
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1378
1397
|
- When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
|
|
1379
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1398
|
+
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, reply with \`slock message send --target "#general:00000000" <<'${messageHeredocDelimiter}'\` followed by the message body and \`${messageHeredocDelimiter}\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
|
|
1380
1399
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1381
|
-
- You can read thread history: \`slock message read --channel "#general:
|
|
1382
|
-
- You can stop receiving ordinary delivery for a thread with \`slock thread unfollow --target "#general:
|
|
1400
|
+
- You can read thread history: \`slock message read --channel "#general:00000000"\`
|
|
1401
|
+
- You can stop receiving ordinary delivery for a thread with \`slock thread unfollow --target "#general:00000000"\`. Only do this when your work in that thread is clearly complete or no longer relevant.
|
|
1383
1402
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.` : `### Threads
|
|
1384
1403
|
|
|
1385
1404
|
Threads are sub-conversations attached to a specific message. They let you discuss a topic without cluttering the main channel.
|
|
1386
1405
|
|
|
1387
|
-
- **Thread targets** have a colon and short ID suffix: \`#general:
|
|
1406
|
+
- **Thread targets** have a colon and short ID suffix: \`#general:00000000\` (thread in #general) or \`dm:@richard:11111111\` (thread in a DM).
|
|
1388
1407
|
- When you receive a message from a thread (the target has a \`:shortid\` suffix), **always reply using that same target** to keep the conversation in the thread.
|
|
1389
|
-
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=
|
|
1408
|
+
- **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, call \`${t("send_message")}(target="#general:00000000", content="...")\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
|
|
1390
1409
|
- When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
|
|
1391
1410
|
- You can read thread history via ${readCmd} with the same thread target.
|
|
1392
1411
|
- Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
|
|
@@ -1419,7 +1438,9 @@ If a built-in Slock app or registered third-party service requires login, use Sl
|
|
|
1419
1438
|
|
|
1420
1439
|
\`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
|
|
1421
1440
|
|
|
1422
|
-
To jump directly to a specific hit with nearby context, use \`slock message read --channel "..." --around "messageId"\` or \`slock message read --channel "..." --around 12345
|
|
1441
|
+
To jump directly to a specific hit with nearby context, use \`slock message read --channel "..." --around "messageId"\` or \`slock message read --channel "..." --around 12345\`.
|
|
1442
|
+
|
|
1443
|
+
When verifying a cited message id, use \`slock message resolve <id>\`. It is exact-only and fails closed for missing or ambiguous ids; \`read --around\` is only a context-navigation command.` : `### Reading history
|
|
1423
1444
|
|
|
1424
1445
|
Use ${readCmd} with the \`channel\` parameter set to \`"#channel-name"\`, \`"dm:@peer-name"\`, or a thread target like \`"#channel:shortid"\`.
|
|
1425
1446
|
|
|
@@ -1552,13 +1573,18 @@ ${opts.postStartupNotes.join("\n")}`;
|
|
|
1552
1573
|
Messages you receive have a single RFC 5424-style structured data header followed by the sender and content:
|
|
1553
1574
|
|
|
1554
1575
|
\`\`\`
|
|
1555
|
-
[target=#general msg=
|
|
1556
|
-
[target=#general msg=
|
|
1557
|
-
[target=dm:@richard msg=
|
|
1558
|
-
[target=#general:
|
|
1559
|
-
[target=dm:@richard:
|
|
1576
|
+
[target=#general msg=00000000 time=2026-03-15T01:00:00 type=human] @richard: hello everyone
|
|
1577
|
+
[target=#general msg=11111111 time=2026-03-15T01:00:01 type=agent] @Alice: hi there
|
|
1578
|
+
[target=dm:@richard msg=22222222 time=2026-03-15T01:00:02 type=human] @richard: hey, can you help?
|
|
1579
|
+
[target=#general:00000000 msg=33333333 time=2026-03-15T01:00:03 type=human] @richard: thread reply
|
|
1580
|
+
[target=dm:@richard:22222222 msg=44444444 time=2026-03-15T01:00:04 type=human] @richard: DM thread reply
|
|
1560
1581
|
\`\`\`
|
|
1561
1582
|
|
|
1583
|
+
Prompt examples use obvious placeholder IDs such as \`00000000\`, \`11111111\`,
|
|
1584
|
+
and \`22222222\`. They show the shape of a real message ID but are not actual
|
|
1585
|
+
messages. Do not cite them as evidence; use only IDs from messages you actually
|
|
1586
|
+
received or read.
|
|
1587
|
+
|
|
1562
1588
|
Header fields:
|
|
1563
1589
|
- \`target=\` \u2014 where the message came from. Reuse as the \`target\` parameter when replying.
|
|
1564
1590
|
- \`msg=\` \u2014 message short ID (first 8 chars of UUID). Use as thread suffix to start/reply in a thread.
|
|
@@ -1630,9 +1656,11 @@ Slock auto-renders these inline tokens as interactive links whenever they appear
|
|
|
1630
1656
|
|
|
1631
1657
|
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.
|
|
1632
1658
|
|
|
1659
|
+
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.
|
|
1660
|
+
|
|
1633
1661
|
### Formatting \u2014 URLs in non-English text
|
|
1634
1662
|
|
|
1635
|
-
When writing a URL next to non-ASCII punctuation (Chinese, Japanese, etc.),
|
|
1663
|
+
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.
|
|
1636
1664
|
|
|
1637
1665
|
- **Wrong**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1Ahttp://localhost:3000\uFF0C\u8BF7\u67E5\u770B\` (the \`\uFF0C\` gets swallowed into the link)
|
|
1638
1666
|
- **Correct**: \`\u6D4B\u8BD5\u73AF\u5883\uFF1A<http://localhost:3000>\uFF0C\u8BF7\u67E5\u770B\`
|
|
@@ -1712,12 +1740,12 @@ How to handle these:
|
|
|
1712
1740
|
- Call ${checkCmd} at the next safe breakpoint to materialize the pending messages before taking side-effect actions that depend on current context.
|
|
1713
1741
|
- If the new message is higher priority, pivot after reading it. If not, continue your current work.`;
|
|
1714
1742
|
} else {
|
|
1715
|
-
const notifyExample = isCli ? `\`[
|
|
1743
|
+
const notifyExample = isCli ? `\`[Slock inbox notice: You have N pending inbox message(s). Call slock message check to read them when you're ready.]\`` : `\`[Slock inbox notice: You have N pending inbox message(s). Call ${t("check_messages")} to read them when you're ready.]\``;
|
|
1716
1744
|
prompt += `
|
|
1717
1745
|
|
|
1718
1746
|
## Message Notifications
|
|
1719
1747
|
|
|
1720
|
-
While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens,
|
|
1748
|
+
While you are busy (executing tools, thinking, etc.), new messages may arrive. When this happens, the daemon may write a Slock inbox notice like:
|
|
1721
1749
|
|
|
1722
1750
|
${notifyExample}
|
|
1723
1751
|
|
|
@@ -2324,6 +2352,7 @@ function routeFamilyForPath(pathname) {
|
|
|
2324
2352
|
if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
|
|
2325
2353
|
if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
|
|
2326
2354
|
if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
|
|
2355
|
+
if (/^\/internal\/agent-api\/messages\/[^/]+\/resolve$/.test(pathname)) return "agent-api/messages/resolve";
|
|
2327
2356
|
if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
|
|
2328
2357
|
if (pathname === "/internal/agent-api/server") return "server";
|
|
2329
2358
|
if (pathname.startsWith("/internal/agent-api/history")) return "agent-api/events";
|
|
@@ -2744,7 +2773,7 @@ function shouldBufferJsonResponse(upstream, pathname, registration) {
|
|
|
2744
2773
|
if (!registration.inboxCoordinator) return false;
|
|
2745
2774
|
const contentType = upstream.headers.get("content-type") ?? "";
|
|
2746
2775
|
if (!contentType.includes("application/json")) return false;
|
|
2747
|
-
return pathname === "/internal/agent-api/send" || pathname === "/internal/agent-api/events" || pathname === "/internal/agent-api/history";
|
|
2776
|
+
return pathname === "/internal/agent-api/send" || pathname === "/internal/agent-api/events" || pathname === "/internal/agent-api/history" || /^\/internal\/agent-api\/messages\/[^/]+\/resolve$/.test(pathname);
|
|
2748
2777
|
}
|
|
2749
2778
|
function consumeVisibleResponse(registration, targetUrl, sendTarget, responseText) {
|
|
2750
2779
|
const coordinator = registration.inboxCoordinator;
|
|
@@ -3081,6 +3110,125 @@ function collectResultErrorDetail(message, fallback) {
|
|
|
3081
3110
|
function isProviderApiFailureText(value, hasToolUse) {
|
|
3082
3111
|
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));
|
|
3083
3112
|
}
|
|
3113
|
+
function finiteNumber(value) {
|
|
3114
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3115
|
+
}
|
|
3116
|
+
function finiteString(value) {
|
|
3117
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3118
|
+
}
|
|
3119
|
+
function withDefined(attrs) {
|
|
3120
|
+
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3121
|
+
}
|
|
3122
|
+
function collectNumericFields(value, fields) {
|
|
3123
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3124
|
+
const attrs = {};
|
|
3125
|
+
for (const [sourceKey, attrKey] of Object.entries(fields)) {
|
|
3126
|
+
const numberValue = finiteNumber(value[sourceKey]);
|
|
3127
|
+
if (numberValue !== void 0) attrs[attrKey] = numberValue;
|
|
3128
|
+
}
|
|
3129
|
+
return attrs;
|
|
3130
|
+
}
|
|
3131
|
+
function collectModelUsageAttrs(value) {
|
|
3132
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
3133
|
+
const sanitized = {};
|
|
3134
|
+
const aggregate = {};
|
|
3135
|
+
const knownNumericFields = [
|
|
3136
|
+
"inputTokens",
|
|
3137
|
+
"outputTokens",
|
|
3138
|
+
"cacheReadInputTokens",
|
|
3139
|
+
"cacheCreationInputTokens",
|
|
3140
|
+
"webSearchRequests",
|
|
3141
|
+
"costUSD",
|
|
3142
|
+
"contextWindow",
|
|
3143
|
+
"maxOutputTokens"
|
|
3144
|
+
];
|
|
3145
|
+
for (const [modelName, rawUsage] of Object.entries(value)) {
|
|
3146
|
+
if (!rawUsage || typeof rawUsage !== "object" || Array.isArray(rawUsage)) continue;
|
|
3147
|
+
const modelUsage = {};
|
|
3148
|
+
for (const field of knownNumericFields) {
|
|
3149
|
+
const numberValue = finiteNumber(rawUsage[field]);
|
|
3150
|
+
if (numberValue === void 0) continue;
|
|
3151
|
+
modelUsage[field] = numberValue;
|
|
3152
|
+
const aggregateKey = field === "costUSD" ? "costUsd" : field;
|
|
3153
|
+
if (field === "contextWindow" || field === "maxOutputTokens") {
|
|
3154
|
+
aggregate[aggregateKey] = Math.max(aggregate[aggregateKey] ?? 0, numberValue);
|
|
3155
|
+
} else {
|
|
3156
|
+
aggregate[aggregateKey] = (aggregate[aggregateKey] ?? 0) + numberValue;
|
|
3157
|
+
}
|
|
3158
|
+
}
|
|
3159
|
+
if (Object.keys(modelUsage).length > 0) sanitized[modelName] = modelUsage;
|
|
3160
|
+
}
|
|
3161
|
+
const modelNames = Object.keys(sanitized).sort();
|
|
3162
|
+
if (modelNames.length === 0) return {};
|
|
3163
|
+
const orderedSanitized = Object.fromEntries(modelNames.map((modelName) => [modelName, sanitized[modelName]]));
|
|
3164
|
+
return withDefined({
|
|
3165
|
+
modelUsageModelCount: modelNames.length,
|
|
3166
|
+
modelUsageModels: modelNames.join(","),
|
|
3167
|
+
modelUsageJson: JSON.stringify(orderedSanitized),
|
|
3168
|
+
modelUsageInputTokens: aggregate.inputTokens,
|
|
3169
|
+
modelUsageOutputTokens: aggregate.outputTokens,
|
|
3170
|
+
modelUsageCachedInputTokens: aggregate.cacheReadInputTokens,
|
|
3171
|
+
modelUsageCacheCreationInputTokens: aggregate.cacheCreationInputTokens,
|
|
3172
|
+
modelUsageWebSearchRequests: aggregate.webSearchRequests,
|
|
3173
|
+
modelUsageCostUsd: aggregate.costUsd,
|
|
3174
|
+
modelUsageMaxContextWindow: aggregate.contextWindow,
|
|
3175
|
+
modelUsageMaxOutputTokens: aggregate.maxOutputTokens
|
|
3176
|
+
});
|
|
3177
|
+
}
|
|
3178
|
+
function parseClaudeResultUsageTelemetry(event) {
|
|
3179
|
+
const usage = event.usage && typeof event.usage === "object" ? event.usage : void 0;
|
|
3180
|
+
const totalCostUsd = finiteNumber(event.total_cost_usd);
|
|
3181
|
+
const modelUsageAttrs = collectModelUsageAttrs(event.modelUsage);
|
|
3182
|
+
const hasTelemetrySource = usage !== void 0 || totalCostUsd !== void 0 || Object.keys(modelUsageAttrs).length > 0;
|
|
3183
|
+
if (!hasTelemetrySource) return null;
|
|
3184
|
+
const inputTokens = finiteNumber(usage?.input_tokens);
|
|
3185
|
+
const outputTokens = finiteNumber(usage?.output_tokens);
|
|
3186
|
+
const cachedInputTokens = finiteNumber(usage?.cache_read_input_tokens);
|
|
3187
|
+
const cacheCreationInputTokens = finiteNumber(usage?.cache_creation_input_tokens);
|
|
3188
|
+
const attrs = {
|
|
3189
|
+
...withDefined({
|
|
3190
|
+
totalCostUsd,
|
|
3191
|
+
durationMs: finiteNumber(event.duration_ms),
|
|
3192
|
+
durationApiMs: finiteNumber(event.duration_api_ms),
|
|
3193
|
+
numTurns: finiteNumber(event.num_turns),
|
|
3194
|
+
resultSubtype: finiteString(event.subtype),
|
|
3195
|
+
stopReason: finiteString(event.stop_reason),
|
|
3196
|
+
resultIsError: typeof event.is_error === "boolean" ? event.is_error : void 0,
|
|
3197
|
+
fastModeState: finiteString(event.fast_mode_state),
|
|
3198
|
+
permissionDenialsCount: Array.isArray(event.permission_denials) ? event.permission_denials.length : void 0,
|
|
3199
|
+
serviceTier: finiteString(usage?.service_tier),
|
|
3200
|
+
inferenceGeo: finiteString(usage?.inference_geo),
|
|
3201
|
+
usageSpeed: finiteString(usage?.speed),
|
|
3202
|
+
usageIterationsCount: Array.isArray(usage?.iterations) ? usage.iterations.length : void 0
|
|
3203
|
+
}),
|
|
3204
|
+
...collectNumericFields(usage?.server_tool_use, {
|
|
3205
|
+
web_search_requests: "serverToolUseWebSearchRequests",
|
|
3206
|
+
web_fetch_requests: "serverToolUseWebFetchRequests"
|
|
3207
|
+
}),
|
|
3208
|
+
...collectNumericFields(usage?.cache_creation, {
|
|
3209
|
+
ephemeral_1h_input_tokens: "cacheCreationEphemeral1hInputTokens",
|
|
3210
|
+
ephemeral_5m_input_tokens: "cacheCreationEphemeral5mInputTokens"
|
|
3211
|
+
}),
|
|
3212
|
+
...modelUsageAttrs
|
|
3213
|
+
};
|
|
3214
|
+
if (inputTokens !== void 0) attrs.inputTokens = inputTokens;
|
|
3215
|
+
if (outputTokens !== void 0) attrs.outputTokens = outputTokens;
|
|
3216
|
+
if (cachedInputTokens !== void 0) attrs.cachedInputTokens = cachedInputTokens;
|
|
3217
|
+
if (cacheCreationInputTokens !== void 0) attrs.cacheCreationInputTokens = cacheCreationInputTokens;
|
|
3218
|
+
const tokenComponents = [inputTokens, outputTokens, cachedInputTokens, cacheCreationInputTokens];
|
|
3219
|
+
const totalTokens = tokenComponents.reduce((sum, value) => sum + (value ?? 0), 0);
|
|
3220
|
+
if (tokenComponents.some((value) => value !== void 0)) attrs.totalTokens = totalTokens;
|
|
3221
|
+
if (Object.keys(attrs).length === 0) return null;
|
|
3222
|
+
return {
|
|
3223
|
+
kind: "telemetry",
|
|
3224
|
+
name: "token_usage",
|
|
3225
|
+
source: "claude_result_usage",
|
|
3226
|
+
usageKind: "per_turn",
|
|
3227
|
+
...typeof event.session_id === "string" && event.session_id ? { sessionId: event.session_id } : {},
|
|
3228
|
+
...typeof event.uuid === "string" && event.uuid ? { runtimeResultId: event.uuid } : {},
|
|
3229
|
+
attrs
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3084
3232
|
var ClaudeEventNormalizer = class {
|
|
3085
3233
|
normalizeLine(line) {
|
|
3086
3234
|
let event;
|
|
@@ -3155,6 +3303,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3155
3303
|
case "result": {
|
|
3156
3304
|
const subtype = typeof event.subtype === "string" ? event.subtype : "success";
|
|
3157
3305
|
const stopReason = typeof event.stop_reason === "string" ? event.stop_reason : null;
|
|
3306
|
+
const usageTelemetry = parseClaudeResultUsageTelemetry(event);
|
|
3158
3307
|
switch (subtype) {
|
|
3159
3308
|
case "success":
|
|
3160
3309
|
if (event.is_error && stopReason !== "max_tokens") {
|
|
@@ -3176,6 +3325,7 @@ var ClaudeEventNormalizer = class {
|
|
|
3176
3325
|
pushResultError(event, "Structured output retries exceeded");
|
|
3177
3326
|
break;
|
|
3178
3327
|
}
|
|
3328
|
+
if (usageTelemetry) events.push(usageTelemetry);
|
|
3179
3329
|
events.push({ kind: "turn_end", sessionId: event.session_id });
|
|
3180
3330
|
break;
|
|
3181
3331
|
}
|
|
@@ -3441,7 +3591,7 @@ function buildClaudeArgs(config, opts) {
|
|
|
3441
3591
|
args.push("--effort", launchRuntimeFields.reasoningEffort);
|
|
3442
3592
|
}
|
|
3443
3593
|
if (launchRuntimeFields.mode.kind === "fast") {
|
|
3444
|
-
args.push("--
|
|
3594
|
+
args.push("--settings", JSON.stringify({ fastMode: true }));
|
|
3445
3595
|
}
|
|
3446
3596
|
if (config.sessionId) {
|
|
3447
3597
|
args.push("--resume", config.sessionId);
|
|
@@ -3599,53 +3749,64 @@ var RuntimeTurnState = class {
|
|
|
3599
3749
|
};
|
|
3600
3750
|
|
|
3601
3751
|
// src/drivers/codexTelemetrySidecar.ts
|
|
3602
|
-
function
|
|
3752
|
+
function finiteNumber2(value) {
|
|
3603
3753
|
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
3604
3754
|
}
|
|
3605
|
-
function
|
|
3755
|
+
function finiteString2(value) {
|
|
3606
3756
|
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
3607
3757
|
}
|
|
3608
3758
|
function ratio(numerator, denominator) {
|
|
3609
3759
|
if (numerator === void 0 || denominator === void 0 || denominator <= 0) return void 0;
|
|
3610
3760
|
return Number((numerator / denominator).toFixed(6));
|
|
3611
3761
|
}
|
|
3612
|
-
function
|
|
3762
|
+
function withDefined2(attrs) {
|
|
3613
3763
|
return Object.fromEntries(Object.entries(attrs).filter(([, value]) => value !== void 0));
|
|
3614
3764
|
}
|
|
3615
3765
|
function parseTokenUsageTelemetry(message) {
|
|
3616
3766
|
const usage = message.params?.tokenUsage;
|
|
3617
3767
|
const total = usage?.total;
|
|
3618
3768
|
if (!total || typeof total !== "object") return null;
|
|
3619
|
-
const inputTokens =
|
|
3620
|
-
const cachedInputTokens =
|
|
3621
|
-
const totalTokens =
|
|
3622
|
-
const modelContextWindow =
|
|
3623
|
-
const attrs =
|
|
3769
|
+
const inputTokens = finiteNumber2(total.inputTokens);
|
|
3770
|
+
const cachedInputTokens = finiteNumber2(total.cachedInputTokens);
|
|
3771
|
+
const totalTokens = finiteNumber2(total.totalTokens);
|
|
3772
|
+
const modelContextWindow = finiteNumber2(usage.modelContextWindow);
|
|
3773
|
+
const attrs = withDefined2({
|
|
3624
3774
|
totalTokens,
|
|
3625
3775
|
inputTokens,
|
|
3626
3776
|
cachedInputTokens,
|
|
3627
|
-
outputTokens:
|
|
3628
|
-
reasoningOutputTokens:
|
|
3777
|
+
outputTokens: finiteNumber2(total.outputTokens),
|
|
3778
|
+
reasoningOutputTokens: finiteNumber2(total.reasoningOutputTokens),
|
|
3629
3779
|
modelContextWindow,
|
|
3630
3780
|
cachedInputRatio: ratio(cachedInputTokens, inputTokens),
|
|
3631
3781
|
contextUtilization: ratio(totalTokens, modelContextWindow)
|
|
3632
3782
|
});
|
|
3633
3783
|
if (Object.keys(attrs).length === 0) return null;
|
|
3634
|
-
return {
|
|
3784
|
+
return {
|
|
3785
|
+
kind: "telemetry",
|
|
3786
|
+
name: "token_usage",
|
|
3787
|
+
source: "codex_thread_token_usage_updated",
|
|
3788
|
+
usageKind: "cumulative_session",
|
|
3789
|
+
attrs
|
|
3790
|
+
};
|
|
3635
3791
|
}
|
|
3636
3792
|
function parseRateLimitTelemetry(message) {
|
|
3637
3793
|
const rateLimits = message.params?.rateLimits;
|
|
3638
3794
|
const primary = rateLimits?.primary;
|
|
3639
3795
|
if (!rateLimits || typeof rateLimits !== "object" || !primary || typeof primary !== "object") return null;
|
|
3640
|
-
const attrs =
|
|
3641
|
-
limitId:
|
|
3642
|
-
planType:
|
|
3643
|
-
usedPercent:
|
|
3644
|
-
windowDurationMins:
|
|
3645
|
-
resetsAt:
|
|
3796
|
+
const attrs = withDefined2({
|
|
3797
|
+
limitId: finiteString2(rateLimits.limitId),
|
|
3798
|
+
planType: finiteString2(rateLimits.planType),
|
|
3799
|
+
usedPercent: finiteNumber2(primary.usedPercent),
|
|
3800
|
+
windowDurationMins: finiteNumber2(primary.windowDurationMins),
|
|
3801
|
+
resetsAt: finiteNumber2(primary.resetsAt)
|
|
3646
3802
|
});
|
|
3647
3803
|
if (Object.keys(attrs).length === 0) return null;
|
|
3648
|
-
return {
|
|
3804
|
+
return {
|
|
3805
|
+
kind: "telemetry",
|
|
3806
|
+
name: "rate_limits",
|
|
3807
|
+
source: "codex_account_rate_limits_updated",
|
|
3808
|
+
attrs
|
|
3809
|
+
};
|
|
3649
3810
|
}
|
|
3650
3811
|
function parseCodexTelemetryEvent(message) {
|
|
3651
3812
|
switch (message.method) {
|
|
@@ -3752,7 +3913,11 @@ var CodexEventNormalizer = class {
|
|
|
3752
3913
|
}
|
|
3753
3914
|
const telemetry = parseCodexTelemetryEvent(message);
|
|
3754
3915
|
if (telemetry) {
|
|
3755
|
-
events.push(
|
|
3916
|
+
events.push({
|
|
3917
|
+
...telemetry,
|
|
3918
|
+
...this.currentThreadId ? { sessionId: this.currentThreadId } : {},
|
|
3919
|
+
...this.turnState.activeTurnId ? { turnId: this.turnState.activeTurnId } : {}
|
|
3920
|
+
});
|
|
3756
3921
|
return { events };
|
|
3757
3922
|
}
|
|
3758
3923
|
const rawProgress = rawResponseItemProgressEvent(message);
|
|
@@ -5794,6 +5959,277 @@ var OpenCodeDriver = class {
|
|
|
5794
5959
|
}
|
|
5795
5960
|
};
|
|
5796
5961
|
|
|
5962
|
+
// src/drivers/pi.ts
|
|
5963
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
5964
|
+
import { spawn as spawn9, spawnSync as spawnSync3 } from "child_process";
|
|
5965
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
5966
|
+
import path11 from "path";
|
|
5967
|
+
var PI_SESSION_DIR = ".pi-sessions";
|
|
5968
|
+
var PI_PROVIDER_LABELS = {
|
|
5969
|
+
deepseek: "DeepSeek",
|
|
5970
|
+
google: "Google",
|
|
5971
|
+
openai: "OpenAI",
|
|
5972
|
+
openrouter: "OpenRouter"
|
|
5973
|
+
};
|
|
5974
|
+
function buildPiSessionDir(workingDirectory) {
|
|
5975
|
+
return path11.join(workingDirectory, PI_SESSION_DIR);
|
|
5976
|
+
}
|
|
5977
|
+
function buildPiRpcArgs(ctx, sessionId) {
|
|
5978
|
+
const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
|
|
5979
|
+
const args = [
|
|
5980
|
+
"--mode",
|
|
5981
|
+
"rpc",
|
|
5982
|
+
"--session-dir",
|
|
5983
|
+
buildPiSessionDir(ctx.workingDirectory),
|
|
5984
|
+
"--system-prompt",
|
|
5985
|
+
ctx.standingPrompt
|
|
5986
|
+
];
|
|
5987
|
+
if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
|
|
5988
|
+
args.push("--model", launchRuntimeFields.model);
|
|
5989
|
+
}
|
|
5990
|
+
if (launchRuntimeFields.reasoningEffort) {
|
|
5991
|
+
args.push("--thinking", launchRuntimeFields.reasoningEffort);
|
|
5992
|
+
}
|
|
5993
|
+
if (sessionId) {
|
|
5994
|
+
args.push("--session-id", sessionId);
|
|
5995
|
+
}
|
|
5996
|
+
return args;
|
|
5997
|
+
}
|
|
5998
|
+
async function buildPiSpawnEnv(ctx) {
|
|
5999
|
+
return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
|
|
6000
|
+
}
|
|
6001
|
+
function parsePiModelsOutput(output) {
|
|
6002
|
+
const stripAnsi = (value) => value.replace(/\u001b\[[0-9;]*m/g, "");
|
|
6003
|
+
const models = [];
|
|
6004
|
+
const seen = /* @__PURE__ */ new Set();
|
|
6005
|
+
for (const rawLine of stripAnsi(output).split(/\r?\n/)) {
|
|
6006
|
+
const line = rawLine.trim();
|
|
6007
|
+
if (!line || /^provider\s+model\s+/i.test(line) || /^[-\s]+$/.test(line)) continue;
|
|
6008
|
+
const columns = line.split(/\s+/);
|
|
6009
|
+
const provider = columns[0];
|
|
6010
|
+
const model = columns[1];
|
|
6011
|
+
if (!provider || !model || provider.startsWith("-") || model.startsWith("-")) continue;
|
|
6012
|
+
if (/^(yes|no)$/i.test(model)) continue;
|
|
6013
|
+
const id = `${provider}/${model}`;
|
|
6014
|
+
if (seen.has(id)) continue;
|
|
6015
|
+
seen.add(id);
|
|
6016
|
+
models.push({
|
|
6017
|
+
id,
|
|
6018
|
+
label: `${humanizePiSegment(model)} \xB7 ${PI_PROVIDER_LABELS[provider] || humanizePiSegment(provider)}`,
|
|
6019
|
+
verified: "launchable"
|
|
6020
|
+
});
|
|
6021
|
+
}
|
|
6022
|
+
return models.length > 0 ? { models } : null;
|
|
6023
|
+
}
|
|
6024
|
+
function detectPiModels(runCommand = runPiModelsCommand) {
|
|
6025
|
+
const result = runCommand();
|
|
6026
|
+
if (result.error || result.status !== 0) return null;
|
|
6027
|
+
return parsePiModelsOutput(result.stdout);
|
|
6028
|
+
}
|
|
6029
|
+
function runPiModelsCommand() {
|
|
6030
|
+
const result = spawnSync3("pi", ["--list-models"], {
|
|
6031
|
+
env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
|
|
6032
|
+
encoding: "utf8",
|
|
6033
|
+
timeout: 5e3
|
|
6034
|
+
});
|
|
6035
|
+
return {
|
|
6036
|
+
status: result.status,
|
|
6037
|
+
stdout: String(result.stdout || ""),
|
|
6038
|
+
error: result.error
|
|
6039
|
+
};
|
|
6040
|
+
}
|
|
6041
|
+
function humanizePiSegment(value) {
|
|
6042
|
+
return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
|
|
6043
|
+
}
|
|
6044
|
+
function formatPiLabelToken(token) {
|
|
6045
|
+
const normalized = token.toLowerCase();
|
|
6046
|
+
const specialCases = {
|
|
6047
|
+
ai: "AI",
|
|
6048
|
+
api: "API",
|
|
6049
|
+
deepseek: "DeepSeek",
|
|
6050
|
+
flash: "Flash",
|
|
6051
|
+
gpt: "GPT",
|
|
6052
|
+
pro: "Pro",
|
|
6053
|
+
v4: "V4"
|
|
6054
|
+
};
|
|
6055
|
+
if (specialCases[normalized]) return specialCases[normalized];
|
|
6056
|
+
if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
|
|
6057
|
+
if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
|
|
6058
|
+
if (/^\d/.test(token)) return token;
|
|
6059
|
+
return normalized.charAt(0).toUpperCase() + normalized.slice(1);
|
|
6060
|
+
}
|
|
6061
|
+
function piErrorMessage(error) {
|
|
6062
|
+
if (typeof error === "string" && error.trim()) return error.trim();
|
|
6063
|
+
if (error && typeof error === "object") {
|
|
6064
|
+
const record = error;
|
|
6065
|
+
if (typeof record.message === "string" && record.message.trim()) return record.message.trim();
|
|
6066
|
+
try {
|
|
6067
|
+
return JSON.stringify(error);
|
|
6068
|
+
} catch {
|
|
6069
|
+
}
|
|
6070
|
+
}
|
|
6071
|
+
return "Unknown Pi error";
|
|
6072
|
+
}
|
|
6073
|
+
var PiDriver = class {
|
|
6074
|
+
id = "pi";
|
|
6075
|
+
supportsNativeStandingPrompt = true;
|
|
6076
|
+
lifecycle = {
|
|
6077
|
+
kind: "persistent",
|
|
6078
|
+
stdin: "direct",
|
|
6079
|
+
inFlightWake: "steer"
|
|
6080
|
+
};
|
|
6081
|
+
communication = {
|
|
6082
|
+
chat: "slock_cli",
|
|
6083
|
+
runtimeControl: "none"
|
|
6084
|
+
};
|
|
6085
|
+
session = {
|
|
6086
|
+
recovery: "resume_or_fresh"
|
|
6087
|
+
};
|
|
6088
|
+
model = {
|
|
6089
|
+
detectedModelsVerifiedAs: "launchable",
|
|
6090
|
+
toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
|
|
6091
|
+
};
|
|
6092
|
+
supportsStdinNotification = true;
|
|
6093
|
+
mcpToolPrefix = "";
|
|
6094
|
+
busyDeliveryMode = "direct";
|
|
6095
|
+
usesSlockCliForCommunication = true;
|
|
6096
|
+
sessionId = null;
|
|
6097
|
+
sessionAnnounced = false;
|
|
6098
|
+
sawTextDelta = false;
|
|
6099
|
+
requestId = 0;
|
|
6100
|
+
process = null;
|
|
6101
|
+
probe() {
|
|
6102
|
+
const command = resolveCommandOnPath("pi");
|
|
6103
|
+
if (!command) return { available: false };
|
|
6104
|
+
return {
|
|
6105
|
+
available: true,
|
|
6106
|
+
version: readCommandVersion(command) ?? void 0
|
|
6107
|
+
};
|
|
6108
|
+
}
|
|
6109
|
+
async detectModels() {
|
|
6110
|
+
return detectPiModels();
|
|
6111
|
+
}
|
|
6112
|
+
async spawn(ctx) {
|
|
6113
|
+
this.sessionId = ctx.config.sessionId || randomUUID3();
|
|
6114
|
+
this.sessionAnnounced = false;
|
|
6115
|
+
this.sawTextDelta = false;
|
|
6116
|
+
this.requestId = 0;
|
|
6117
|
+
mkdirSync4(buildPiSessionDir(ctx.workingDirectory), { recursive: true });
|
|
6118
|
+
const proc = spawn9(resolveCommandOnPath("pi") ?? "pi", buildPiRpcArgs(ctx, this.sessionId), {
|
|
6119
|
+
cwd: ctx.workingDirectory,
|
|
6120
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
6121
|
+
env: await buildPiSpawnEnv(ctx),
|
|
6122
|
+
shell: process.platform === "win32"
|
|
6123
|
+
});
|
|
6124
|
+
this.process = proc;
|
|
6125
|
+
this.sendRpcCommand("prompt", { message: ctx.prompt });
|
|
6126
|
+
return { process: proc };
|
|
6127
|
+
}
|
|
6128
|
+
parseLine(line) {
|
|
6129
|
+
let event;
|
|
6130
|
+
try {
|
|
6131
|
+
event = JSON.parse(line);
|
|
6132
|
+
} catch {
|
|
6133
|
+
return [];
|
|
6134
|
+
}
|
|
6135
|
+
const events = [];
|
|
6136
|
+
if (event.type === "session" && event.id) {
|
|
6137
|
+
this.sessionId = event.id;
|
|
6138
|
+
if (!this.sessionAnnounced) {
|
|
6139
|
+
this.sessionAnnounced = true;
|
|
6140
|
+
events.push({ kind: "session_init", sessionId: event.id });
|
|
6141
|
+
}
|
|
6142
|
+
return events;
|
|
6143
|
+
}
|
|
6144
|
+
if (!this.sessionAnnounced && this.sessionId) {
|
|
6145
|
+
events.push({ kind: "session_init", sessionId: this.sessionId });
|
|
6146
|
+
this.sessionAnnounced = true;
|
|
6147
|
+
}
|
|
6148
|
+
if (event.type === "response") {
|
|
6149
|
+
if (event.data?.sessionId && event.data.sessionId !== this.sessionId) {
|
|
6150
|
+
this.sessionId = event.data.sessionId;
|
|
6151
|
+
}
|
|
6152
|
+
if (event.success === false) {
|
|
6153
|
+
events.push({ kind: "error", message: piErrorMessage(event.error) });
|
|
6154
|
+
}
|
|
6155
|
+
return events;
|
|
6156
|
+
}
|
|
6157
|
+
if (event.type === "message_start" && event.message?.role === "assistant") {
|
|
6158
|
+
this.sawTextDelta = false;
|
|
6159
|
+
return events;
|
|
6160
|
+
}
|
|
6161
|
+
const assistantEvent = event.assistantMessageEvent;
|
|
6162
|
+
if (event.type === "message_update" && assistantEvent) {
|
|
6163
|
+
switch (assistantEvent.type) {
|
|
6164
|
+
case "thinking_delta":
|
|
6165
|
+
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6166
|
+
events.push({ kind: "thinking", text: assistantEvent.delta });
|
|
6167
|
+
}
|
|
6168
|
+
break;
|
|
6169
|
+
case "text_delta":
|
|
6170
|
+
if (typeof assistantEvent.delta === "string" && assistantEvent.delta.length > 0) {
|
|
6171
|
+
this.sawTextDelta = true;
|
|
6172
|
+
events.push({ kind: "text", text: assistantEvent.delta });
|
|
6173
|
+
}
|
|
6174
|
+
break;
|
|
6175
|
+
case "thinking_start":
|
|
6176
|
+
case "text_start":
|
|
6177
|
+
break;
|
|
6178
|
+
case "tool_use":
|
|
6179
|
+
case "tool_call":
|
|
6180
|
+
case "tool_start":
|
|
6181
|
+
events.push({
|
|
6182
|
+
kind: "tool_call",
|
|
6183
|
+
name: assistantEvent.name || assistantEvent.toolName || "unknown_tool",
|
|
6184
|
+
input: assistantEvent.input ?? assistantEvent.parameters ?? {}
|
|
6185
|
+
});
|
|
6186
|
+
break;
|
|
6187
|
+
case "text_end":
|
|
6188
|
+
if (!this.sawTextDelta && typeof assistantEvent.content === "string" && assistantEvent.content.length > 0) {
|
|
6189
|
+
events.push({ kind: "text", text: assistantEvent.content });
|
|
6190
|
+
}
|
|
6191
|
+
break;
|
|
6192
|
+
}
|
|
6193
|
+
return events;
|
|
6194
|
+
}
|
|
6195
|
+
if (event.type === "agent_end") {
|
|
6196
|
+
events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
|
|
6197
|
+
} else if (event.type === "error") {
|
|
6198
|
+
events.push({ kind: "error", message: piErrorMessage(event.error ?? event.message) });
|
|
6199
|
+
}
|
|
6200
|
+
return events;
|
|
6201
|
+
}
|
|
6202
|
+
encodeStdinMessage(text, _sessionId, opts) {
|
|
6203
|
+
return JSON.stringify({
|
|
6204
|
+
id: this.nextRequestId(),
|
|
6205
|
+
type: opts?.mode === "idle" ? "prompt" : "steer",
|
|
6206
|
+
message: text
|
|
6207
|
+
});
|
|
6208
|
+
}
|
|
6209
|
+
buildSystemPrompt(config, _agentId) {
|
|
6210
|
+
return buildCliTransportSystemPrompt(config, {
|
|
6211
|
+
toolPrefix: "",
|
|
6212
|
+
extraCriticalRules: [],
|
|
6213
|
+
postStartupNotes: [
|
|
6214
|
+
"**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."
|
|
6215
|
+
],
|
|
6216
|
+
includeStdinNotificationSection: true,
|
|
6217
|
+
messageNotificationStyle: "direct"
|
|
6218
|
+
});
|
|
6219
|
+
}
|
|
6220
|
+
nextRequestId() {
|
|
6221
|
+
this.requestId += 1;
|
|
6222
|
+
return String(this.requestId);
|
|
6223
|
+
}
|
|
6224
|
+
sendRpcCommand(type, params) {
|
|
6225
|
+
this.process?.stdin?.write(JSON.stringify({
|
|
6226
|
+
id: this.nextRequestId(),
|
|
6227
|
+
type,
|
|
6228
|
+
...params
|
|
6229
|
+
}) + "\n");
|
|
6230
|
+
}
|
|
6231
|
+
};
|
|
6232
|
+
|
|
5797
6233
|
// src/drivers/index.ts
|
|
5798
6234
|
var driverFactories = {
|
|
5799
6235
|
claude: () => new ClaudeDriver(),
|
|
@@ -5803,7 +6239,8 @@ var driverFactories = {
|
|
|
5803
6239
|
cursor: () => new CursorDriver(),
|
|
5804
6240
|
gemini: () => new GeminiDriver(),
|
|
5805
6241
|
kimi: () => new KimiDriver(),
|
|
5806
|
-
opencode: () => new OpenCodeDriver()
|
|
6242
|
+
opencode: () => new OpenCodeDriver(),
|
|
6243
|
+
pi: () => new PiDriver()
|
|
5807
6244
|
};
|
|
5808
6245
|
function getDriver(runtimeId) {
|
|
5809
6246
|
const createDriver = driverFactories[runtimeId];
|
|
@@ -5816,7 +6253,7 @@ function getDriver(runtimeId) {
|
|
|
5816
6253
|
|
|
5817
6254
|
// src/workspaces.ts
|
|
5818
6255
|
import { readdir, rm, stat } from "fs/promises";
|
|
5819
|
-
import
|
|
6256
|
+
import path12 from "path";
|
|
5820
6257
|
function isValidWorkspaceDirectoryName(directoryName) {
|
|
5821
6258
|
return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
|
|
5822
6259
|
}
|
|
@@ -5824,7 +6261,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
|
|
|
5824
6261
|
if (!isValidWorkspaceDirectoryName(directoryName)) {
|
|
5825
6262
|
return null;
|
|
5826
6263
|
}
|
|
5827
|
-
return
|
|
6264
|
+
return path12.join(dataDir, directoryName);
|
|
5828
6265
|
}
|
|
5829
6266
|
function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
|
|
5830
6267
|
return {
|
|
@@ -5873,7 +6310,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
|
|
|
5873
6310
|
return summary;
|
|
5874
6311
|
}
|
|
5875
6312
|
const childSummaries = await Promise.all(
|
|
5876
|
-
entries.map((entry) => summarizeWorkspaceEntry(
|
|
6313
|
+
entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
|
|
5877
6314
|
);
|
|
5878
6315
|
for (const childSummary of childSummaries) {
|
|
5879
6316
|
summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
|
|
@@ -5892,7 +6329,7 @@ async function scanWorkspaceDirectories(dataDir) {
|
|
|
5892
6329
|
if (!entry.isDirectory()) {
|
|
5893
6330
|
return null;
|
|
5894
6331
|
}
|
|
5895
|
-
const dirPath =
|
|
6332
|
+
const dirPath = path12.join(dataDir, entry.name);
|
|
5896
6333
|
try {
|
|
5897
6334
|
const summary = await summarizeWorkspaceDirectory(dirPath);
|
|
5898
6335
|
return {
|
|
@@ -6068,6 +6505,8 @@ function runtimeDisplayName(runtimeId) {
|
|
|
6068
6505
|
return "Kimi CLI";
|
|
6069
6506
|
case "opencode":
|
|
6070
6507
|
return "OpenCode";
|
|
6508
|
+
case "pi":
|
|
6509
|
+
return "Pi CLI";
|
|
6071
6510
|
default:
|
|
6072
6511
|
return runtimeId || "This runtime";
|
|
6073
6512
|
}
|
|
@@ -6332,12 +6771,12 @@ function findSessionJsonl(root, predicate) {
|
|
|
6332
6771
|
for (const entry of entries) {
|
|
6333
6772
|
if (++visited > maxEntries) return null;
|
|
6334
6773
|
if (!entry.isFile() || !predicate(entry.name)) continue;
|
|
6335
|
-
return
|
|
6774
|
+
return path13.join(dir, entry.name);
|
|
6336
6775
|
}
|
|
6337
6776
|
for (const entry of entries) {
|
|
6338
6777
|
if (++visited > maxEntries) return null;
|
|
6339
6778
|
if (!entry.isDirectory()) continue;
|
|
6340
|
-
const found = visit(
|
|
6779
|
+
const found = visit(path13.join(dir, entry.name), depth - 1);
|
|
6341
6780
|
if (found) return found;
|
|
6342
6781
|
}
|
|
6343
6782
|
return null;
|
|
@@ -6350,9 +6789,9 @@ function safeSessionFilename(value) {
|
|
|
6350
6789
|
}
|
|
6351
6790
|
function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
6352
6791
|
try {
|
|
6353
|
-
const dir =
|
|
6354
|
-
|
|
6355
|
-
const filePath =
|
|
6792
|
+
const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
|
|
6793
|
+
mkdirSync5(dir, { recursive: true });
|
|
6794
|
+
const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
|
|
6356
6795
|
writeFileSync7(filePath, JSON.stringify({
|
|
6357
6796
|
type: "runtime_session_handoff",
|
|
6358
6797
|
runtime,
|
|
@@ -6372,7 +6811,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
|
|
|
6372
6811
|
}
|
|
6373
6812
|
}
|
|
6374
6813
|
function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), fallbackDir) {
|
|
6375
|
-
const directPath =
|
|
6814
|
+
const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
|
|
6376
6815
|
if (directPath) {
|
|
6377
6816
|
try {
|
|
6378
6817
|
if (statSync(directPath).isFile()) {
|
|
@@ -6381,7 +6820,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os5.homedir(), f
|
|
|
6381
6820
|
} catch {
|
|
6382
6821
|
}
|
|
6383
6822
|
}
|
|
6384
|
-
const resolvedPath = runtime === "claude" ? findSessionJsonl(
|
|
6823
|
+
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;
|
|
6385
6824
|
if (!resolvedPath && fallbackDir) {
|
|
6386
6825
|
const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
|
|
6387
6826
|
if (fallback) return fallback;
|
|
@@ -6470,7 +6909,7 @@ function dynamicClaimInstruction(driver) {
|
|
|
6470
6909
|
}
|
|
6471
6910
|
function formatIncomingMessage(message, driver) {
|
|
6472
6911
|
const threadJoinPrefix = message.thread_join_context ? [
|
|
6473
|
-
`[
|
|
6912
|
+
`[Slock thread context: you were added to a new thread via @mention.]`,
|
|
6474
6913
|
`parent: ${message.thread_join_context.parent_target}`,
|
|
6475
6914
|
`thread: ${message.thread_join_context.thread_target}`,
|
|
6476
6915
|
`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}")`}`,
|
|
@@ -7296,7 +7735,7 @@ function getBusyDeliveryNote(driver) {
|
|
|
7296
7735
|
if (driver.busyDeliveryMode === "gated") {
|
|
7297
7736
|
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.";
|
|
7298
7737
|
}
|
|
7299
|
-
return "\n\nNote: While you are busy, you may receive [
|
|
7738
|
+
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.";
|
|
7300
7739
|
}
|
|
7301
7740
|
var NATIVE_STANDING_PROMPT_STARTUP_INPUT = (
|
|
7302
7741
|
// Claude Code 2.1.114 treats "follow your system prompt" style user turns as
|
|
@@ -7805,7 +8244,7 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7805
8244
|
);
|
|
7806
8245
|
wakeMessage = void 0;
|
|
7807
8246
|
}
|
|
7808
|
-
const agentDataDir =
|
|
8247
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
7809
8248
|
await mkdir(agentDataDir, { recursive: true });
|
|
7810
8249
|
let runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
|
|
7811
8250
|
const legacyRuntimeProfileControl = runtimeConfig.runtimeProfileControl?.kind === "migration" ? runtimeConfig.runtimeProfileControl : null;
|
|
@@ -7819,23 +8258,23 @@ var AgentProcessManager = class _AgentProcessManager {
|
|
|
7819
8258
|
);
|
|
7820
8259
|
runtimeConfig = { ...runtimeConfig, runtimeProfileControl: null };
|
|
7821
8260
|
}
|
|
7822
|
-
const memoryMdPath =
|
|
8261
|
+
const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
|
|
7823
8262
|
try {
|
|
7824
8263
|
await access(memoryMdPath);
|
|
7825
8264
|
} catch {
|
|
7826
8265
|
const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
|
|
7827
8266
|
await writeFile(memoryMdPath, initialMemoryMd);
|
|
7828
8267
|
}
|
|
7829
|
-
const notesDir =
|
|
8268
|
+
const notesDir = path13.join(agentDataDir, "notes");
|
|
7830
8269
|
await mkdir(notesDir, { recursive: true });
|
|
7831
8270
|
if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
|
|
7832
8271
|
const seedFiles = buildOnboardingSeedFiles();
|
|
7833
8272
|
for (const { relativePath, content } of seedFiles) {
|
|
7834
|
-
const fullPath =
|
|
8273
|
+
const fullPath = path13.join(agentDataDir, relativePath);
|
|
7835
8274
|
try {
|
|
7836
8275
|
await access(fullPath);
|
|
7837
8276
|
} catch {
|
|
7838
|
-
await mkdir(
|
|
8277
|
+
await mkdir(path13.dirname(fullPath), { recursive: true });
|
|
7839
8278
|
await writeFile(fullPath, content);
|
|
7840
8279
|
}
|
|
7841
8280
|
}
|
|
@@ -8707,7 +9146,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8707
9146
|
return true;
|
|
8708
9147
|
}
|
|
8709
9148
|
async resetWorkspace(agentId) {
|
|
8710
|
-
const agentDataDir =
|
|
9149
|
+
const agentDataDir = path13.join(this.dataDir, agentId);
|
|
8711
9150
|
try {
|
|
8712
9151
|
await rm2(agentDataDir, { recursive: true, force: true });
|
|
8713
9152
|
logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
|
|
@@ -8768,7 +9207,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
8768
9207
|
return result;
|
|
8769
9208
|
}
|
|
8770
9209
|
buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
|
|
8771
|
-
const workspacePath =
|
|
9210
|
+
const workspacePath = path13.join(this.dataDir, agentId);
|
|
8772
9211
|
return {
|
|
8773
9212
|
agentId,
|
|
8774
9213
|
launchId,
|
|
@@ -9025,7 +9464,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9025
9464
|
}
|
|
9026
9465
|
// Workspace file browsing
|
|
9027
9466
|
async getFileTree(agentId, dirPath) {
|
|
9028
|
-
const agentDir =
|
|
9467
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
9029
9468
|
try {
|
|
9030
9469
|
await stat2(agentDir);
|
|
9031
9470
|
} catch {
|
|
@@ -9033,8 +9472,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9033
9472
|
}
|
|
9034
9473
|
let targetDir = agentDir;
|
|
9035
9474
|
if (dirPath) {
|
|
9036
|
-
const resolved =
|
|
9037
|
-
if (!resolved.startsWith(agentDir +
|
|
9475
|
+
const resolved = path13.resolve(agentDir, dirPath);
|
|
9476
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
9038
9477
|
return [];
|
|
9039
9478
|
}
|
|
9040
9479
|
targetDir = resolved;
|
|
@@ -9042,14 +9481,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9042
9481
|
return this.listDirectoryChildren(targetDir, agentDir);
|
|
9043
9482
|
}
|
|
9044
9483
|
async readFile(agentId, filePath) {
|
|
9045
|
-
const agentDir =
|
|
9046
|
-
const resolved =
|
|
9047
|
-
if (!resolved.startsWith(agentDir +
|
|
9484
|
+
const agentDir = path13.join(this.dataDir, agentId);
|
|
9485
|
+
const resolved = path13.resolve(agentDir, filePath);
|
|
9486
|
+
if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
|
|
9048
9487
|
throw new Error("Access denied");
|
|
9049
9488
|
}
|
|
9050
9489
|
const info = await stat2(resolved);
|
|
9051
9490
|
if (info.isDirectory()) throw new Error("Cannot read a directory");
|
|
9052
|
-
const ext =
|
|
9491
|
+
const ext = path13.extname(resolved).toLowerCase();
|
|
9053
9492
|
if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
|
|
9054
9493
|
if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
|
|
9055
9494
|
const content = await readFile(resolved, "utf-8");
|
|
@@ -9084,13 +9523,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9084
9523
|
const agent = this.agents.get(agentId);
|
|
9085
9524
|
const runtime = runtimeHint || agent?.config.runtime || "claude";
|
|
9086
9525
|
const home = os5.homedir();
|
|
9087
|
-
const workspaceDir =
|
|
9526
|
+
const workspaceDir = path13.join(this.dataDir, agentId);
|
|
9088
9527
|
const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
|
|
9089
9528
|
const globalResults = await Promise.all(
|
|
9090
|
-
paths.global.map((p) => this.scanSkillsDir(
|
|
9529
|
+
paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
|
|
9091
9530
|
);
|
|
9092
9531
|
const workspaceResults = await Promise.all(
|
|
9093
|
-
paths.workspace.map((p) => this.scanSkillsDir(
|
|
9532
|
+
paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
|
|
9094
9533
|
);
|
|
9095
9534
|
const dedup = (skills) => {
|
|
9096
9535
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -9119,7 +9558,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9119
9558
|
const skills = [];
|
|
9120
9559
|
for (const entry of entries) {
|
|
9121
9560
|
if (entry.isDirectory() || entry.isSymbolicLink()) {
|
|
9122
|
-
const skillMd =
|
|
9561
|
+
const skillMd = path13.join(dir, entry.name, "SKILL.md");
|
|
9123
9562
|
try {
|
|
9124
9563
|
const content = await readFile(skillMd, "utf-8");
|
|
9125
9564
|
const skill = this.parseSkillMd(entry.name, content);
|
|
@@ -9130,7 +9569,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
9130
9569
|
} else if (entry.name.endsWith(".md")) {
|
|
9131
9570
|
const cmdName = entry.name.replace(/\.md$/, "");
|
|
9132
9571
|
try {
|
|
9133
|
-
const content = await readFile(
|
|
9572
|
+
const content = await readFile(path13.join(dir, entry.name), "utf-8");
|
|
9134
9573
|
const skill = this.parseSkillMd(cmdName, content);
|
|
9135
9574
|
skill.sourcePath = dir;
|
|
9136
9575
|
skills.push(skill);
|
|
@@ -10019,15 +10458,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10019
10458
|
}
|
|
10020
10459
|
}
|
|
10021
10460
|
recordRuntimeTelemetry(agentId, ap, event) {
|
|
10461
|
+
const telemetryAttrs = {
|
|
10462
|
+
...event.attrs,
|
|
10463
|
+
...event.source ? { source: event.source } : {},
|
|
10464
|
+
...event.usageKind ? { usageKind: event.usageKind } : {},
|
|
10465
|
+
...event.sessionId ? { sessionId: event.sessionId } : {},
|
|
10466
|
+
...event.turnId ? { turnId: event.turnId } : {},
|
|
10467
|
+
...event.runtimeResultId ? { runtimeResultId: event.runtimeResultId } : {}
|
|
10468
|
+
};
|
|
10022
10469
|
const attrs = {
|
|
10023
10470
|
agentId,
|
|
10024
10471
|
launchId: ap.launchId || void 0,
|
|
10025
10472
|
runtime: ap.config.runtime,
|
|
10026
10473
|
model: ap.config.model,
|
|
10027
10474
|
telemetry_name: event.name,
|
|
10028
|
-
...
|
|
10475
|
+
...telemetryAttrs
|
|
10029
10476
|
};
|
|
10030
|
-
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`,
|
|
10477
|
+
ap.runtimeTraceSpan?.addEvent(`runtime.telemetry.${event.name}`, telemetryAttrs);
|
|
10031
10478
|
this.recordDaemonTrace(`daemon.runtime.telemetry.${event.name}`, attrs);
|
|
10032
10479
|
}
|
|
10033
10480
|
sendAgentStatus(agentId, status, launchId) {
|
|
@@ -10101,7 +10548,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
|
|
|
10101
10548
|
}
|
|
10102
10549
|
const inboxCount = ap.inbox.length;
|
|
10103
10550
|
if (inboxCount === 0) return false;
|
|
10104
|
-
const notification = `[
|
|
10551
|
+
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.]`;
|
|
10105
10552
|
logger.info(`[Agent ${agentId}] Sending stdin notification: ${inboxCount} pending inbox message(s)`);
|
|
10106
10553
|
const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
|
|
10107
10554
|
if (encoded) {
|
|
@@ -10248,8 +10695,8 @@ ${RESPONSE_TARGET_HINT}`);
|
|
|
10248
10695
|
const nodes = [];
|
|
10249
10696
|
for (const entry of entries) {
|
|
10250
10697
|
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
10251
|
-
const fullPath =
|
|
10252
|
-
const relativePath =
|
|
10698
|
+
const fullPath = path13.join(dir, entry.name);
|
|
10699
|
+
const relativePath = path13.relative(rootDir, fullPath);
|
|
10253
10700
|
let info;
|
|
10254
10701
|
try {
|
|
10255
10702
|
info = await stat2(fullPath);
|
|
@@ -10553,10 +11000,10 @@ var ReminderCache = class {
|
|
|
10553
11000
|
};
|
|
10554
11001
|
|
|
10555
11002
|
// src/machineLock.ts
|
|
10556
|
-
import { createHash as createHash4, randomUUID as
|
|
10557
|
-
import { mkdirSync as
|
|
11003
|
+
import { createHash as createHash4, randomUUID as randomUUID4 } from "crypto";
|
|
11004
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
|
|
10558
11005
|
import os6 from "os";
|
|
10559
|
-
import
|
|
11006
|
+
import path14 from "path";
|
|
10560
11007
|
var INCOMPLETE_LOCK_STALE_MS = 3e4;
|
|
10561
11008
|
var DaemonMachineLockConflictError = class extends Error {
|
|
10562
11009
|
code = "DAEMON_MACHINE_LOCK_HELD";
|
|
@@ -10578,7 +11025,7 @@ function resolveDefaultMachineStateRoot() {
|
|
|
10578
11025
|
return resolveSlockHomePath("machines");
|
|
10579
11026
|
}
|
|
10580
11027
|
function ownerPath(lockDir) {
|
|
10581
|
-
return
|
|
11028
|
+
return path14.join(lockDir, "owner.json");
|
|
10582
11029
|
}
|
|
10583
11030
|
function readOwner(lockDir) {
|
|
10584
11031
|
try {
|
|
@@ -10608,13 +11055,13 @@ function acquireDaemonMachineLock(options) {
|
|
|
10608
11055
|
const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
|
|
10609
11056
|
const fingerprint = apiKeyFingerprint(options.apiKey);
|
|
10610
11057
|
const lockId = getDaemonMachineLockId(options.apiKey);
|
|
10611
|
-
const machineDir =
|
|
10612
|
-
const lockDir =
|
|
10613
|
-
const token =
|
|
10614
|
-
|
|
11058
|
+
const machineDir = path14.join(rootDir, lockId);
|
|
11059
|
+
const lockDir = path14.join(machineDir, "daemon.lock");
|
|
11060
|
+
const token = randomUUID4();
|
|
11061
|
+
mkdirSync6(machineDir, { recursive: true });
|
|
10615
11062
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
10616
11063
|
try {
|
|
10617
|
-
|
|
11064
|
+
mkdirSync6(lockDir);
|
|
10618
11065
|
const owner = {
|
|
10619
11066
|
pid: process.pid,
|
|
10620
11067
|
token,
|
|
@@ -10637,7 +11084,15 @@ function acquireDaemonMachineLock(options) {
|
|
|
10637
11084
|
release: () => {
|
|
10638
11085
|
const currentOwner = readOwner(lockDir);
|
|
10639
11086
|
if (currentOwner?.pid === process.pid && currentOwner.token === token) {
|
|
10640
|
-
|
|
11087
|
+
const released = { ...currentOwner, pid: 0 };
|
|
11088
|
+
try {
|
|
11089
|
+
writeFileSync8(ownerPath(lockDir), `${JSON.stringify(released, null, 2)}
|
|
11090
|
+
`, {
|
|
11091
|
+
mode: 384
|
|
11092
|
+
});
|
|
11093
|
+
} catch {
|
|
11094
|
+
rmSync3(lockDir, { recursive: true, force: true });
|
|
11095
|
+
}
|
|
10641
11096
|
}
|
|
10642
11097
|
}
|
|
10643
11098
|
};
|
|
@@ -10661,8 +11116,8 @@ function acquireDaemonMachineLock(options) {
|
|
|
10661
11116
|
}
|
|
10662
11117
|
|
|
10663
11118
|
// src/localTraceSink.ts
|
|
10664
|
-
import { appendFileSync, mkdirSync as
|
|
10665
|
-
import
|
|
11119
|
+
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync2, rmSync as rmSync4, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
|
|
11120
|
+
import path15 from "path";
|
|
10666
11121
|
var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
|
|
10667
11122
|
var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
|
|
10668
11123
|
var DEFAULT_MAX_FILES = 8;
|
|
@@ -10699,7 +11154,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10699
11154
|
currentSize = 0;
|
|
10700
11155
|
sequence = 0;
|
|
10701
11156
|
constructor(options) {
|
|
10702
|
-
this.traceDir =
|
|
11157
|
+
this.traceDir = path15.join(options.machineDir, "traces");
|
|
10703
11158
|
this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
|
|
10704
11159
|
const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
|
|
10705
11160
|
const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
|
|
@@ -10725,11 +11180,11 @@ var LocalRotatingTraceSink = class {
|
|
|
10725
11180
|
return this.currentFile;
|
|
10726
11181
|
}
|
|
10727
11182
|
ensureFile(nextBytes) {
|
|
10728
|
-
|
|
11183
|
+
mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
|
|
10729
11184
|
const nowMs = this.nowMsProvider();
|
|
10730
11185
|
const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
|
|
10731
11186
|
if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
|
|
10732
|
-
this.currentFile =
|
|
11187
|
+
this.currentFile = path15.join(
|
|
10733
11188
|
this.traceDir,
|
|
10734
11189
|
`daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
|
|
10735
11190
|
);
|
|
@@ -10744,7 +11199,7 @@ var LocalRotatingTraceSink = class {
|
|
|
10744
11199
|
const excess = files.length - this.maxFiles;
|
|
10745
11200
|
if (excess <= 0) return;
|
|
10746
11201
|
for (const file of files.slice(0, excess)) {
|
|
10747
|
-
rmSync4(
|
|
11202
|
+
rmSync4(path15.join(this.traceDir, file), { force: true });
|
|
10748
11203
|
}
|
|
10749
11204
|
}
|
|
10750
11205
|
};
|
|
@@ -10832,14 +11287,14 @@ function isDiagnosticErrorAttr(key) {
|
|
|
10832
11287
|
}
|
|
10833
11288
|
|
|
10834
11289
|
// src/traceBundleUpload.ts
|
|
10835
|
-
import { createHash as createHash6, randomUUID as
|
|
11290
|
+
import { createHash as createHash6, randomUUID as randomUUID5 } from "crypto";
|
|
10836
11291
|
import { gzipSync } from "zlib";
|
|
10837
11292
|
import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
|
|
10838
|
-
import
|
|
11293
|
+
import path16 from "path";
|
|
10839
11294
|
|
|
10840
11295
|
// src/directUploadCapability.ts
|
|
10841
|
-
function joinUrl(base,
|
|
10842
|
-
return `${base.replace(/\/+$/, "")}${
|
|
11296
|
+
function joinUrl(base, path18) {
|
|
11297
|
+
return `${base.replace(/\/+$/, "")}${path18}`;
|
|
10843
11298
|
}
|
|
10844
11299
|
function jsonHeaders(apiKey) {
|
|
10845
11300
|
return {
|
|
@@ -11058,7 +11513,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
11058
11513
|
}, nextMs);
|
|
11059
11514
|
}
|
|
11060
11515
|
async findUploadCandidates() {
|
|
11061
|
-
const traceDir =
|
|
11516
|
+
const traceDir = path16.join(this.options.machineDir, "traces");
|
|
11062
11517
|
let names;
|
|
11063
11518
|
try {
|
|
11064
11519
|
names = await readdir3(traceDir);
|
|
@@ -11070,8 +11525,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
11070
11525
|
const currentFile = this.options.currentFileProvider?.();
|
|
11071
11526
|
const candidates = [];
|
|
11072
11527
|
for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
|
|
11073
|
-
const file =
|
|
11074
|
-
if (currentFile &&
|
|
11528
|
+
const file = path16.join(traceDir, name);
|
|
11529
|
+
if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
|
|
11075
11530
|
if (await this.isUploaded(file)) continue;
|
|
11076
11531
|
try {
|
|
11077
11532
|
const info = await stat3(file);
|
|
@@ -11103,7 +11558,7 @@ var DaemonTraceBundleUploader = class {
|
|
|
11103
11558
|
}
|
|
11104
11559
|
const gzipped = gzipSync(raw);
|
|
11105
11560
|
const bundleSha256 = sha256Hex(gzipped);
|
|
11106
|
-
const bundleId =
|
|
11561
|
+
const bundleId = randomUUID5();
|
|
11107
11562
|
await uploadWithSignedCapability({
|
|
11108
11563
|
serverUrl: this.options.serverUrl,
|
|
11109
11564
|
apiKey: this.options.apiKey,
|
|
@@ -11145,8 +11600,8 @@ var DaemonTraceBundleUploader = class {
|
|
|
11145
11600
|
}
|
|
11146
11601
|
}
|
|
11147
11602
|
uploadStatePath(file) {
|
|
11148
|
-
const stateDir =
|
|
11149
|
-
return
|
|
11603
|
+
const stateDir = path16.join(this.options.machineDir, "trace-uploads");
|
|
11604
|
+
return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
|
|
11150
11605
|
}
|
|
11151
11606
|
async isUploaded(file) {
|
|
11152
11607
|
try {
|
|
@@ -11158,9 +11613,9 @@ var DaemonTraceBundleUploader = class {
|
|
|
11158
11613
|
}
|
|
11159
11614
|
async markUploaded(file, metadata) {
|
|
11160
11615
|
const stateFile = this.uploadStatePath(file);
|
|
11161
|
-
await mkdir2(
|
|
11616
|
+
await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
|
|
11162
11617
|
await writeFile2(stateFile, `${JSON.stringify({
|
|
11163
|
-
file:
|
|
11618
|
+
file: path16.basename(file),
|
|
11164
11619
|
uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11165
11620
|
...metadata
|
|
11166
11621
|
}, null, 2)}
|
|
@@ -11237,23 +11692,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
|
|
|
11237
11692
|
}
|
|
11238
11693
|
}
|
|
11239
11694
|
function resolveChatBridgePath(moduleUrl = import.meta.url) {
|
|
11240
|
-
const dirname =
|
|
11241
|
-
const jsPath =
|
|
11695
|
+
const dirname = path17.dirname(fileURLToPath(moduleUrl));
|
|
11696
|
+
const jsPath = path17.resolve(dirname, "chat-bridge.js");
|
|
11242
11697
|
try {
|
|
11243
11698
|
accessSync(jsPath);
|
|
11244
11699
|
return jsPath;
|
|
11245
11700
|
} catch {
|
|
11246
|
-
return
|
|
11701
|
+
return path17.resolve(dirname, "chat-bridge.ts");
|
|
11247
11702
|
}
|
|
11248
11703
|
}
|
|
11249
11704
|
function resolveSlockCliPath(moduleUrl = import.meta.url) {
|
|
11250
|
-
const thisDir =
|
|
11251
|
-
const bundledDistPath =
|
|
11705
|
+
const thisDir = path17.dirname(fileURLToPath(moduleUrl));
|
|
11706
|
+
const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
|
|
11252
11707
|
try {
|
|
11253
11708
|
accessSync(bundledDistPath);
|
|
11254
11709
|
return bundledDistPath;
|
|
11255
11710
|
} catch {
|
|
11256
|
-
const workspaceDistPath =
|
|
11711
|
+
const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
|
|
11257
11712
|
accessSync(workspaceDistPath);
|
|
11258
11713
|
return workspaceDistPath;
|
|
11259
11714
|
}
|
|
@@ -11383,6 +11838,11 @@ function summarizeIncomingMessage(msg) {
|
|
|
11383
11838
|
var DaemonCore = class {
|
|
11384
11839
|
options;
|
|
11385
11840
|
daemonVersion;
|
|
11841
|
+
// When this runner is launched by a managed Computer service, the
|
|
11842
|
+
// service exports SLOCK_COMPUTER_VERSION (the `@slock-ai/computer`
|
|
11843
|
+
// bundle version). Reported in `ready` so the server can surface the
|
|
11844
|
+
// Computer's own version (distinct from the underlying daemonVersion).
|
|
11845
|
+
computerVersion;
|
|
11386
11846
|
chatBridgePath;
|
|
11387
11847
|
slockCliPath;
|
|
11388
11848
|
slockHome;
|
|
@@ -11398,6 +11858,7 @@ var DaemonCore = class {
|
|
|
11398
11858
|
constructor(options) {
|
|
11399
11859
|
this.options = options;
|
|
11400
11860
|
this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
|
|
11861
|
+
this.computerVersion = (process.env.SLOCK_COMPUTER_VERSION || "").trim() || null;
|
|
11401
11862
|
this.chatBridgePath = options.chatBridgePath ?? resolveChatBridgePath();
|
|
11402
11863
|
this.slockCliPath = options.slockCliPath ?? resolveSlockCliPath();
|
|
11403
11864
|
this.slockHome = resolveSlockHome();
|
|
@@ -11432,7 +11893,7 @@ var DaemonCore = class {
|
|
|
11432
11893
|
}
|
|
11433
11894
|
resolveMachineStateRoot() {
|
|
11434
11895
|
if (this.options.machineStateDir) return this.options.machineStateDir;
|
|
11435
|
-
if (this.options.dataDir) return
|
|
11896
|
+
if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
|
|
11436
11897
|
return resolveDefaultMachineStateRoot();
|
|
11437
11898
|
}
|
|
11438
11899
|
shouldEnableLocalTrace() {
|
|
@@ -11458,7 +11919,7 @@ var DaemonCore = class {
|
|
|
11458
11919
|
sink: this.localTraceSink
|
|
11459
11920
|
});
|
|
11460
11921
|
this.agentManager.setTracer(this.tracer);
|
|
11461
|
-
this.agentManager.setCliTransportTraceDir(
|
|
11922
|
+
this.agentManager.setCliTransportTraceDir(path17.join(machineDir, "traces"));
|
|
11462
11923
|
}
|
|
11463
11924
|
installTraceBundleUploader(machineDir) {
|
|
11464
11925
|
if (!this.shouldEnableLocalTrace()) return;
|
|
@@ -11857,6 +12318,26 @@ var DaemonCore = class {
|
|
|
11857
12318
|
case "ping":
|
|
11858
12319
|
this.connection.send({ type: "pong" });
|
|
11859
12320
|
break;
|
|
12321
|
+
case "computer:restart":
|
|
12322
|
+
case "computer:upgrade": {
|
|
12323
|
+
const action = msg.type === "computer:restart" ? "restart" : "upgrade";
|
|
12324
|
+
this.recordDaemonTrace("daemon.computer_control.received", {
|
|
12325
|
+
action,
|
|
12326
|
+
handled: Boolean(this.options.onComputerControl)
|
|
12327
|
+
});
|
|
12328
|
+
if (this.options.onComputerControl) {
|
|
12329
|
+
try {
|
|
12330
|
+
this.options.onComputerControl(action);
|
|
12331
|
+
} catch (err) {
|
|
12332
|
+
logger.error(
|
|
12333
|
+
`[Daemon] computer:${action} control handler failed: ${err instanceof Error ? err.message : String(err)}`
|
|
12334
|
+
);
|
|
12335
|
+
}
|
|
12336
|
+
} else {
|
|
12337
|
+
logger.info(`[Daemon] Ignoring computer:${action} \u2014 not launched by a Computer service.`);
|
|
12338
|
+
}
|
|
12339
|
+
break;
|
|
12340
|
+
}
|
|
11860
12341
|
}
|
|
11861
12342
|
}
|
|
11862
12343
|
onReminderFire(job) {
|
|
@@ -11883,7 +12364,8 @@ var DaemonCore = class {
|
|
|
11883
12364
|
runningAgents: runningAgentIds,
|
|
11884
12365
|
hostname: this.options.hostname ?? os7.hostname(),
|
|
11885
12366
|
os: this.options.osDescription ?? `${os7.platform()} ${os7.arch()}`,
|
|
11886
|
-
daemonVersion: this.daemonVersion
|
|
12367
|
+
daemonVersion: this.daemonVersion,
|
|
12368
|
+
...this.computerVersion ? { computerVersion: this.computerVersion } : {}
|
|
11887
12369
|
});
|
|
11888
12370
|
this.recordDaemonTrace("daemon.ready.sent", {
|
|
11889
12371
|
runtimes_count: runtimes.length,
|