@slock-ai/daemon 0.51.1-play.20260519174619 → 0.52.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,11 +7,11 @@ import {
7
7
  } from "./chunk-KNMCE6WB.js";
8
8
 
9
9
  // src/core.ts
10
- import path17 from "path";
10
+ import path16 from "path";
11
11
  import os8 from "os";
12
12
  import { createRequire } from "module";
13
13
  import { accessSync } from "fs";
14
- import { fileURLToPath as fileURLToPath2 } from "url";
14
+ import { fileURLToPath } from "url";
15
15
 
16
16
  // ../shared/src/tracing/index.ts
17
17
  var DEFAULT_TRACE_FLAGS = "00";
@@ -723,7 +723,6 @@ var SERVER_CAPABILITY_MATRIX = {
723
723
  var RUNTIMES = [
724
724
  { id: "claude", displayName: "Claude Code", binary: "claude", supported: true },
725
725
  { id: "codex", displayName: "Codex CLI", binary: "codex", supported: true },
726
- { id: "pi", displayName: "Pi", binary: "pi", supported: true },
727
726
  { id: "kimi", displayName: "Kimi CLI", binary: "kimi", supported: true },
728
727
  { id: "copilot", displayName: "Copilot CLI", binary: "copilot", supported: true },
729
728
  { id: "cursor", displayName: "Cursor CLI", binary: "cursor-agent", supported: true },
@@ -765,10 +764,10 @@ var DISPLAY_PLAN_CONFIG = {
765
764
  };
766
765
 
767
766
  // src/agentProcessManager.ts
768
- import { mkdirSync as mkdirSync5, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync8 } from "fs";
767
+ import { mkdirSync as mkdirSync4, readdirSync as readdirSync2, statSync as statSync2, writeFileSync as writeFileSync7 } from "fs";
769
768
  import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2 } from "fs/promises";
770
769
  import { createHash as createHash2 } from "crypto";
771
- import path13 from "path";
770
+ import path12 from "path";
772
771
  import os6 from "os";
773
772
 
774
773
  // src/drivers/claude.ts
@@ -872,15 +871,13 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
872
871
  17. **\`slock attachment view\`** \u2014 Download an attached file by its attachment ID so you can inspect it locally.
873
872
  18. **\`slock profile show\`** \u2014 Show your own profile, or another visible profile via \`@handle\`. Mirrors the canonical Slock profile view.
874
873
  19. **\`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.
875
- 20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
876
- 21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
877
- 22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
878
- 23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
879
- 24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
880
- 25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
881
- 26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
882
- 27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
883
- 28. **\`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\`).
874
+ 20. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
875
+ 21. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
876
+ 22. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
877
+ 23. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
878
+ 24. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
879
+ 25. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
880
+ 26. **\`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\`).
884
881
 
885
882
  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:
886
883
  - failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
@@ -981,11 +978,6 @@ Each channel has a **name** and optionally a **description** that define its pur
981
978
  - **Reply in context** \u2014 always respond in the channel/thread the message came from.
982
979
  - **Stay on topic** \u2014 when proactively sharing results or updates, post in the channel most relevant to the work. Don't scatter messages across unrelated channels.
983
980
  - If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
984
- const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
985
-
986
- If a registered third-party service requires login, use Slock Agent Login through the CLI instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first run \`slock integration list\` and match the app to a registered service before browsing the app. Use \`slock integration login --service <service>\` to provision or reuse your agent login for that service. When the command returns \`Agent login ready\` or \`Already logged in\`, the agent-side login is ready. If the output includes an app URL, open that URL as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow, use internal request IDs as OAuth callback codes, or call third-party exchange endpoints unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use \`slock profile show\`. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.` : `### Third-party integrations
987
-
988
- If a registered third-party service requires login, use Slock Agent Login through the available registered-service interface instead of asking the human to copy tokens or complete human OAuth for you. If a human asks you to sign into, open, use, or fetch identity from a third-party app, first inspect the registered-service interface and match the app to a registered service before browsing the app. Once the registered-service interface reports the agent login is ready, the agent-side login is ready. If that interface provides an app URL, use it as the service-provided third-party app surface; it should look like the service's normal Login with Slock callback and not require you to understand Slock's internal grant/request protocol. Do not crawl third-party routes looking for a session before trying the registered-service login path. Do not open the human \`Login with Slock\` browser flow or treat internal request IDs as OAuth callback codes unless a human explicitly asks you to debug that server-to-server protocol. If the service or human asks for your Slock Agent identity card, use your Slock profile view. Third-party pages may show \`Login with Slock\`; for agent-facing access, prefer the registered service / Slock Agent Login path.`;
989
981
  const readingHistorySection = isCli ? `### Reading history
990
982
 
991
983
  \`slock message read --channel "#channel-name"\` or \`slock message read --channel dm:@peer-name\` or \`slock message read --channel "#channel:shortid"\`
@@ -1157,8 +1149,6 @@ ${discoverySection}
1157
1149
 
1158
1150
  ${channelAwarenessSection}
1159
1151
 
1160
- ${thirdPartyIntegrationsSection}
1161
-
1162
1152
  ${readingHistorySection}
1163
1153
 
1164
1154
  ${historicalReferenceSection}
@@ -1190,17 +1180,6 @@ Keep the user informed. They cannot see your internal reasoning, so:
1190
1180
  - For multi-step work, send short progress updates (e.g. "Working on step 2/3\u2026").
1191
1181
  - When done, summarize the result.
1192
1182
  - Keep updates concise \u2014 one or two sentences. Don't flood the chat.
1193
- - For long answers where users need the conclusion first but details still matter, put the conclusion and next action outside any collapse, then use sanitized HTML details blocks for optional depth:
1194
-
1195
- \`\`\`html
1196
- <details>
1197
- <summary>Evidence, logs, or edge cases</summary>
1198
-
1199
- Detailed notes go here.
1200
- </details>
1201
- \`\`\`
1202
-
1203
- Do not hide the main recommendation, blocker, or required action inside \`<details>\`; only fold supporting evidence, logs, alternatives, or extended rationale.
1204
1183
 
1205
1184
  ### Conversation etiquette
1206
1185
 
@@ -1296,13 +1275,12 @@ You may develop a specialized role over time through your interactions. Embrace
1296
1275
 
1297
1276
  ## Message Notifications
1298
1277
 
1299
- While you are working, new messages may be delivered directly into your current thread.
1278
+ While you are working, the daemon may write a batched inbox-count notification into your current turn.
1300
1279
 
1301
1280
  How to handle these:
1302
- - Treat direct follow-up messages as new user input for the same live session.
1303
- - Adapt if the new message changes priority or direction.
1304
- - You do NOT need to poll just because direct follow-up delivery is available.
1305
- - Use ${checkCmd} only when you need to inspect other pending channels or recover broader context.`;
1281
+ - Treat the notification as a signal that new Slock messages are waiting; it does not include the message content.
1282
+ - Call ${checkCmd} at the next safe breakpoint to materialize the pending messages before taking side-effect actions that depend on current context.
1283
+ - If the new message is higher priority, pivot after reading it. If not, continue your current work.`;
1306
1284
  } else {
1307
1285
  const notifyExample = isCli ? `\`[System notification: You have N new message(s) waiting. Call slock message check to read them when you're ready.]\`` : `\`[System notification: You have N new message(s) waiting. Call ${t("check_messages")} to read them when you're ready.]\``;
1308
1286
  prompt += `
@@ -1370,19 +1348,6 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1370
1348
  return candidates.filter((candidate) => existsSync(candidate.path));
1371
1349
  }
1372
1350
 
1373
- // src/authEnv.ts
1374
- var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1375
- var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1376
- function scrubDaemonAuthEnv(env) {
1377
- delete env[DAEMON_API_KEY_ENV];
1378
- return env;
1379
- }
1380
- function scrubDaemonChildEnv(env) {
1381
- delete env[DAEMON_API_KEY_ENV];
1382
- delete env[SLOCK_AGENT_TOKEN_ENV];
1383
- return env;
1384
- }
1385
-
1386
1351
  // src/agentCredentialProxy.ts
1387
1352
  import { randomBytes } from "crypto";
1388
1353
  import http from "http";
@@ -1390,6 +1355,7 @@ import { URL as URL2 } from "url";
1390
1355
  var registrations = /* @__PURE__ */ new Map();
1391
1356
  var servers = /* @__PURE__ */ new Map();
1392
1357
  var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
1358
+ var LOCAL_HELD_CONTEXT_LIMIT = 3;
1393
1359
  function allocatePort() {
1394
1360
  return 43e3 + randomBytes(2).readUInt16BE(0) % 1e4;
1395
1361
  }
@@ -1422,8 +1388,10 @@ async function handleProxyRequest(req, res) {
1422
1388
  res.end(JSON.stringify({ error: "invalid local agent proxy token", code: "invalid_agent_proxy_token" }));
1423
1389
  return;
1424
1390
  }
1391
+ const method = req.method ?? "GET";
1392
+ let target;
1425
1393
  try {
1426
- const target = new URL2(req.url ?? "/", registration.serverUrl);
1394
+ target = new URL2(req.url ?? "/", registration.serverUrl);
1427
1395
  const headers = new Headers();
1428
1396
  for (const [name, value] of Object.entries(req.headers)) {
1429
1397
  if (value === void 0) continue;
@@ -1439,12 +1407,20 @@ async function handleProxyRequest(req, res) {
1439
1407
  headers.set("X-Agent-Id", registration.agentId);
1440
1408
  headers.set("X-Slock-Client", "cli");
1441
1409
  headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
1442
- const method = req.method ?? "GET";
1443
1410
  let body = method === "GET" || method === "HEAD" ? void 0 : req;
1444
1411
  let sendTarget;
1445
- if (method === "POST" && target.pathname === "/internal/agent-api/send") {
1412
+ const sideEffectAction = agentApiSideEffectAction(target.pathname);
1413
+ if (method === "GET" && target.pathname === "/internal/agent-api/events") {
1414
+ const localEvents = localAgentApiEventsResponse(registration, target);
1415
+ if (localEvents) {
1416
+ res.writeHead(localEvents.status, { "content-type": "application/json" });
1417
+ res.end(JSON.stringify(localEvents.body));
1418
+ return;
1419
+ }
1420
+ }
1421
+ if (method === "POST" && sideEffectAction) {
1446
1422
  const rawBody = await readRequestBody(req);
1447
- const prepared = await prepareAgentApiSendForward(registration, headers, rawBody);
1423
+ const prepared = await prepareAgentApiSideEffectForward(registration, headers, rawBody, sideEffectAction);
1448
1424
  if (prepared.localResponse) {
1449
1425
  const responseText = JSON.stringify(prepared.localResponse);
1450
1426
  res.writeHead(200, { "content-type": "application/json" });
@@ -1452,7 +1428,7 @@ async function handleProxyRequest(req, res) {
1452
1428
  return;
1453
1429
  }
1454
1430
  body = prepared.bodyText;
1455
- sendTarget = prepared.target;
1431
+ if (sideEffectAction === "send") sendTarget = prepared.target;
1456
1432
  headers.set("content-type", "application/json");
1457
1433
  headers.set("content-length", String(Buffer.byteLength(prepared.bodyText)));
1458
1434
  }
@@ -1486,14 +1462,33 @@ async function handleProxyRequest(req, res) {
1486
1462
  res.end();
1487
1463
  }
1488
1464
  } catch (err) {
1465
+ const failure = proxyFailureForError(method, target, err);
1466
+ logger.warn(
1467
+ `[Agent Credential Proxy] request failed (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, method=${failure.method}, path=${failure.pathname}, query_keys=${failure.queryKeys.join(",") || "none"}): ${failure.errorName}: ${failure.errorMessage}`
1468
+ );
1469
+ registration.inboxCoordinator?.recordProxyFailure?.(failure);
1489
1470
  res.writeHead(502, { "content-type": "application/json" });
1490
1471
  res.end(JSON.stringify({
1491
1472
  error: "failed to proxy local agent request",
1492
1473
  code: "agent_proxy_failed",
1493
- detail: err instanceof Error ? err.message : String(err)
1474
+ detail: failure.errorMessage
1494
1475
  }));
1495
1476
  }
1496
1477
  }
1478
+ function proxyFailureForError(method, target, err) {
1479
+ const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
1480
+ return {
1481
+ method,
1482
+ pathname: target?.pathname ?? "unknown",
1483
+ queryKeys,
1484
+ errorName: err instanceof Error ? err.name : typeof err,
1485
+ errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
1486
+ };
1487
+ }
1488
+ function truncateProxyErrorMessage(message) {
1489
+ const normalized = message.replace(/\s+/g, " ").trim();
1490
+ return normalized.length > 500 ? `${normalized.slice(0, 497)}...` : normalized;
1491
+ }
1497
1492
  async function readRequestBody(req) {
1498
1493
  let body = "";
1499
1494
  req.setEncoding("utf8");
@@ -1505,14 +1500,6 @@ async function readRequestBody(req) {
1505
1500
  function messageSeq(message) {
1506
1501
  return Number(message.seq ?? 0);
1507
1502
  }
1508
- function boundaryBefore(messages) {
1509
- let minSeq = Number.POSITIVE_INFINITY;
1510
- for (const message of messages) {
1511
- const seq = Math.floor(messageSeq(message));
1512
- if (Number.isFinite(seq) && seq > 0) minSeq = Math.min(minSeq, seq);
1513
- }
1514
- return Number.isFinite(minSeq) ? Math.max(0, minSeq - 1) : void 0;
1515
- }
1516
1503
  function maxMessageSeq(messages) {
1517
1504
  let maxSeq = 0;
1518
1505
  for (const message of messages) {
@@ -1521,6 +1508,117 @@ function maxMessageSeq(messages) {
1521
1508
  }
1522
1509
  return maxSeq > 0 ? maxSeq : void 0;
1523
1510
  }
1511
+ function sortBySeq(messages) {
1512
+ return [...messages].sort((a, b) => messageSeq(a) - messageSeq(b));
1513
+ }
1514
+ function latestVisibleMessages(messages, limit) {
1515
+ const sorted = sortBySeq(messages);
1516
+ return sorted.slice(Math.max(0, sorted.length - limit));
1517
+ }
1518
+ function parseAgentApiEventsQuery(target) {
1519
+ const limit = Math.min(Math.max(Number(target.searchParams.get("limit")) || 50, 1), 200);
1520
+ const sinceRaw = target.searchParams.get("since")?.trim() ?? "";
1521
+ if (!sinceRaw) return { limit, sinceSeq: null, sinceCursorKind: null };
1522
+ if (sinceRaw === "latest") return { limit, sinceSeq: null, sinceCursorKind: "latest" };
1523
+ const parsed = Number(sinceRaw);
1524
+ if (!Number.isFinite(parsed) || parsed < 0) {
1525
+ return {
1526
+ limit,
1527
+ sinceSeq: null,
1528
+ sinceCursorKind: null,
1529
+ error: {
1530
+ error: "since must be a non-negative integer (messageSeq) or 'latest'",
1531
+ code: "since_invalid"
1532
+ }
1533
+ };
1534
+ }
1535
+ return { limit, sinceSeq: Math.floor(parsed), sinceCursorKind: "seq" };
1536
+ }
1537
+ function localAgentApiEventsResponse(registration, target) {
1538
+ const coordinator = registration.inboxCoordinator;
1539
+ if (!coordinator) return void 0;
1540
+ const pending = coordinator.getAllPendingMessages?.() ?? [];
1541
+ const parsedQuery = parseAgentApiEventsQuery(target);
1542
+ if (parsedQuery.error && pending.length > 0) {
1543
+ return { status: 400, body: parsedQuery.error };
1544
+ }
1545
+ if (pending.length === 0) return void 0;
1546
+ const normalized = sortBySeq(normalizeVisibleMessages(pending));
1547
+ const filtered = parsedQuery.sinceSeq !== null ? normalized.filter((message) => {
1548
+ const seq = messageSeq(message);
1549
+ return Number.isFinite(seq) && seq > parsedQuery.sinceSeq;
1550
+ }) : normalized;
1551
+ const events = filtered.slice(0, parsedQuery.limit);
1552
+ const hasMore = filtered.length > events.length;
1553
+ const newestEvent = events[events.length - 1];
1554
+ const lastSeenMsgId = newestEvent?.message_id ?? newestEvent?.id ?? null;
1555
+ const lastSeenSeq = newestEvent?.seq ?? parsedQuery.sinceSeq;
1556
+ if (events.length > 0) {
1557
+ coordinator.consumeVisibleMessages({ messages: events, source: "agent_api_events" });
1558
+ }
1559
+ coordinator.recordDrainOutcome?.({
1560
+ source: "daemon_pending",
1561
+ sinceCursorKind: parsedQuery.sinceCursorKind,
1562
+ notifiedCount: pending.length,
1563
+ drainedCount: events.length,
1564
+ hasMore
1565
+ });
1566
+ return {
1567
+ status: 200,
1568
+ body: {
1569
+ events,
1570
+ last_seen_msgId: lastSeenMsgId,
1571
+ last_seen_seq: lastSeenSeq,
1572
+ reply_target: null,
1573
+ pending_notice_ids: [],
1574
+ wake_reason: null,
1575
+ has_more: hasMore
1576
+ }
1577
+ };
1578
+ }
1579
+ function heldAvailableActions(action) {
1580
+ return action === "send" ? ["check_messages", "send_draft", "send_anyway"] : ["check_messages", "retry_action"];
1581
+ }
1582
+ function localHeldResponse(input) {
1583
+ if (input.messages.length === 0) return void 0;
1584
+ const normalized = sortBySeq(normalizeVisibleMessages(input.messages, input.target));
1585
+ const heldMessages = latestVisibleMessages(normalized, LOCAL_HELD_CONTEXT_LIMIT);
1586
+ const omittedMessageCount = Math.max(0, normalized.length - heldMessages.length);
1587
+ const seenUpToSeq = maxMessageSeq(normalized);
1588
+ if (seenUpToSeq === void 0) return void 0;
1589
+ input.coordinator.consumeVisibleMessages({
1590
+ target: input.target,
1591
+ messages: heldMessages,
1592
+ boundarySeq: seenUpToSeq,
1593
+ source: input.source
1594
+ });
1595
+ const response = {
1596
+ state: "held",
1597
+ outcome: "held",
1598
+ subtype: "freshness",
1599
+ reason: "newer_messages_available",
1600
+ available_actions: heldAvailableActions(input.action),
1601
+ heldMessages,
1602
+ newMessageCount: normalized.length,
1603
+ shownMessageCount: heldMessages.length,
1604
+ omittedMessageCount
1605
+ };
1606
+ response.seenUpToSeq = seenUpToSeq;
1607
+ return response;
1608
+ }
1609
+ function recordFreshnessDecision(coordinator, decision) {
1610
+ coordinator?.recordFreshnessDecision?.(decision);
1611
+ }
1612
+ function agentApiSideEffectAction(pathname) {
1613
+ if (pathname === "/internal/agent-api/send") return "send";
1614
+ if (pathname === "/internal/agent-api/tasks/claim") return "task_claim";
1615
+ if (pathname === "/internal/agent-api/tasks/update-status") return "task_update";
1616
+ return void 0;
1617
+ }
1618
+ function sideEffectTarget(action, body) {
1619
+ const field = action === "send" ? body.target : body.channel;
1620
+ return typeof field === "string" && field.length > 0 ? field : void 0;
1621
+ }
1524
1622
  function parseTargetFields(target) {
1525
1623
  if (target.startsWith("dm:@")) {
1526
1624
  const rest = target.slice("dm:@".length);
@@ -1577,42 +1675,98 @@ async function loadRecentTargetMessages(registration, headers, target) {
1577
1675
  const parsed = await res.json().catch(() => null);
1578
1676
  return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
1579
1677
  }
1580
- async function prepareAgentApiSendForward(registration, headers, rawBody) {
1678
+ async function prepareAgentApiSideEffectForward(registration, headers, rawBody, action) {
1581
1679
  let body;
1582
1680
  try {
1583
1681
  body = rawBody ? JSON.parse(rawBody) : {};
1584
1682
  } catch {
1585
1683
  return { bodyText: rawBody };
1586
1684
  }
1587
- const target = typeof body.target === "string" ? body.target : void 0;
1685
+ const target = sideEffectTarget(action, body);
1588
1686
  const coordinator = registration.inboxCoordinator;
1589
- if (!target || !coordinator || body.continueAnyway === true) {
1687
+ if (!target || !coordinator) {
1688
+ return { bodyText: JSON.stringify(body), target };
1689
+ }
1690
+ if (action === "send" && body.continueAnyway === true) {
1691
+ recordFreshnessDecision(coordinator, {
1692
+ action,
1693
+ decision: "bypass",
1694
+ target,
1695
+ inboxTrustState: "trusted",
1696
+ reason: "continue_anyway"
1697
+ });
1590
1698
  return { bodyText: JSON.stringify(body), target };
1591
1699
  }
1592
1700
  const pending = coordinator.getPendingMessages(target);
1593
1701
  if (pending.length > 0) {
1594
- const staleBoundary = boundaryBefore(pending);
1595
- if (staleBoundary !== void 0) {
1596
- body.seenUpToSeq = staleBoundary;
1702
+ const localResponse = localHeldResponse({
1703
+ action,
1704
+ target,
1705
+ messages: pending,
1706
+ coordinator,
1707
+ source: "side_effect_preflight_context"
1708
+ });
1709
+ if (localResponse) {
1710
+ recordFreshnessDecision(coordinator, {
1711
+ action,
1712
+ decision: "local_hold",
1713
+ target,
1714
+ inboxTrustState: "trusted",
1715
+ reason: "exact_target_pending",
1716
+ pendingCount: pending.length,
1717
+ pendingMaxSeq: maxMessageSeq(pending),
1718
+ modelSeenSeq: coordinator.getBoundary(target),
1719
+ heldMessageCount: typeof localResponse.shownMessageCount === "number" ? localResponse.shownMessageCount : void 0,
1720
+ omittedMessageCount: typeof localResponse.omittedMessageCount === "number" ? localResponse.omittedMessageCount : void 0
1721
+ });
1597
1722
  }
1598
- return { bodyText: JSON.stringify(body), target };
1723
+ return {
1724
+ bodyText: JSON.stringify(body),
1725
+ target,
1726
+ localResponse
1727
+ };
1599
1728
  }
1600
1729
  const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
1601
1730
  const loadedBoundary = coordinator.getBoundary(target);
1602
1731
  const boundary = Math.max(existingBoundary ?? 0, loadedBoundary ?? 0);
1603
1732
  if (boundary > 0) {
1604
- body.seenUpToSeq = boundary;
1733
+ if (action === "send") body.seenUpToSeq = boundary;
1734
+ recordFreshnessDecision(coordinator, {
1735
+ action,
1736
+ decision: "forward",
1737
+ target,
1738
+ inboxTrustState: "trusted",
1739
+ reason: "model_seen_boundary",
1740
+ pendingCount: 0,
1741
+ modelSeenSeq: boundary
1742
+ });
1605
1743
  return { bodyText: JSON.stringify(body), target };
1606
1744
  }
1607
1745
  const recent = await loadRecentTargetMessages(registration, headers, target);
1608
1746
  if (recent.length > 0) {
1609
1747
  const seenUpToSeq = maxMessageSeq(recent);
1610
- coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "send_preflight_context" });
1748
+ coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "side_effect_preflight_context" });
1749
+ recordFreshnessDecision(coordinator, {
1750
+ action,
1751
+ decision: "syncing_hold",
1752
+ target,
1753
+ inboxTrustState: "untrusted",
1754
+ reason: "target_first_touch_recent_context",
1755
+ pendingCount: 0,
1756
+ pendingMaxSeq: seenUpToSeq,
1757
+ modelSeenSeq: 0,
1758
+ heldMessageCount: recent.length,
1759
+ omittedMessageCount: 0
1760
+ });
1611
1761
  return {
1612
1762
  bodyText: JSON.stringify(body),
1613
1763
  target,
1614
1764
  localResponse: {
1615
1765
  state: "held",
1766
+ outcome: "held",
1767
+ subtype: "freshness",
1768
+ reason: "newer_messages_available",
1769
+ available_actions: heldAvailableActions(action),
1616
1770
  seenUpToSeq,
1617
1771
  heldMessages: recent,
1618
1772
  newMessageCount: recent.length,
@@ -1621,6 +1775,15 @@ async function prepareAgentApiSendForward(registration, headers, rawBody) {
1621
1775
  }
1622
1776
  };
1623
1777
  }
1778
+ recordFreshnessDecision(coordinator, {
1779
+ action,
1780
+ decision: "forward",
1781
+ target,
1782
+ inboxTrustState: "trusted",
1783
+ reason: "no_exact_target_pending_or_recent_context",
1784
+ pendingCount: 0,
1785
+ modelSeenSeq: 0
1786
+ });
1624
1787
  return { bodyText: JSON.stringify(body), target };
1625
1788
  }
1626
1789
  function shouldBufferJsonResponse(upstream, pathname, registration) {
@@ -1648,7 +1811,15 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
1648
1811
  return;
1649
1812
  }
1650
1813
  if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
1651
- coordinator.consumeVisibleMessages({ messages: normalizeVisibleMessages(parsed.events), source: "agent_api_events" });
1814
+ const messages = normalizeVisibleMessages(parsed.events);
1815
+ coordinator.consumeVisibleMessages({ messages, source: "agent_api_events" });
1816
+ coordinator.recordDrainOutcome?.({
1817
+ source: "server_events",
1818
+ sinceCursorKind: parseAgentApiEventsQuery(targetUrl).sinceCursorKind,
1819
+ notifiedCount: 0,
1820
+ drainedCount: messages.length,
1821
+ hasMore: Boolean(parsed.has_more)
1822
+ });
1652
1823
  return;
1653
1824
  }
1654
1825
  if (targetUrl.pathname === "/internal/agent-api/history" && Array.isArray(parsed.messages)) {
@@ -1767,7 +1938,7 @@ ${cmdCredentialLine}"${process.execPath}" "${ctx.slockCliPath}" %*\r
1767
1938
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
1768
1939
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
1769
1940
  };
1770
- scrubDaemonChildEnv(spawnEnv);
1941
+ delete spawnEnv.SLOCK_AGENT_TOKEN;
1771
1942
  delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY;
1772
1943
  delete spawnEnv.SLOCK_AGENT_CREDENTIAL_KEY_FILE;
1773
1944
  delete spawnEnv.SLOCK_AGENT_PROXY_URL;
@@ -1814,7 +1985,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn) {
1814
1985
  }
1815
1986
  function resolveCommandOnPath(command, deps = {}) {
1816
1987
  const platform = deps.platform ?? process.platform;
1817
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
1988
+ const env = deps.env ?? process.env;
1818
1989
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1819
1990
  if (platform === "win32") {
1820
1991
  return resolveCommandOnWindows(command, env, execFileSyncFn);
@@ -1839,7 +2010,7 @@ function firstExistingPath(candidates, deps = {}) {
1839
2010
  return null;
1840
2011
  }
1841
2012
  function readCommandVersion(command, args = [], deps = {}) {
1842
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
2013
+ const env = deps.env ?? process.env;
1843
2014
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
1844
2015
  try {
1845
2016
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -2193,8 +2364,7 @@ var ClaudeDriver = class {
2193
2364
  "- Runtime Profile migration completion is the only exception to CLI-only operation: when a migration notice tells you to acknowledge with `runtime_profile_migration_done`, call the `mcp__chat__runtime_profile_migration_done` tool with the exact `migration_key`; do not use `slock` CLI or reply in chat as the acknowledgment."
2194
2365
  ],
2195
2366
  postStartupNotes: [
2196
- "**Claude runtime note:** Slock preserves Claude Code same-turn steering through a gated stream-json delivery path. Busy messages are buffered and delivered at Claude-observed safe boundaries; if no earlier safe boundary is available, they are delivered after the current turn ends.",
2197
- "For long tool runs, you can also use `slock message check` at natural breakpoints to pull pending messages explicitly."
2367
+ "**Claude runtime note:** While you are busy, Slock batches inbox-count notifications instead of injecting message content. Use `slock message check` at natural breakpoints to pull the pending messages before side-effect actions that depend on current context."
2198
2368
  ],
2199
2369
  includeStdinNotificationSection: true,
2200
2370
  messageNotificationStyle: "direct"
@@ -2688,7 +2858,7 @@ var CodexDriver = class {
2688
2858
  toolPrefix: "",
2689
2859
  extraCriticalRules: [],
2690
2860
  postStartupNotes: [
2691
- "**IMPORTANT**: Your process stays alive across turns. New messages may be delivered directly into the current thread while you are working."
2861
+ "**IMPORTANT**: Your process stays alive across turns. While you are working, Slock may write batched inbox-count notifications into the current turn; call `slock message check` at natural breakpoints to read the pending messages."
2692
2862
  ],
2693
2863
  includeStdinNotificationSection: true,
2694
2864
  messageNotificationStyle: "direct"
@@ -3136,7 +3306,7 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
3136
3306
  }
3137
3307
  function runCursorModelsCommand() {
3138
3308
  return spawnSync("cursor-agent", ["models"], {
3139
- env: scrubDaemonChildEnv({ ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" }),
3309
+ env: { ...process.env, FORCE_COLOR: "0", NO_COLOR: "1" },
3140
3310
  encoding: "utf8",
3141
3311
  timeout: 5e3
3142
3312
  });
@@ -3183,7 +3353,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
3183
3353
  }
3184
3354
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
3185
3355
  const existsSyncFn = deps.existsSyncFn ?? existsSync6;
3186
- const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
3356
+ const env = deps.env ?? process.env;
3187
3357
  const winPath = path8.win32;
3188
3358
  let geminiEntry = null;
3189
3359
  try {
@@ -3357,16 +3527,13 @@ var GeminiDriver = class {
3357
3527
  // src/drivers/kimi.ts
3358
3528
  import { randomUUID } from "crypto";
3359
3529
  import { spawn as spawn6 } from "child_process";
3360
- import { chmodSync, existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
3530
+ import { existsSync as existsSync7, readFileSync as readFileSync3, writeFileSync as writeFileSync6 } from "fs";
3361
3531
  import os4 from "os";
3362
3532
  import path9 from "path";
3363
3533
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
3364
3534
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
3365
3535
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
3366
3536
  var KIMI_MCP_FILE = ".slock-kimi-mcp.json";
3367
- var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
3368
- var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
3369
- var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
3370
3537
  function parseToolArguments(raw) {
3371
3538
  if (typeof raw !== "string") return raw;
3372
3539
  try {
@@ -3375,73 +3542,6 @@ function parseToolArguments(raw) {
3375
3542
  return raw;
3376
3543
  }
3377
3544
  }
3378
- function readKimiConfigSource(home = os4.homedir(), env = process.env) {
3379
- const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
3380
- if (inlineConfig && inlineConfig.trim()) {
3381
- return {
3382
- raw: inlineConfig,
3383
- explicitPath: null,
3384
- sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
3385
- };
3386
- }
3387
- const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
3388
- const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
3389
- try {
3390
- return {
3391
- raw: readFileSync3(configPath, "utf8"),
3392
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
3393
- sourcePath: configPath
3394
- };
3395
- } catch {
3396
- return {
3397
- raw: null,
3398
- explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
3399
- sourcePath: configPath
3400
- };
3401
- }
3402
- }
3403
- function buildKimiSpawnEnv(env = process.env) {
3404
- const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
3405
- delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
3406
- delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
3407
- return scrubDaemonChildEnv(spawnEnv);
3408
- }
3409
- function buildKimiEffectiveEnv(ctx, overrideEnv) {
3410
- return {
3411
- ...process.env,
3412
- ...ctx.config.envVars || {},
3413
- ...overrideEnv || {}
3414
- };
3415
- }
3416
- function buildKimiLaunchOptions(ctx, opts = {}) {
3417
- const env = buildKimiEffectiveEnv(ctx, opts.env);
3418
- const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
3419
- const args = [];
3420
- let configFilePath = null;
3421
- let configContent = null;
3422
- if (source.explicitPath) {
3423
- configFilePath = source.explicitPath;
3424
- } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
3425
- configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
3426
- configContent = source.raw;
3427
- if (opts.writeGeneratedConfig !== false) {
3428
- writeFileSync6(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
3429
- chmodSync(configFilePath, 384);
3430
- }
3431
- }
3432
- if (configFilePath) {
3433
- args.push("--config-file", configFilePath);
3434
- }
3435
- if (ctx.config.model && ctx.config.model !== "default") {
3436
- args.push("--model", ctx.config.model);
3437
- }
3438
- return {
3439
- args,
3440
- env: buildKimiSpawnEnv(env),
3441
- configFilePath,
3442
- configContent
3443
- };
3444
- }
3445
3545
  function resolveKimiSpawn(commandArgs, deps = {}) {
3446
3546
  return {
3447
3547
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -3465,25 +3565,7 @@ var KimiDriver = class {
3465
3565
  };
3466
3566
  model = {
3467
3567
  detectedModelsVerifiedAs: "launchable",
3468
- toLaunchSpec: (modelId, ctx, opts) => {
3469
- if (!ctx) return { args: ["--model", modelId] };
3470
- const launchCtx = {
3471
- ...ctx,
3472
- config: {
3473
- ...ctx.config,
3474
- model: modelId
3475
- }
3476
- };
3477
- const launch = buildKimiLaunchOptions(launchCtx, {
3478
- home: opts?.home,
3479
- writeGeneratedConfig: false
3480
- });
3481
- return {
3482
- args: launch.args,
3483
- env: launch.env,
3484
- configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
3485
- };
3486
- }
3568
+ toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
3487
3569
  };
3488
3570
  supportsStdinNotification = true;
3489
3571
  mcpToolPrefix = "";
@@ -3537,7 +3619,6 @@ var KimiDriver = class {
3537
3619
  }
3538
3620
  }
3539
3621
  }), "utf8");
3540
- const launch = buildKimiLaunchOptions(ctx);
3541
3622
  const args = [
3542
3623
  "--wire",
3543
3624
  "--yolo",
@@ -3546,15 +3627,14 @@ var KimiDriver = class {
3546
3627
  "--mcp-config-file",
3547
3628
  mcpConfigPath,
3548
3629
  "--session",
3549
- this.sessionId,
3550
- ...launch.args
3630
+ this.sessionId
3551
3631
  ];
3552
3632
  if (ctx.config.model && ctx.config.model !== "default") {
3553
3633
  args.push("--model", ctx.config.model);
3554
3634
  }
3555
3635
  const spawnEnv = prepareCliTransport(ctx, { NO_COLOR: "1" }).spawnEnv;
3556
- const spawnTarget = resolveKimiSpawn(args);
3557
- const proc = spawn6(spawnTarget.command, spawnTarget.args, {
3636
+ const launch = resolveKimiSpawn(args);
3637
+ const proc = spawn6(launch.command, launch.args, {
3558
3638
  cwd: ctx.workingDirectory,
3559
3639
  stdio: ["pipe", "pipe", "pipe"],
3560
3640
  env: spawnEnv,
@@ -3562,7 +3642,7 @@ var KimiDriver = class {
3562
3642
  // and has an 8191-character command-line limit. Kimi's official
3563
3643
  // installer/uv entrypoint is an executable, so launch it directly and
3564
3644
  // keep prompts on stdin / files instead of routing through cmd.exe.
3565
- shell: spawnTarget.shell
3645
+ shell: launch.shell
3566
3646
  });
3567
3647
  proc.stdin?.write(JSON.stringify({
3568
3648
  jsonrpc: "2.0",
@@ -3678,9 +3758,14 @@ var KimiDriver = class {
3678
3758
  return detectKimiModels();
3679
3759
  }
3680
3760
  };
3681
- function detectKimiModels(home = os4.homedir(), opts = {}) {
3682
- const raw = readKimiConfigSource(home, opts.env).raw;
3683
- if (raw === null) return null;
3761
+ function detectKimiModels(home = os4.homedir()) {
3762
+ const configPath = path9.join(home, ".kimi", "config.toml");
3763
+ let raw;
3764
+ try {
3765
+ raw = readFileSync3(configPath, "utf8");
3766
+ } catch {
3767
+ return null;
3768
+ }
3684
3769
  const models = [];
3685
3770
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
3686
3771
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -3941,7 +4026,7 @@ function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeMode
3941
4026
  }
3942
4027
  function runOpenCodeModelsCommand(home) {
3943
4028
  const result = spawnSync2("opencode", ["models"], {
3944
- env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
4029
+ env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
3945
4030
  encoding: "utf8",
3946
4031
  timeout: 5e3
3947
4032
  });
@@ -4099,297 +4184,6 @@ var OpenCodeDriver = class {
4099
4184
  }
4100
4185
  };
4101
4186
 
4102
- // src/drivers/pi.ts
4103
- import { spawn as spawn8 } from "child_process";
4104
- import { existsSync as existsSync8, mkdirSync as mkdirSync4, writeFileSync as writeFileSync7 } from "fs";
4105
- import path11 from "path";
4106
- import { fileURLToPath } from "url";
4107
- import { getAgentDir, VERSION as PI_SDK_VERSION } from "@earendil-works/pi-coding-agent";
4108
- var CHAT_MCP_TOOL_PREFIX2 = "chat_";
4109
- var NO_MESSAGE_PROMPT2 = "No new messages are pending. Stop now.";
4110
- var FIRST_MESSAGE_TASK_PREFIX2 = "First message task (system-triggered):";
4111
- var MIN_SUPPORTED_PI_VERSION = "0.74.0";
4112
- function parseSemver2(version) {
4113
- const match = version.match(/(\d+)\.(\d+)\.(\d+)/);
4114
- if (!match) return null;
4115
- return [Number(match[1]), Number(match[2]), Number(match[3])];
4116
- }
4117
- function isSupportedPiVersion(version) {
4118
- if (!version) return true;
4119
- const actual = parseSemver2(version);
4120
- const minimum = parseSemver2(MIN_SUPPORTED_PI_VERSION);
4121
- if (!actual || !minimum) return true;
4122
- for (let i = 0; i < 3; i += 1) {
4123
- if (actual[i] > minimum[i]) return true;
4124
- if (actual[i] < minimum[i]) return false;
4125
- }
4126
- return true;
4127
- }
4128
- function unsupportedPiVersionMessage(version) {
4129
- if (!version || isSupportedPiVersion(version)) return null;
4130
- return `Pi SDK ${version} is unsupported; requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION}. Upgrade the daemon Pi dependency before starting this runtime.`;
4131
- }
4132
- function probePi(version = PI_SDK_VERSION) {
4133
- const unsupportedMessage = unsupportedPiVersionMessage(version);
4134
- if (unsupportedMessage) {
4135
- return {
4136
- available: false,
4137
- version: `${version} (requires @earendil-works/pi-coding-agent >= ${MIN_SUPPORTED_PI_VERSION})`
4138
- };
4139
- }
4140
- return { available: true, version };
4141
- }
4142
- function resolvePiSdkRunnerPath(moduleUrl = import.meta.url) {
4143
- const moduleDir = path11.dirname(fileURLToPath(moduleUrl));
4144
- const sourceSibling = path11.join(moduleDir, "piSdkRunner.ts");
4145
- if (existsSync8(sourceSibling)) return sourceSibling;
4146
- const bundledEntry = path11.join(moduleDir, "drivers", "piSdkRunner.js");
4147
- if (existsSync8(bundledEntry)) return bundledEntry;
4148
- return path11.join(moduleDir, "piSdkRunner.js");
4149
- }
4150
- function buildPiSdkNodeArgs(runnerPath = resolvePiSdkRunnerPath()) {
4151
- if (runnerPath.endsWith(".ts")) {
4152
- return [...process.execArgv, runnerPath];
4153
- }
4154
- return [runnerPath];
4155
- }
4156
- function buildPiLaunchOptions(ctx, opts = {}) {
4157
- const command = opts.command ?? process.execPath;
4158
- const piDir = path11.join(ctx.workingDirectory, ".slock", "pi");
4159
- const sessionDir = path11.join(piDir, "sessions");
4160
- mkdirSync4(sessionDir, { recursive: true });
4161
- const slock = prepareCliTransport(ctx, { NO_COLOR: "1" });
4162
- const runnerPath = opts.runnerPath ?? resolvePiSdkRunnerPath();
4163
- const agentDir = opts.agentDir ?? getAgentDir();
4164
- const runnerConfigPath = path11.join(
4165
- piDir,
4166
- `sdk-run-${(ctx.launchId || "launch").replace(/[^a-zA-Z0-9_.-]/g, "_")}.json`
4167
- );
4168
- const turnPrompt = ctx.prompt === ctx.standingPrompt ? NO_MESSAGE_PROMPT2 : ctx.prompt;
4169
- const runnerConfig = {
4170
- cwd: ctx.workingDirectory,
4171
- agentDir,
4172
- sessionDir,
4173
- sessionId: ctx.config.sessionId || null,
4174
- standingPrompt: ctx.standingPrompt,
4175
- prompt: turnPrompt,
4176
- model: ctx.config.model && ctx.config.model !== "default" ? ctx.config.model : null
4177
- };
4178
- writeFileSync7(runnerConfigPath, `${JSON.stringify(runnerConfig)}
4179
- `, { encoding: "utf8", mode: 384 });
4180
- const args = [
4181
- ...buildPiSdkNodeArgs(runnerPath),
4182
- "--config",
4183
- runnerConfigPath
4184
- ];
4185
- return {
4186
- command,
4187
- args,
4188
- env: slock.spawnEnv,
4189
- sessionDir,
4190
- agentDir,
4191
- runnerConfigPath,
4192
- sdkVersion: PI_SDK_VERSION
4193
- };
4194
- }
4195
- function isSystemFirstMessageTask2(message) {
4196
- return message.sender_id === "system" && message.channel_type === "channel" && message.channel_name === "all" && message.content.trimStart().startsWith(FIRST_MESSAGE_TASK_PREFIX2);
4197
- }
4198
- function buildPiSystemPrompt(config) {
4199
- return buildCliTransportSystemPrompt(config, {
4200
- toolPrefix: CHAT_MCP_TOOL_PREFIX2,
4201
- extraCriticalRules: [
4202
- "- Runtime Profile migration controls are not available in the Pi runtime yet. If asked to acknowledge a runtime migration, explain the blocker instead of inventing a command."
4203
- ],
4204
- postStartupNotes: [
4205
- "**Pi runtime note:** Slock launches you as a per-turn process. Complete the current wake using `slock` CLI commands, then stop; the daemon will restart you when new messages arrive."
4206
- ],
4207
- includeStdinNotificationSection: false,
4208
- messageNotificationStyle: "poll"
4209
- });
4210
- }
4211
- function contentText(content) {
4212
- if (!content) return "";
4213
- const chunks = [];
4214
- for (const item of content) {
4215
- if (item.type === "text" && typeof item.text === "string") {
4216
- chunks.push(item.text);
4217
- }
4218
- }
4219
- return chunks.join("");
4220
- }
4221
- function apiKeyErrorMessage(line) {
4222
- const trimmed = line.trim();
4223
- if (!trimmed) return null;
4224
- if (/no api key found/i.test(trimmed)) return trimmed;
4225
- if (/api key.+required/i.test(trimmed)) return trimmed;
4226
- if (/no models available/i.test(trimmed)) return trimmed;
4227
- return null;
4228
- }
4229
- var PiDriver = class {
4230
- id = "pi";
4231
- lifecycle = {
4232
- kind: "per_turn",
4233
- start: "defer_until_concrete_message",
4234
- exit: "terminate_on_turn_end",
4235
- inFlightWake: "coalesce_into_pending"
4236
- };
4237
- communication = {
4238
- chat: "slock_cli",
4239
- runtimeControl: "none"
4240
- };
4241
- session = {
4242
- recovery: "resume_or_fresh"
4243
- };
4244
- model = {
4245
- detectedModelsVerifiedAs: "launchable",
4246
- toLaunchSpec: (modelId, ctx) => {
4247
- if (!ctx) return modelId && modelId !== "default" ? { args: ["--model", modelId] } : { args: [] };
4248
- const launchCtx = {
4249
- ...ctx,
4250
- config: {
4251
- ...ctx.config,
4252
- model: modelId
4253
- }
4254
- };
4255
- const launch = buildPiLaunchOptions(launchCtx);
4256
- return {
4257
- args: launch.args,
4258
- env: launch.env,
4259
- configFiles: [launch.runnerConfigPath],
4260
- params: {
4261
- agentDir: launch.agentDir,
4262
- sessionDir: launch.sessionDir,
4263
- sdkVersion: launch.sdkVersion,
4264
- resources: "extensions/skills/prompt-templates/themes/context-files disabled by Slock policy"
4265
- }
4266
- };
4267
- }
4268
- };
4269
- supportsStdinNotification = false;
4270
- mcpToolPrefix = CHAT_MCP_TOOL_PREFIX2;
4271
- busyDeliveryMode = "none";
4272
- terminateProcessOnTurnEnd = true;
4273
- deferSpawnUntilMessage = true;
4274
- usesSlockCliForCommunication = true;
4275
- sessionId = null;
4276
- sessionAnnounced = false;
4277
- apiKeyErrorAnnounced = false;
4278
- turnEnded = false;
4279
- assistantTextByMessageId = /* @__PURE__ */ new Map();
4280
- shouldDeferWakeMessage(message) {
4281
- return isSystemFirstMessageTask2(message);
4282
- }
4283
- probe() {
4284
- return probePi();
4285
- }
4286
- async detectModels() {
4287
- return null;
4288
- }
4289
- spawn(ctx) {
4290
- this.sessionId = ctx.config.sessionId || null;
4291
- this.sessionAnnounced = false;
4292
- this.apiKeyErrorAnnounced = false;
4293
- this.turnEnded = false;
4294
- this.assistantTextByMessageId.clear();
4295
- const unsupportedMessage = unsupportedPiVersionMessage(PI_SDK_VERSION);
4296
- if (unsupportedMessage) throw new Error(unsupportedMessage);
4297
- const launch = buildPiLaunchOptions(ctx);
4298
- const proc = spawn8(launch.command, launch.args, {
4299
- cwd: ctx.workingDirectory,
4300
- stdio: ["pipe", "pipe", "pipe"],
4301
- env: launch.env,
4302
- shell: false
4303
- });
4304
- proc.stdin?.end();
4305
- return { process: proc };
4306
- }
4307
- parseLine(line) {
4308
- let event;
4309
- try {
4310
- event = JSON.parse(line);
4311
- } catch {
4312
- if (this.apiKeyErrorAnnounced) return [];
4313
- const message = apiKeyErrorMessage(line);
4314
- if (!message) return [];
4315
- this.apiKeyErrorAnnounced = true;
4316
- this.turnEnded = true;
4317
- return [
4318
- { kind: "error", message },
4319
- { kind: "turn_end", sessionId: this.sessionId || void 0 }
4320
- ];
4321
- }
4322
- const events = [];
4323
- if (event.type === "session" && event.id) {
4324
- this.sessionId = event.id;
4325
- }
4326
- if (!this.sessionAnnounced && this.sessionId) {
4327
- events.push({ kind: "session_init", sessionId: this.sessionId });
4328
- this.sessionAnnounced = true;
4329
- }
4330
- switch (event.type) {
4331
- case "agent_start":
4332
- case "turn_start":
4333
- events.push({ kind: "thinking", text: "" });
4334
- break;
4335
- case "message_update":
4336
- case "message_end":
4337
- if (event.message?.role === "assistant") {
4338
- const key = event.message.id || "current";
4339
- const currentText = contentText(event.message.content);
4340
- const previousText = this.assistantTextByMessageId.get(key) ?? "";
4341
- if (currentText.length > previousText.length && currentText.startsWith(previousText)) {
4342
- events.push({ kind: "text", text: currentText.slice(previousText.length) });
4343
- } else if (currentText && currentText !== previousText) {
4344
- events.push({ kind: "text", text: currentText });
4345
- }
4346
- this.assistantTextByMessageId.set(key, currentText);
4347
- if (event.message.stopReason === "error" || event.message.stopReason === "aborted") {
4348
- events.push({ kind: "error", message: event.message.errorMessage || `Request ${event.message.stopReason}` });
4349
- }
4350
- }
4351
- break;
4352
- case "tool_execution_start":
4353
- events.push({
4354
- kind: "tool_call",
4355
- name: event.toolName || "unknown_tool",
4356
- input: event.args
4357
- });
4358
- break;
4359
- case "tool_execution_end":
4360
- events.push({
4361
- kind: "tool_output",
4362
- name: event.toolName || "unknown_tool"
4363
- });
4364
- if (event.isError) {
4365
- events.push({ kind: "error", message: `Pi tool ${event.toolName || "unknown_tool"} failed` });
4366
- }
4367
- break;
4368
- case "compaction_start":
4369
- events.push({ kind: "compaction_started" });
4370
- break;
4371
- case "compaction_end":
4372
- events.push({ kind: "compaction_finished" });
4373
- if (event.errorMessage) events.push({ kind: "error", message: event.errorMessage });
4374
- break;
4375
- case "turn_end":
4376
- case "agent_end":
4377
- if (!this.turnEnded) {
4378
- events.push({ kind: "turn_end", sessionId: this.sessionId || void 0 });
4379
- this.turnEnded = true;
4380
- }
4381
- break;
4382
- }
4383
- return events;
4384
- }
4385
- encodeStdinMessage(_text, _sessionId, _opts) {
4386
- return null;
4387
- }
4388
- buildSystemPrompt(config, _agentId) {
4389
- return buildPiSystemPrompt(config);
4390
- }
4391
- };
4392
-
4393
4187
  // src/drivers/index.ts
4394
4188
  var driverFactories = {
4395
4189
  claude: () => new ClaudeDriver(),
@@ -4398,8 +4192,7 @@ var driverFactories = {
4398
4192
  cursor: () => new CursorDriver(),
4399
4193
  gemini: () => new GeminiDriver(),
4400
4194
  kimi: () => new KimiDriver(),
4401
- opencode: () => new OpenCodeDriver(),
4402
- pi: () => new PiDriver()
4195
+ opencode: () => new OpenCodeDriver()
4403
4196
  };
4404
4197
  function getDriver(runtimeId) {
4405
4198
  const createDriver = driverFactories[runtimeId];
@@ -4412,7 +4205,7 @@ function getDriver(runtimeId) {
4412
4205
 
4413
4206
  // src/workspaces.ts
4414
4207
  import { readdir, rm, stat } from "fs/promises";
4415
- import path12 from "path";
4208
+ import path11 from "path";
4416
4209
  function isValidWorkspaceDirectoryName(directoryName) {
4417
4210
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
4418
4211
  }
@@ -4420,7 +4213,7 @@ function resolveWorkspaceDirectoryPath(dataDir, directoryName) {
4420
4213
  if (!isValidWorkspaceDirectoryName(directoryName)) {
4421
4214
  return null;
4422
4215
  }
4423
- return path12.join(dataDir, directoryName);
4216
+ return path11.join(dataDir, directoryName);
4424
4217
  }
4425
4218
  function emptyWorkspaceDirectorySummary(latestMtime = /* @__PURE__ */ new Date(0)) {
4426
4219
  return {
@@ -4469,7 +4262,7 @@ async function summarizeWorkspaceDirectory(dirPath) {
4469
4262
  return summary;
4470
4263
  }
4471
4264
  const childSummaries = await Promise.all(
4472
- entries.map((entry) => summarizeWorkspaceEntry(path12.join(dirPath, entry.name), entry))
4265
+ entries.map((entry) => summarizeWorkspaceEntry(path11.join(dirPath, entry.name), entry))
4473
4266
  );
4474
4267
  for (const childSummary of childSummaries) {
4475
4268
  summary = mergeWorkspaceDirectorySummaries(summary, childSummary);
@@ -4488,7 +4281,7 @@ async function scanWorkspaceDirectories(dataDir) {
4488
4281
  if (!entry.isDirectory()) {
4489
4282
  return null;
4490
4283
  }
4491
- const dirPath = path12.join(dataDir, entry.name);
4284
+ const dirPath = path11.join(dataDir, entry.name);
4492
4285
  try {
4493
4286
  const summary = await summarizeWorkspaceDirectory(dirPath);
4494
4287
  return {
@@ -4745,12 +4538,12 @@ function findSessionJsonl(root, predicate) {
4745
4538
  for (const entry of entries) {
4746
4539
  if (++visited > maxEntries) return null;
4747
4540
  if (!entry.isFile() || !predicate(entry.name)) continue;
4748
- return path13.join(dir, entry.name);
4541
+ return path12.join(dir, entry.name);
4749
4542
  }
4750
4543
  for (const entry of entries) {
4751
4544
  if (++visited > maxEntries) return null;
4752
4545
  if (!entry.isDirectory()) continue;
4753
- const found = visit(path13.join(dir, entry.name), depth - 1);
4546
+ const found = visit(path12.join(dir, entry.name), depth - 1);
4754
4547
  if (found) return found;
4755
4548
  }
4756
4549
  return null;
@@ -4763,10 +4556,10 @@ function safeSessionFilename(value) {
4763
4556
  }
4764
4557
  function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
4765
4558
  try {
4766
- const dir = path13.join(fallbackDir, ".slock", "runtime-sessions");
4767
- mkdirSync5(dir, { recursive: true });
4768
- const filePath = path13.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
4769
- writeFileSync8(filePath, JSON.stringify({
4559
+ const dir = path12.join(fallbackDir, ".slock", "runtime-sessions");
4560
+ mkdirSync4(dir, { recursive: true });
4561
+ const filePath = path12.join(dir, `${runtime}-${safeSessionFilename(sessionId)}.jsonl`);
4562
+ writeFileSync7(filePath, JSON.stringify({
4770
4563
  type: "runtime_session_handoff",
4771
4564
  runtime,
4772
4565
  sessionId,
@@ -4785,7 +4578,7 @@ function writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir) {
4785
4578
  }
4786
4579
  }
4787
4580
  function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), fallbackDir) {
4788
- const directPath = path13.isAbsolute(sessionId) ? sessionId : null;
4581
+ const directPath = path12.isAbsolute(sessionId) ? sessionId : null;
4789
4582
  if (directPath) {
4790
4583
  try {
4791
4584
  if (statSync2(directPath).isFile()) {
@@ -4794,7 +4587,7 @@ function resolveRuntimeSessionRef(runtime, sessionId, homeDir = os6.homedir(), f
4794
4587
  } catch {
4795
4588
  }
4796
4589
  }
4797
- 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;
4590
+ const resolvedPath = runtime === "claude" ? findSessionJsonl(path12.join(homeDir, ".claude", "projects"), (filename) => filename === `${sessionId}.jsonl`) : runtime === "codex" ? findSessionJsonl(path12.join(homeDir, ".codex", "sessions"), (filename) => filename.endsWith(".jsonl") && filename.includes(sessionId)) : null;
4798
4591
  if (!resolvedPath && fallbackDir) {
4799
4592
  const fallback = writeRuntimeSessionHandoff(runtime, sessionId, fallbackDir);
4800
4593
  if (fallback) return fallback;
@@ -5645,10 +5438,10 @@ function getMessageDeliveryText(driver) {
5645
5438
  function getBusyDeliveryNote(driver) {
5646
5439
  if (!driver.supportsStdinNotification) return "";
5647
5440
  if (driver.busyDeliveryMode === "direct") {
5648
- return "\n\nNote: While you are busy, new messages may be delivered directly into your active turn. Handle them when appropriate and keep working.";
5441
+ return "\n\nNote: While you are busy, the daemon may write batched inbox-count notifications into your active turn. Call check_messages to read the pending messages before context-sensitive side effects.";
5649
5442
  }
5650
5443
  if (driver.busyDeliveryMode === "gated") {
5651
- return "\n\nNote: While you are busy, new messages may be delivered at runtime-observed safe boundaries in your active turn. If no safe boundary is available, they will be delivered after the current turn ends.";
5444
+ 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.";
5652
5445
  }
5653
5446
  return "\n\nNote: While you are busy, you may receive [System notification: ...] messages. Finish your current step, then call check_messages to check for messages.";
5654
5447
  }
@@ -5724,13 +5517,16 @@ var AgentProcessManager = class _AgentProcessManager {
5724
5517
  getVisibleBoundary(agentId, target) {
5725
5518
  return this.agentVisibleBoundaries.get(agentId)?.get(target);
5726
5519
  }
5727
- pendingVisibleMessages(agentId, target) {
5728
- const collect = (messages) => (messages ?? []).filter((message) => formatMessageTarget(message) === target && typeof message.seq === "number" && message.seq > 0);
5520
+ allPendingVisibleMessages(agentId) {
5521
+ const collect = (messages) => (messages ?? []).filter((message) => typeof message.seq === "number" && message.seq > 0);
5729
5522
  return [
5730
5523
  ...collect(this.agents.get(agentId)?.inbox),
5731
5524
  ...collect(this.startingInboxes.get(agentId))
5732
5525
  ];
5733
5526
  }
5527
+ pendingVisibleMessages(agentId, target) {
5528
+ return this.allPendingVisibleMessages(agentId).filter((message) => formatMessageTarget(message) === target);
5529
+ }
5734
5530
  /**
5735
5531
  * Single-inbox consume point for messages that have been rendered into the
5736
5532
  * agent-visible input surface. Daemon pending inbox is only a runner cache:
@@ -5801,8 +5597,71 @@ var AgentProcessManager = class _AgentProcessManager {
5801
5597
  return {
5802
5598
  getBoundary: (target) => this.getVisibleBoundary(agentId, target),
5803
5599
  getPendingMessages: (target) => this.pendingVisibleMessages(agentId, target),
5804
- consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input)
5600
+ getAllPendingMessages: () => this.allPendingVisibleMessages(agentId),
5601
+ consumeVisibleMessages: (input) => this.consumeVisibleMessages(agentId, input),
5602
+ recordDrainOutcome: (input) => {
5603
+ this.recordDaemonTrace("daemon.agent.drain.outcome", {
5604
+ agentId,
5605
+ source: input.source,
5606
+ since_cursor_kind: input.sinceCursorKind ?? void 0,
5607
+ notified_count: input.notifiedCount,
5608
+ drained_count: input.drainedCount,
5609
+ has_more: input.hasMore
5610
+ });
5611
+ },
5612
+ recordProxyFailure: (input) => this.recordAgentProxyFailure(agentId, input),
5613
+ recordFreshnessDecision: (input) => {
5614
+ this.recordDaemonTrace("daemon.agent.inbox.freshness_decision", {
5615
+ agentId,
5616
+ action: input.action,
5617
+ decision: input.decision,
5618
+ target: input.target,
5619
+ inbox_trust_state: input.inboxTrustState,
5620
+ reason: input.reason,
5621
+ pending_count: input.pendingCount,
5622
+ pending_max_seq: input.pendingMaxSeq,
5623
+ model_seen_seq: input.modelSeenSeq,
5624
+ held_message_count: input.heldMessageCount,
5625
+ omitted_message_count: input.omittedMessageCount
5626
+ });
5627
+ this.recordFreshnessDecisionActivity(agentId, input);
5628
+ }
5629
+ };
5630
+ }
5631
+ recordAgentProxyFailure(agentId, input) {
5632
+ this.recordDaemonTrace("daemon.agent.proxy.failure", {
5633
+ agentId,
5634
+ method: input.method,
5635
+ path: input.pathname,
5636
+ query_keys: input.queryKeys,
5637
+ error_name: input.errorName,
5638
+ error_message: input.errorMessage
5639
+ }, "error");
5640
+ }
5641
+ recordFreshnessDecisionActivity(agentId, input) {
5642
+ if (input.decision !== "local_hold" && input.decision !== "syncing_hold") return;
5643
+ const ap = this.agents.get(agentId);
5644
+ const messageCount = input.pendingCount ?? input.heldMessageCount ?? 0;
5645
+ const title = input.action === "send" ? "Send held by freshness check" : input.action === "task_claim" ? "Task claim held by freshness check" : "Task update held by freshness check";
5646
+ const entry = {
5647
+ kind: "slock_action",
5648
+ title,
5649
+ text: [
5650
+ input.target ? `target: ${input.target}` : null,
5651
+ `new messages: ${messageCount} newer message${messageCount === 1 ? "" : "s"}`,
5652
+ `decision: ${input.decision === "syncing_hold" ? "syncing hold" : "local hold"}; review the newer context before retrying`
5653
+ ].filter((line) => Boolean(line)).join("\n")
5805
5654
  };
5655
+ if (ap) ap.activityClientSeq += 1;
5656
+ this.sendToServer({
5657
+ type: "agent:activity",
5658
+ agentId,
5659
+ activity: ap?.lastActivity || "online",
5660
+ detail: ap?.lastActivityDetail || "",
5661
+ entries: [entry],
5662
+ launchId: ap?.launchId || void 0,
5663
+ clientSeq: ap?.activityClientSeq
5664
+ });
5806
5665
  }
5807
5666
  recordDaemonTrace(name, attrs, status = "ok", parentTraceparent) {
5808
5667
  const span = this.tracer.startSpan(name, {
@@ -6028,26 +5887,26 @@ var AgentProcessManager = class _AgentProcessManager {
6028
5887
  this.recordDaemonTrace("daemon.agent.spawn.started", this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId));
6029
5888
  try {
6030
5889
  const driver = this.driverResolver(config.runtime || "claude");
6031
- const agentDataDir = path13.join(this.dataDir, agentId);
5890
+ const agentDataDir = path12.join(this.dataDir, agentId);
6032
5891
  await mkdir(agentDataDir, { recursive: true });
6033
5892
  const runtimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
6034
- const memoryMdPath = path13.join(agentDataDir, "MEMORY.md");
5893
+ const memoryMdPath = path12.join(agentDataDir, "MEMORY.md");
6035
5894
  try {
6036
5895
  await access(memoryMdPath);
6037
5896
  } catch {
6038
5897
  const initialMemoryMd = buildInitialMemoryMd(runtimeConfig);
6039
5898
  await writeFile(memoryMdPath, initialMemoryMd);
6040
5899
  }
6041
- const notesDir = path13.join(agentDataDir, "notes");
5900
+ const notesDir = path12.join(agentDataDir, "notes");
6042
5901
  await mkdir(notesDir, { recursive: true });
6043
5902
  if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
6044
5903
  const seedFiles = buildOnboardingSeedFiles();
6045
5904
  for (const { relativePath, content } of seedFiles) {
6046
- const fullPath = path13.join(agentDataDir, relativePath);
5905
+ const fullPath = path12.join(agentDataDir, relativePath);
6047
5906
  try {
6048
5907
  await access(fullPath);
6049
5908
  } catch {
6050
- await mkdir(path13.dirname(fullPath), { recursive: true });
5909
+ await mkdir(path12.dirname(fullPath), { recursive: true });
6051
5910
  await writeFile(fullPath, content);
6052
5911
  }
6053
5912
  }
@@ -6857,6 +6716,11 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6857
6716
  }
6858
6717
  if (ap.driver.busyDeliveryMode === "gated") {
6859
6718
  ap.pendingNotificationCount++;
6719
+ if (!ap.notificationTimer) {
6720
+ ap.notificationTimer = setTimeout(() => {
6721
+ this.sendStdinNotification(agentId);
6722
+ }, 3e3);
6723
+ }
6860
6724
  this.recordGatedSteeringEvent(agentId, ap, "buffer", {
6861
6725
  reason: "busy_message",
6862
6726
  pendingMessages: ap.inbox.length
@@ -6869,7 +6733,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6869
6733
  session_id_present: true,
6870
6734
  launchId: ap.launchId || void 0,
6871
6735
  inbox_count: ap.inbox.length,
6872
- pending_notification_count: ap.pendingNotificationCount
6736
+ pending_notification_count: ap.pendingNotificationCount,
6737
+ notification_timer_present: Boolean(ap.notificationTimer)
6873
6738
  }));
6874
6739
  return true;
6875
6740
  }
@@ -6892,7 +6757,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6892
6757
  return true;
6893
6758
  }
6894
6759
  async resetWorkspace(agentId) {
6895
- const agentDataDir = path13.join(this.dataDir, agentId);
6760
+ const agentDataDir = path12.join(this.dataDir, agentId);
6896
6761
  try {
6897
6762
  await rm2(agentDataDir, { recursive: true, force: true });
6898
6763
  logger.info(`[Agent ${agentId}] Workspace reset complete (${agentDataDir})`);
@@ -6929,7 +6794,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
6929
6794
  return result;
6930
6795
  }
6931
6796
  buildRuntimeProfileReport(agentId, config, sessionId, launchId) {
6932
- const workspacePath = path13.join(this.dataDir, agentId);
6797
+ const workspacePath = path12.join(this.dataDir, agentId);
6933
6798
  return {
6934
6799
  agentId,
6935
6800
  launchId,
@@ -7186,7 +7051,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7186
7051
  }
7187
7052
  // Workspace file browsing
7188
7053
  async getFileTree(agentId, dirPath) {
7189
- const agentDir = path13.join(this.dataDir, agentId);
7054
+ const agentDir = path12.join(this.dataDir, agentId);
7190
7055
  try {
7191
7056
  await stat2(agentDir);
7192
7057
  } catch {
@@ -7194,8 +7059,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7194
7059
  }
7195
7060
  let targetDir = agentDir;
7196
7061
  if (dirPath) {
7197
- const resolved = path13.resolve(agentDir, dirPath);
7198
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
7062
+ const resolved = path12.resolve(agentDir, dirPath);
7063
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
7199
7064
  return [];
7200
7065
  }
7201
7066
  targetDir = resolved;
@@ -7203,14 +7068,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7203
7068
  return this.listDirectoryChildren(targetDir, agentDir);
7204
7069
  }
7205
7070
  async readFile(agentId, filePath) {
7206
- const agentDir = path13.join(this.dataDir, agentId);
7207
- const resolved = path13.resolve(agentDir, filePath);
7208
- if (!resolved.startsWith(agentDir + path13.sep) && resolved !== agentDir) {
7071
+ const agentDir = path12.join(this.dataDir, agentId);
7072
+ const resolved = path12.resolve(agentDir, filePath);
7073
+ if (!resolved.startsWith(agentDir + path12.sep) && resolved !== agentDir) {
7209
7074
  throw new Error("Access denied");
7210
7075
  }
7211
7076
  const info = await stat2(resolved);
7212
7077
  if (info.isDirectory()) throw new Error("Cannot read a directory");
7213
- const ext = path13.extname(resolved).toLowerCase();
7078
+ const ext = path12.extname(resolved).toLowerCase();
7214
7079
  if (WORKSPACE_TEXT_EXTENSIONS.has(ext) || ext === "") {
7215
7080
  if (info.size > WORKSPACE_TEXT_FILE_MAX_BYTES) throw new Error("File too large");
7216
7081
  const content = await readFile(resolved, "utf-8");
@@ -7245,13 +7110,13 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7245
7110
  const agent = this.agents.get(agentId);
7246
7111
  const runtime = runtimeHint || agent?.config.runtime || "claude";
7247
7112
  const home = os6.homedir();
7248
- const workspaceDir = path13.join(this.dataDir, agentId);
7113
+ const workspaceDir = path12.join(this.dataDir, agentId);
7249
7114
  const paths = _AgentProcessManager.SKILL_PATHS[runtime] || _AgentProcessManager.SKILL_PATHS.claude;
7250
7115
  const globalResults = await Promise.all(
7251
- paths.global.map((p) => this.scanSkillsDir(path13.join(home, p)))
7116
+ paths.global.map((p) => this.scanSkillsDir(path12.join(home, p)))
7252
7117
  );
7253
7118
  const workspaceResults = await Promise.all(
7254
- paths.workspace.map((p) => this.scanSkillsDir(path13.join(workspaceDir, p)))
7119
+ paths.workspace.map((p) => this.scanSkillsDir(path12.join(workspaceDir, p)))
7255
7120
  );
7256
7121
  const dedup = (skills) => {
7257
7122
  const seen = /* @__PURE__ */ new Set();
@@ -7280,7 +7145,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7280
7145
  const skills = [];
7281
7146
  for (const entry of entries) {
7282
7147
  if (entry.isDirectory() || entry.isSymbolicLink()) {
7283
- const skillMd = path13.join(dir, entry.name, "SKILL.md");
7148
+ const skillMd = path12.join(dir, entry.name, "SKILL.md");
7284
7149
  try {
7285
7150
  const content = await readFile(skillMd, "utf-8");
7286
7151
  const skill = this.parseSkillMd(entry.name, content);
@@ -7291,7 +7156,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7291
7156
  } else if (entry.name.endsWith(".md")) {
7292
7157
  const cmdName = entry.name.replace(/\.md$/, "");
7293
7158
  try {
7294
- const content = await readFile(path13.join(dir, entry.name), "utf-8");
7159
+ const content = await readFile(path12.join(dir, entry.name), "utf-8");
7295
7160
  const skill = this.parseSkillMd(cmdName, content);
7296
7161
  skill.sourcePath = dir;
7297
7162
  skills.push(skill);
@@ -7694,11 +7559,17 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7694
7559
  if (reason !== "turn_end") {
7695
7560
  if (ap.gatedSteering.toolBoundaryFlushDisabled) return false;
7696
7561
  if (ap.gatedSteering.compacting || ap.gatedSteering.outstandingToolUses > 0) return false;
7562
+ this.recordGatedSteeringEvent(agentId, ap, "notify", { reason, pendingMessages: ap.inbox.length });
7563
+ return this.sendStdinNotification(agentId);
7697
7564
  }
7698
7565
  const pendingMessages = ap.inbox.length;
7699
7566
  const pendingNotificationCount = ap.pendingNotificationCount;
7700
7567
  const nextMessages = ap.inbox.splice(0, ap.inbox.length);
7701
7568
  ap.pendingNotificationCount = 0;
7569
+ if (ap.notificationTimer) {
7570
+ clearTimeout(ap.notificationTimer);
7571
+ ap.notificationTimer = null;
7572
+ }
7702
7573
  ap.gatedSteering.lastFlushReason = reason;
7703
7574
  if (reason !== "turn_end") {
7704
7575
  ap.gatedSteering.inFlightBatch = {
@@ -7726,28 +7597,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7726
7597
  }
7727
7598
  flushCompactionBoundaryMessages(agentId, ap) {
7728
7599
  if (!ap.sessionId || !ap.driver.supportsStdinNotification || ap.inbox.length === 0) return false;
7729
- if (ap.driver.busyDeliveryMode === "gated") {
7730
- return this.tryFlushGatedSteering(agentId, ap, "compaction_finished");
7731
- }
7732
- if (ap.driver.busyDeliveryMode === "direct") {
7733
- const pendingMessages = ap.inbox.length;
7734
- const pendingNotificationCount = ap.pendingNotificationCount;
7735
- const nextMessages = ap.inbox.splice(0, ap.inbox.length);
7736
- ap.pendingNotificationCount = 0;
7737
- if (ap.notificationTimer) {
7738
- clearTimeout(ap.notificationTimer);
7739
- ap.notificationTimer = null;
7740
- }
7741
- this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.flush", {
7742
- mode: "direct",
7743
- messageCount: nextMessages.length
7744
- });
7745
- if (this.deliverMessagesViaStdin(agentId, ap, nextMessages, "busy")) {
7746
- return true;
7747
- }
7748
- ap.pendingNotificationCount += pendingNotificationCount || pendingMessages;
7749
- return false;
7750
- }
7751
7600
  if (ap.pendingNotificationCount > 0) {
7752
7601
  if (ap.notificationTimer) {
7753
7602
  clearTimeout(ap.notificationTimer);
@@ -7966,6 +7815,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7966
7815
  if (ap.inbox.length > 0 && ap.driver.supportsStdinNotification && ap.sessionId) {
7967
7816
  const nextMessages = ap.inbox.splice(0, ap.inbox.length);
7968
7817
  ap.pendingNotificationCount = 0;
7818
+ if (ap.notificationTimer) {
7819
+ clearTimeout(ap.notificationTimer);
7820
+ ap.notificationTimer = null;
7821
+ }
7969
7822
  if (ap.driver.busyDeliveryMode === "gated") {
7970
7823
  ap.gatedSteering.lastFlushReason = "turn_end";
7971
7824
  this.recordGatedSteeringEvent(agentId, ap, "flush", {
@@ -8109,13 +7962,16 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8109
7962
  /** Send a batched notification to the agent via stdin about pending messages */
8110
7963
  sendStdinNotification(agentId) {
8111
7964
  const ap = this.agents.get(agentId);
8112
- if (!ap) return;
7965
+ if (!ap) return false;
8113
7966
  const count = ap.pendingNotificationCount;
8114
7967
  ap.pendingNotificationCount = 0;
8115
- ap.notificationTimer = null;
8116
- if (count === 0) return;
8117
- if (ap.isIdle) return;
8118
- if (!ap.sessionId) return;
7968
+ if (ap.notificationTimer) {
7969
+ clearTimeout(ap.notificationTimer);
7970
+ ap.notificationTimer = null;
7971
+ }
7972
+ if (count === 0) return false;
7973
+ if (ap.isIdle) return false;
7974
+ if (!ap.sessionId) return false;
8119
7975
  if (ap.gatedSteering.compacting) {
8120
7976
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_suppressed", {
8121
7977
  pendingNotificationCount: count,
@@ -8126,30 +7982,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8126
7982
  logger.info(
8127
7983
  `[Agent ${agentId}] Suppressing stdin delivery until context compaction finishes; pending=${ap.inbox.length}`
8128
7984
  );
8129
- return;
8130
- }
8131
- if (ap.driver.busyDeliveryMode === "gated") {
8132
- this.recordGatedSteeringEvent(agentId, ap, "suppress", {
8133
- reason: "timer_notification_not_safe_boundary",
8134
- pendingNotificationCount: count
8135
- });
8136
- ap.pendingNotificationCount += count;
8137
- logger.info(
8138
- `[Agent ${agentId}] Suppressing raw busy stdin notification until Claude gated steering boundary; pending=${ap.inbox.length}`
8139
- );
8140
- return;
8141
- }
8142
- if (ap.driver.busyDeliveryMode === "direct" && ap.inbox.length > 0) {
8143
- const queuedMessages = ap.inbox.splice(0, ap.inbox.length);
8144
- console.log(`[Agent ${agentId}] Delivering queued message via stdin while busy`);
8145
- if (this.deliverMessagesViaStdin(agentId, ap, queuedMessages, "busy")) {
8146
- return;
8147
- }
8148
- ap.pendingNotificationCount += count;
8149
- return;
7985
+ return false;
8150
7986
  }
8151
- const notification = `[System notification: You have ${count} new message${count > 1 ? "s" : ""} waiting. Call check_messages to read ${count > 1 ? "them" : "it"} when you're ready.]`;
8152
- logger.info(`[Agent ${agentId}] Sending stdin notification: ${count} message(s)`);
7987
+ const inboxCount = ap.inbox.length;
7988
+ if (inboxCount === 0) return false;
7989
+ const notification = `[System notification: You have ${inboxCount} pending inbox message${inboxCount > 1 ? "s" : ""}. Call check_messages to read ${inboxCount > 1 ? "them" : "it"} when you're ready.]`;
7990
+ logger.info(`[Agent ${agentId}] Sending stdin notification: ${inboxCount} pending inbox message(s)`);
8153
7991
  const encoded = ap.driver.encodeStdinMessage(notification, ap.sessionId, { mode: "busy" });
8154
7992
  if (encoded) {
8155
7993
  ap.process.stdin?.write(encoded + "\n");
@@ -8161,9 +7999,12 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8161
7999
  outcome: "written",
8162
8000
  mode: "busy",
8163
8001
  pending_notification_count: count,
8002
+ inbox_count: inboxCount,
8164
8003
  session_id_present: true
8165
8004
  });
8005
+ return true;
8166
8006
  } else {
8007
+ ap.pendingNotificationCount += count;
8167
8008
  this.recordDaemonTrace("daemon.agent.stdin_notification", {
8168
8009
  agentId,
8169
8010
  runtime: ap.config.runtime,
@@ -8172,8 +8013,10 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
8172
8013
  outcome: "encode_failed",
8173
8014
  mode: "busy",
8174
8015
  pending_notification_count: count,
8016
+ inbox_count: inboxCount,
8175
8017
  session_id_present: true
8176
8018
  }, "error");
8019
+ return false;
8177
8020
  }
8178
8021
  }
8179
8022
  /** Deliver a message to an agent via stdin, formatting it the same way as the MCP bridge */
@@ -8279,8 +8122,8 @@ ${RESPONSE_TARGET_HINT}`);
8279
8122
  const nodes = [];
8280
8123
  for (const entry of entries) {
8281
8124
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
8282
- const fullPath = path13.join(dir, entry.name);
8283
- const relativePath = path13.relative(rootDir, fullPath);
8125
+ const fullPath = path12.join(dir, entry.name);
8126
+ const relativePath = path12.relative(rootDir, fullPath);
8284
8127
  let info;
8285
8128
  try {
8286
8129
  info = await stat2(fullPath);
@@ -8409,10 +8252,12 @@ var DaemonConnection = class {
8409
8252
  messageKind = msg.type;
8410
8253
  this.markInbound(messageKind);
8411
8254
  this.resetWatchdog();
8412
- this.trace("daemon.connection.inbound_received", {
8413
- message_type: messageKind,
8414
- last_inbound_age_ms_bucket: "0"
8415
- });
8255
+ if (messageKind !== "ping") {
8256
+ this.trace("daemon.connection.inbound_received", {
8257
+ message_type: messageKind,
8258
+ last_inbound_age_ms_bucket: "0"
8259
+ });
8260
+ }
8416
8261
  this.options.onMessage(msg);
8417
8262
  } catch (err) {
8418
8263
  this.markInbound("invalid_json");
@@ -8583,9 +8428,9 @@ var ReminderCache = class {
8583
8428
 
8584
8429
  // src/machineLock.ts
8585
8430
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
8586
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync9 } from "fs";
8431
+ import { mkdirSync as mkdirSync5, readFileSync as readFileSync5, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync8 } from "fs";
8587
8432
  import os7 from "os";
8588
- import path14 from "path";
8433
+ import path13 from "path";
8589
8434
  var INCOMPLETE_LOCK_STALE_MS = 3e4;
8590
8435
  var DaemonMachineLockConflictError = class extends Error {
8591
8436
  code = "DAEMON_MACHINE_LOCK_HELD";
@@ -8607,7 +8452,7 @@ function resolveDefaultMachineStateRoot() {
8607
8452
  return resolveSlockHomePath("machines");
8608
8453
  }
8609
8454
  function ownerPath(lockDir) {
8610
- return path14.join(lockDir, "owner.json");
8455
+ return path13.join(lockDir, "owner.json");
8611
8456
  }
8612
8457
  function readOwner(lockDir) {
8613
8458
  try {
@@ -8637,13 +8482,13 @@ function acquireDaemonMachineLock(options) {
8637
8482
  const rootDir = options.rootDir ?? resolveDefaultMachineStateRoot();
8638
8483
  const fingerprint = apiKeyFingerprint(options.apiKey);
8639
8484
  const lockId = getDaemonMachineLockId(options.apiKey);
8640
- const machineDir = path14.join(rootDir, lockId);
8641
- const lockDir = path14.join(machineDir, "daemon.lock");
8485
+ const machineDir = path13.join(rootDir, lockId);
8486
+ const lockDir = path13.join(machineDir, "daemon.lock");
8642
8487
  const token = randomUUID2();
8643
- mkdirSync6(machineDir, { recursive: true });
8488
+ mkdirSync5(machineDir, { recursive: true });
8644
8489
  for (let attempt = 0; attempt < 2; attempt += 1) {
8645
8490
  try {
8646
- mkdirSync6(lockDir);
8491
+ mkdirSync5(lockDir);
8647
8492
  const owner = {
8648
8493
  pid: process.pid,
8649
8494
  token,
@@ -8653,7 +8498,7 @@ function acquireDaemonMachineLock(options) {
8653
8498
  apiKeyFingerprint: fingerprint.slice(0, 16)
8654
8499
  };
8655
8500
  try {
8656
- writeFileSync9(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
8501
+ writeFileSync8(ownerPath(lockDir), `${JSON.stringify(owner, null, 2)}
8657
8502
  `, { mode: 384 });
8658
8503
  } catch (err) {
8659
8504
  rmSync3(lockDir, { recursive: true, force: true });
@@ -8690,8 +8535,8 @@ function acquireDaemonMachineLock(options) {
8690
8535
  }
8691
8536
 
8692
8537
  // src/localTraceSink.ts
8693
- import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync10 } from "fs";
8694
- import path15 from "path";
8538
+ import { appendFileSync, mkdirSync as mkdirSync6, readdirSync as readdirSync3, rmSync as rmSync4, statSync as statSync4, writeFileSync as writeFileSync9 } from "fs";
8539
+ import path14 from "path";
8695
8540
  var DEFAULT_MAX_FILE_BYTES = 5 * 1024 * 1024;
8696
8541
  var DEFAULT_MAX_FILE_AGE_MS = 5 * 60 * 1e3;
8697
8542
  var DEFAULT_MAX_FILES = 8;
@@ -8727,7 +8572,7 @@ var LocalRotatingTraceSink = class {
8727
8572
  currentSize = 0;
8728
8573
  sequence = 0;
8729
8574
  constructor(options) {
8730
- this.traceDir = path15.join(options.machineDir, "traces");
8575
+ this.traceDir = path14.join(options.machineDir, "traces");
8731
8576
  this.maxFileBytes = Math.max(1024, Math.floor(options.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES));
8732
8577
  const baseAgeMs = Math.max(1e3, Math.floor(options.maxFileAgeMs ?? DEFAULT_MAX_FILE_AGE_MS));
8733
8578
  const ageJitterMs = Math.max(0, Math.floor(options.maxFileAgeJitterMs ?? 0));
@@ -8753,15 +8598,15 @@ var LocalRotatingTraceSink = class {
8753
8598
  return this.currentFile;
8754
8599
  }
8755
8600
  ensureFile(nextBytes) {
8756
- mkdirSync7(this.traceDir, { recursive: true, mode: 448 });
8601
+ mkdirSync6(this.traceDir, { recursive: true, mode: 448 });
8757
8602
  const nowMs = this.nowMsProvider();
8758
8603
  const shouldRotateForAge = this.currentFileOpenedAtMs !== null && nowMs - this.currentFileOpenedAtMs >= this.maxFileAgeMs;
8759
8604
  if (!this.currentFile || this.currentSize + nextBytes > this.maxFileBytes || shouldRotateForAge) {
8760
- this.currentFile = path15.join(
8605
+ this.currentFile = path14.join(
8761
8606
  this.traceDir,
8762
8607
  `daemon-trace-${safeTimestamp(nowMs)}-${process.pid}-${String(this.sequence++).padStart(4, "0")}.jsonl`
8763
8608
  );
8764
- writeFileSync10(this.currentFile, "", { flag: "a", mode: 384 });
8609
+ writeFileSync9(this.currentFile, "", { flag: "a", mode: 384 });
8765
8610
  this.currentSize = statSync4(this.currentFile).size;
8766
8611
  this.currentFileOpenedAtMs = nowMs;
8767
8612
  this.pruneOldFiles();
@@ -8772,7 +8617,7 @@ var LocalRotatingTraceSink = class {
8772
8617
  const excess = files.length - this.maxFiles;
8773
8618
  if (excess <= 0) return;
8774
8619
  for (const file of files.slice(0, excess)) {
8775
- rmSync4(path15.join(this.traceDir, file), { force: true });
8620
+ rmSync4(path14.join(this.traceDir, file), { force: true });
8776
8621
  }
8777
8622
  }
8778
8623
  };
@@ -8859,11 +8704,11 @@ function isDiagnosticErrorAttr(key) {
8859
8704
  import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
8860
8705
  import { gzipSync } from "zlib";
8861
8706
  import { mkdir as mkdir2, readFile as readFile2, readdir as readdir3, stat as stat3, writeFile as writeFile2 } from "fs/promises";
8862
- import path16 from "path";
8707
+ import path15 from "path";
8863
8708
 
8864
8709
  // src/directUploadCapability.ts
8865
- function joinUrl(base, path18) {
8866
- return `${base.replace(/\/+$/, "")}${path18}`;
8710
+ function joinUrl(base, path17) {
8711
+ return `${base.replace(/\/+$/, "")}${path17}`;
8867
8712
  }
8868
8713
  function jsonHeaders(apiKey) {
8869
8714
  return {
@@ -9082,7 +8927,7 @@ var DaemonTraceBundleUploader = class {
9082
8927
  }, nextMs);
9083
8928
  }
9084
8929
  async findUploadCandidates() {
9085
- const traceDir = path16.join(this.options.machineDir, "traces");
8930
+ const traceDir = path15.join(this.options.machineDir, "traces");
9086
8931
  let names;
9087
8932
  try {
9088
8933
  names = await readdir3(traceDir);
@@ -9094,8 +8939,8 @@ var DaemonTraceBundleUploader = class {
9094
8939
  const currentFile = this.options.currentFileProvider?.();
9095
8940
  const candidates = [];
9096
8941
  for (const name of names.filter((entry) => entry.startsWith("daemon-trace-") && entry.endsWith(".jsonl")).sort()) {
9097
- const file = path16.join(traceDir, name);
9098
- if (currentFile && path16.resolve(file) === path16.resolve(currentFile)) continue;
8942
+ const file = path15.join(traceDir, name);
8943
+ if (currentFile && path15.resolve(file) === path15.resolve(currentFile)) continue;
9099
8944
  if (await this.isUploaded(file)) continue;
9100
8945
  try {
9101
8946
  const info = await stat3(file);
@@ -9169,8 +9014,8 @@ var DaemonTraceBundleUploader = class {
9169
9014
  }
9170
9015
  }
9171
9016
  uploadStatePath(file) {
9172
- const stateDir = path16.join(this.options.machineDir, "trace-uploads");
9173
- return path16.join(stateDir, `${path16.basename(file)}.uploaded.json`);
9017
+ const stateDir = path15.join(this.options.machineDir, "trace-uploads");
9018
+ return path15.join(stateDir, `${path15.basename(file)}.uploaded.json`);
9174
9019
  }
9175
9020
  async isUploaded(file) {
9176
9021
  try {
@@ -9182,9 +9027,9 @@ var DaemonTraceBundleUploader = class {
9182
9027
  }
9183
9028
  async markUploaded(file, metadata) {
9184
9029
  const stateFile = this.uploadStatePath(file);
9185
- await mkdir2(path16.dirname(stateFile), { recursive: true, mode: 448 });
9030
+ await mkdir2(path15.dirname(stateFile), { recursive: true, mode: 448 });
9186
9031
  await writeFile2(stateFile, `${JSON.stringify({
9187
- file: path16.basename(file),
9032
+ file: path15.basename(file),
9188
9033
  uploadedAt: (/* @__PURE__ */ new Date()).toISOString(),
9189
9034
  ...metadata
9190
9035
  }, null, 2)}
@@ -9206,7 +9051,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
9206
9051
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels"];
9207
9052
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
9208
9053
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
9209
- var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
9054
+ var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
9210
9055
  var RunnerCredentialMintError2 = class extends Error {
9211
9056
  code;
9212
9057
  retryable;
@@ -9242,9 +9087,9 @@ function runnerCredentialErrorDetail2(error) {
9242
9087
  async function waitForRunnerCredentialRetry2() {
9243
9088
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
9244
9089
  }
9245
- function parseDaemonCliArgs(args, env = {}) {
9090
+ function parseDaemonCliArgs(args) {
9246
9091
  let serverUrl = "";
9247
- let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
9092
+ let apiKey = "";
9248
9093
  for (let i = 0; i < args.length; i++) {
9249
9094
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
9250
9095
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -9261,23 +9106,23 @@ function readDaemonVersion(moduleUrl = import.meta.url) {
9261
9106
  }
9262
9107
  }
9263
9108
  function resolveChatBridgePath(moduleUrl = import.meta.url) {
9264
- const dirname = path17.dirname(fileURLToPath2(moduleUrl));
9265
- const jsPath = path17.resolve(dirname, "chat-bridge.js");
9109
+ const dirname = path16.dirname(fileURLToPath(moduleUrl));
9110
+ const jsPath = path16.resolve(dirname, "chat-bridge.js");
9266
9111
  try {
9267
9112
  accessSync(jsPath);
9268
9113
  return jsPath;
9269
9114
  } catch {
9270
- return path17.resolve(dirname, "chat-bridge.ts");
9115
+ return path16.resolve(dirname, "chat-bridge.ts");
9271
9116
  }
9272
9117
  }
9273
9118
  function resolveSlockCliPath(moduleUrl = import.meta.url) {
9274
- const thisDir = path17.dirname(fileURLToPath2(moduleUrl));
9275
- const bundledDistPath = path17.resolve(thisDir, "cli", "index.js");
9119
+ const thisDir = path16.dirname(fileURLToPath(moduleUrl));
9120
+ const bundledDistPath = path16.resolve(thisDir, "cli", "index.js");
9276
9121
  try {
9277
9122
  accessSync(bundledDistPath);
9278
9123
  return bundledDistPath;
9279
9124
  } catch {
9280
- const workspaceDistPath = path17.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
9125
+ const workspaceDistPath = path16.resolve(thisDir, "..", "..", "cli", "dist", "index.js");
9281
9126
  accessSync(workspaceDistPath);
9282
9127
  return workspaceDistPath;
9283
9128
  }
@@ -9456,7 +9301,7 @@ var DaemonCore = class {
9456
9301
  }
9457
9302
  resolveMachineStateRoot() {
9458
9303
  if (this.options.machineStateDir) return this.options.machineStateDir;
9459
- if (this.options.dataDir) return path17.join(path17.dirname(this.options.dataDir), "machines");
9304
+ if (this.options.dataDir) return path16.join(path16.dirname(this.options.dataDir), "machines");
9460
9305
  return resolveDefaultMachineStateRoot();
9461
9306
  }
9462
9307
  shouldEnableLocalTrace() {
@@ -9958,8 +9803,6 @@ var DaemonCore = class {
9958
9803
  };
9959
9804
 
9960
9805
  export {
9961
- DAEMON_API_KEY_ENV,
9962
- scrubDaemonAuthEnv,
9963
9806
  resolveWorkspaceDirectoryPath,
9964
9807
  scanWorkspaceDirectories,
9965
9808
  deleteWorkspaceDirectory,