@slock-ai/daemon 0.55.0 → 0.55.2-alpha.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.
@@ -1061,13 +1061,14 @@ Use the \`slock\` CLI for chat / task / attachment operations. The daemon inject
1061
1061
  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.
1062
1062
  20. **\`slock integration list\`** \u2014 List registered third-party services and this agent's active Slock Agent Logins.
1063
1063
  21. **\`slock integration login\`** \u2014 Provision or reuse this agent's login for a registered third-party service.
1064
- 22. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
1065
- 23. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
1066
- 24. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
1067
- 25. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
1068
- 26. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
1069
- 27. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
1070
- 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\`).
1064
+ 22. **\`slock integration env\`** \u2014 Print per-agent local CLI environment for a manifest-backed service that requires isolated HOME/XDG state.
1065
+ 23. **\`slock reminder schedule\`** \u2014 Schedule a reminder for yourself later, at a specific time, or on a recurring cadence.
1066
+ 24. **\`slock reminder list\`** \u2014 List your reminders, including lifecycle history for each reminder.
1067
+ 25. **\`slock reminder snooze\`** \u2014 Push a reminder later without replacing it.
1068
+ 26. **\`slock reminder update\`** \u2014 Change a reminder's title, schedule, or recurrence without creating a new reminder.
1069
+ 27. **\`slock reminder cancel\`** \u2014 Cancel one of your reminders by ID.
1070
+ 28. **\`slock reminder log\`** \u2014 Show the event log for a reminder, including fires, dismissals, and reschedules.
1071
+ 29. **\`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\`).
1071
1072
 
1072
1073
  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:
1073
1074
  - failure \u2192 stderr \`{"ok":false,"code":"...","message":"..."}\` with non-zero exit
@@ -1173,7 +1174,7 @@ Each channel has a **name** and optionally a **description** that define its pur
1173
1174
  - If unsure where something belongs, call ${serverInfoCmd} to review channel descriptions.`;
1174
1175
  const thirdPartyIntegrationsSection = isCli ? `### Third-party integrations
1175
1176
 
1176
- 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. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. 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, call internal Slock integration endpoints directly, 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
1177
+ 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. If the service exposes an agent behavior manifest and you need to run its local CLI, run \`slock integration env --service <service>\` before invoking that CLI; if it prints exports, apply them first so service credentials stay under a per-agent profile HOME/XDG tree instead of the host user's global HOME. If it reports that no local env is required, do not invent HOME/XDG overrides. If it fails, do not run that local CLI with the host user's HOME; report that the service manifest is unsupported. Slock does not execute commands from remote manifests automatically. If the CLI reports that the \`integration\` command is unknown, the local daemon/CLI is too old for Slock Agent Login; report that the machine must be upgraded/restarted instead of calling internal HTTP endpoints yourself. 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, call internal Slock integration endpoints directly, 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
1177
1178
 
1178
1179
  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.`;
1179
1180
  const readingHistorySection = isCli ? `### Reading history
@@ -1548,6 +1549,17 @@ var proxyServerState = null;
1548
1549
  var proxyServerStartPromise = null;
1549
1550
  var proxyServerFactory = createProxyServer;
1550
1551
  var DECODED_RESPONSE_HEADERS = /* @__PURE__ */ new Set(["content-encoding", "content-length", "transfer-encoding"]);
1552
+ var HOP_BY_HOP_REQUEST_HEADERS = /* @__PURE__ */ new Set([
1553
+ "connection",
1554
+ "keep-alive",
1555
+ "proxy-authenticate",
1556
+ "proxy-authorization",
1557
+ "proxy-connection",
1558
+ "te",
1559
+ "trailer",
1560
+ "transfer-encoding",
1561
+ "upgrade"
1562
+ ]);
1551
1563
  var LOCAL_HELD_CONTEXT_LIMIT = 3;
1552
1564
  var AGENT_CREDENTIAL_PROXY_HOST = "127.0.0.1";
1553
1565
  var AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS = 3;
@@ -1647,9 +1659,12 @@ async function handleProxyRequest(req, res) {
1647
1659
  target = new URL2(req.url ?? "/", registration.serverUrl);
1648
1660
  const headers = new Headers();
1649
1661
  for (const [name, value] of Object.entries(req.headers)) {
1662
+ const normalizedName = name.toLowerCase();
1650
1663
  if (value === void 0) continue;
1651
- if (name.toLowerCase() === "host") continue;
1652
- if (name.toLowerCase() === "authorization") continue;
1664
+ if (normalizedName === "host") continue;
1665
+ if (normalizedName === "authorization") continue;
1666
+ if (normalizedName === "content-length") continue;
1667
+ if (HOP_BY_HOP_REQUEST_HEADERS.has(normalizedName)) continue;
1653
1668
  if (Array.isArray(value)) {
1654
1669
  for (const item of value) headers.append(name, item);
1655
1670
  } else {
@@ -1660,7 +1675,15 @@ async function handleProxyRequest(req, res) {
1660
1675
  headers.set("X-Agent-Id", registration.agentId);
1661
1676
  headers.set("X-Slock-Client", "cli");
1662
1677
  headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
1663
- let body = method === "GET" || method === "HEAD" ? void 0 : req;
1678
+ let body;
1679
+ let rawBodyBuffer;
1680
+ if (method !== "GET" && method !== "HEAD") {
1681
+ rawBodyBuffer = await readRequestBody(req);
1682
+ const bodyBuffer = new ArrayBuffer(rawBodyBuffer.byteLength);
1683
+ new Uint8Array(bodyBuffer).set(rawBodyBuffer);
1684
+ body = bodyBuffer;
1685
+ headers.set("content-length", String(rawBodyBuffer.byteLength));
1686
+ }
1664
1687
  let sendTarget;
1665
1688
  const sideEffectAction = agentApiSideEffectAction(target.pathname);
1666
1689
  if (method === "GET" && target.pathname === "/internal/agent-api/events") {
@@ -1672,7 +1695,7 @@ async function handleProxyRequest(req, res) {
1672
1695
  }
1673
1696
  }
1674
1697
  if (method === "POST" && sideEffectAction) {
1675
- const rawBody = await readRequestBody(req);
1698
+ const rawBody = rawBodyBuffer?.toString("utf8") ?? "";
1676
1699
  const prepared = await prepareAgentApiSideEffectForward(registration, headers, rawBody, sideEffectAction);
1677
1700
  if (prepared.localResponse) {
1678
1701
  const responseText = JSON.stringify(prepared.localResponse);
@@ -1688,10 +1711,13 @@ async function handleProxyRequest(req, res) {
1688
1711
  const upstream = await daemonFetch(target, {
1689
1712
  method,
1690
1713
  headers,
1691
- body,
1692
- // Required by undici when forwarding a Node stream.
1693
- duplex: body ? "half" : void 0
1714
+ body
1694
1715
  });
1716
+ if (upstream.status >= 500) {
1717
+ registration.inboxCoordinator?.recordTransportNormalizedError?.(
1718
+ transportNormalizedErrorForHttpStatus(target, upstream.status, registration.launchId)
1719
+ );
1720
+ }
1695
1721
  if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
1696
1722
  const responseText = await upstream.text();
1697
1723
  consumeVisibleResponse(registration, target, sendTarget, responseText);
@@ -1714,9 +1740,12 @@ async function handleProxyRequest(req, res) {
1714
1740
  } catch (err) {
1715
1741
  const failure = proxyFailureForError(method, target, err);
1716
1742
  logger.warn(
1717
- `[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}`
1743
+ `[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}${failure.errorCause ? ` (cause=${failure.errorCause})` : ""}`
1718
1744
  );
1719
1745
  registration.inboxCoordinator?.recordProxyFailure?.(failure);
1746
+ registration.inboxCoordinator?.recordTransportNormalizedError?.(
1747
+ transportNormalizedErrorForError(target, err, registration.launchId)
1748
+ );
1720
1749
  writeProxyFailureResponse(res, failure);
1721
1750
  }
1722
1751
  }
@@ -1735,25 +1764,125 @@ function writeProxyFailureResponse(res, failure) {
1735
1764
  }
1736
1765
  function proxyFailureForError(method, target, err) {
1737
1766
  const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
1738
- return {
1767
+ const cause = err instanceof Error ? err.cause : void 0;
1768
+ const failure = {
1739
1769
  method,
1740
1770
  pathname: target?.pathname ?? "unknown",
1741
1771
  queryKeys,
1742
1772
  errorName: err instanceof Error ? err.name : typeof err,
1743
1773
  errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
1744
1774
  };
1775
+ const errorCause = describeProxyErrorCause(cause);
1776
+ if (errorCause) failure.errorCause = errorCause;
1777
+ return failure;
1778
+ }
1779
+ function describeProxyErrorCause(cause) {
1780
+ if (!cause) return void 0;
1781
+ if (cause instanceof Error) {
1782
+ const errorWithCode = cause;
1783
+ const code = typeof errorWithCode.code === "string" ? errorWithCode.code : void 0;
1784
+ return truncateProxyErrorMessage([code, cause.message].filter(Boolean).join(" "));
1785
+ }
1786
+ if (typeof cause === "object") {
1787
+ const causeObject = cause;
1788
+ const code = typeof causeObject.code === "string" ? causeObject.code : void 0;
1789
+ const message = typeof causeObject.message === "string" ? causeObject.message : void 0;
1790
+ const detail = [code, message].filter(Boolean).join(" ");
1791
+ if (detail) return truncateProxyErrorMessage(detail);
1792
+ }
1793
+ return truncateProxyErrorMessage(String(cause));
1745
1794
  }
1746
1795
  function truncateProxyErrorMessage(message) {
1747
1796
  const normalized = message.replace(/\s+/g, " ").trim();
1748
1797
  return normalized.length > 500 ? `${normalized.slice(0, 497)}...` : normalized;
1749
1798
  }
1799
+ function transportNormalizedErrorForHttpStatus(target, status, launchId) {
1800
+ return {
1801
+ normalizedCode: "server_5xx",
1802
+ routeFamily: routeFamilyForPath(target.pathname),
1803
+ responseStarted: true,
1804
+ upstreamLayer: "http_status",
1805
+ upstreamStatus: status,
1806
+ launchId,
1807
+ targetHostClass: daemonUpstreamTargetHostClass(target),
1808
+ // Today the local credential proxy is only used by CLI wrappers. If runtime
1809
+ // or daemon-internal callers use it later, plumb the caller identity here.
1810
+ downstreamCaller: "cli",
1811
+ upstream: "server"
1812
+ };
1813
+ }
1814
+ function transportNormalizedErrorForError(target, err, launchId) {
1815
+ return {
1816
+ normalizedCode: "transport_failure",
1817
+ routeFamily: routeFamilyForPath(target?.pathname ?? "unknown"),
1818
+ responseStarted: false,
1819
+ upstreamLayer: target ? upstreamLayerForProxyError(err) : "unknown",
1820
+ originalMessage: sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err)),
1821
+ launchId,
1822
+ targetHostClass: target ? daemonUpstreamTargetHostClass(target) : "custom_server",
1823
+ // Today the local credential proxy is only used by CLI wrappers. If runtime
1824
+ // or daemon-internal callers use it later, plumb the caller identity here.
1825
+ downstreamCaller: "cli",
1826
+ upstream: "server"
1827
+ };
1828
+ }
1829
+ function routeFamilyForPath(pathname) {
1830
+ if (pathname === "/internal/agent-api/send") return "agent-api/send";
1831
+ if (pathname === "/internal/agent-api/events") return "agent-api/events";
1832
+ if (pathname === "/internal/agent-api/receive-ack") return "agent-api/events";
1833
+ if (pathname === "/internal/agent-api/tasks/claim") return "tasks/claim";
1834
+ if (pathname === "/internal/agent-api/tasks/update-status") return "tasks/update";
1835
+ if (pathname === "/internal/agent-api/tasks" || pathname.startsWith("/internal/agent-api/tasks/")) return "tasks";
1836
+ if (pathname.startsWith("/internal/agent-api/attachments/")) return "agent-api/attachments";
1837
+ if (/^\/internal\/agent-api\/messages\/[^/]+\/reactions$/.test(pathname)) return "agent-api/messages/reactions";
1838
+ if (pathname === "/internal/agent-api/server") return "server";
1839
+ if (pathname.startsWith("/internal/agent-api/history")) return "agent-api/events";
1840
+ if (pathname.startsWith("/internal/agent-api/search")) return "agent-api/events";
1841
+ if (pathname.startsWith("/internal/agent-api/channel-members")) return "channel-members";
1842
+ if (pathname.startsWith("/internal/agent-api/knowledge")) return "knowledge";
1843
+ if (pathname === "/internal/agent-api/profile" || pathname.startsWith("/internal/agent-api/profile/")) return "profile";
1844
+ if (pathname === "/internal/agent-api/integrations" || pathname.startsWith("/internal/agent-api/integrations/")) return "integrations";
1845
+ if (pathname === "/internal/agent-api/upload") return "attachments/upload";
1846
+ if (pathname === "/internal/agent-api/resolve-channel") return "resolve-channel";
1847
+ if (pathname === "/internal/agent-api/threads/unfollow") return "threads/unfollow";
1848
+ if (pathname === "/internal/agent-api/prepare-action") return "action/prepare";
1849
+ if (pathname === "/internal/agent-api/reminders" || pathname.startsWith("/internal/agent-api/reminders/")) return "reminders";
1850
+ if (/^\/internal\/agent-api\/channels\/[^/]+\/join$/.test(pathname)) return "channels/join";
1851
+ if (/^\/internal\/agent-api\/channels\/[^/]+\/leave$/.test(pathname)) return "channels/leave";
1852
+ return "unknown";
1853
+ }
1854
+ function daemonUpstreamTargetHostClass(url) {
1855
+ const hostname = url.hostname.toLowerCase();
1856
+ if (hostname === "api.slock.ai") return "api.slock.ai";
1857
+ return "custom_server";
1858
+ }
1859
+ function upstreamLayerForProxyError(err) {
1860
+ const code = errorCode(err).toUpperCase();
1861
+ const message = (err instanceof Error ? err.message : String(err)).toLowerCase();
1862
+ if (code === "ENOTFOUND" || code === "EAI_AGAIN" || message.includes("dns")) return "dns";
1863
+ if (code === "ECONNREFUSED" || code === "ECONNRESET" || code === "EPIPE" || code === "UND_ERR_SOCKET" || message.includes("socket")) return "tcp";
1864
+ if (code.includes("TLS") || message.includes("certificate") || message.includes("tls")) return "tls";
1865
+ if (code === "UND_ERR_HEADERS_TIMEOUT" || code === "UND_ERR_BODY_TIMEOUT" || message.includes("timeout")) return "read_timeout";
1866
+ if (message.includes("proxy")) return "proxy_connect";
1867
+ if (message.includes("fly")) return "fly_edge";
1868
+ return "unknown";
1869
+ }
1870
+ function errorCode(err) {
1871
+ if (typeof err === "object" && err && "code" in err && typeof err.code === "string") {
1872
+ return err.code;
1873
+ }
1874
+ if (typeof err === "object" && err && "cause" in err) return errorCode(err.cause);
1875
+ return "";
1876
+ }
1877
+ function sanitizeTransportOriginalMessage(message) {
1878
+ return truncateProxyErrorMessage(message).replace(/sk_(?:agent|machine|computer)_[A-Za-z0-9_-]+/g, "sk_[redacted]").replace(/sap_[A-Za-z0-9_-]+/g, "sap_[redacted]").replace(/https?:\/\/\S+/g, "[url]");
1879
+ }
1750
1880
  async function readRequestBody(req) {
1751
- let body = "";
1752
- req.setEncoding("utf8");
1881
+ const chunks = [];
1753
1882
  for await (const chunk of req) {
1754
- body += String(chunk);
1883
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
1755
1884
  }
1756
- return body;
1885
+ return Buffer.concat(chunks);
1757
1886
  }
1758
1887
  function messageSeq(message) {
1759
1888
  return Number(message.seq ?? 0);
@@ -1928,7 +2057,7 @@ async function loadRecentTargetMessages(registration, headers, target) {
1928
2057
  const historyHeaders = new Headers(headers);
1929
2058
  historyHeaders.delete("content-length");
1930
2059
  historyHeaders.delete("content-type");
1931
- const res = await daemonFetch(historyUrl, { method: "GET", headers: historyHeaders });
2060
+ const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
1932
2061
  if (!res.ok) return [];
1933
2062
  const parsed = await res.json().catch(() => null);
1934
2063
  return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
@@ -2131,6 +2260,7 @@ var shellSingleQuote = (value) => `'${value.replace(/'/g, `'\\''`)}'`;
2131
2260
  var powershellSingleQuote = (value) => `'${value.replace(/'/g, "''")}'`;
2132
2261
  var DEFAULT_ACTIVE_CAPABILITIES = "send,read,mentions,tasks,reactions,server,channels,knowledge";
2133
2262
  var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
2263
+ var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
2134
2264
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
2135
2265
  var RAW_CREDENTIAL_ENV_DENYLIST = [
2136
2266
  "SLOCK_AGENT_CREDENTIAL_KEY"
@@ -2343,6 +2473,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
2343
2473
  [SLOCK_HOME_ENV]: slockHome,
2344
2474
  SLOCK_AGENT_ID: ctx.agentId,
2345
2475
  ...ctx.launchId ? { SLOCK_AGENT_LAUNCH_ID: ctx.launchId } : {},
2476
+ ...ctx.cliTransportTraceDir ? { [CLI_TRANSPORT_TRACE_DIR_ENV]: ctx.cliTransportTraceDir } : {},
2346
2477
  SLOCK_SERVER_URL: ctx.config.serverUrl,
2347
2478
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
2348
2479
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
@@ -6617,6 +6748,7 @@ var AgentProcessManager = class _AgentProcessManager {
6617
6748
  driverResolver;
6618
6749
  defaultAgentEnvVarsProvider;
6619
6750
  tracer;
6751
+ cliTransportTraceDir = null;
6620
6752
  deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
6621
6753
  processExitTraceAttrs = /* @__PURE__ */ new WeakMap();
6622
6754
  agentVisibleBoundaries = /* @__PURE__ */ new Map();
@@ -6647,6 +6779,9 @@ var AgentProcessManager = class _AgentProcessManager {
6647
6779
  setTracer(tracer) {
6648
6780
  this.tracer = tracer;
6649
6781
  }
6782
+ setCliTransportTraceDir(traceDir) {
6783
+ this.cliTransportTraceDir = traceDir;
6784
+ }
6650
6785
  visibleBoundaryMap(agentId) {
6651
6786
  let map = this.agentVisibleBoundaries.get(agentId);
6652
6787
  if (!map) {
@@ -6751,6 +6886,7 @@ var AgentProcessManager = class _AgentProcessManager {
6751
6886
  });
6752
6887
  },
6753
6888
  recordProxyFailure: (input) => this.recordAgentProxyFailure(agentId, input),
6889
+ recordTransportNormalizedError: (input) => this.recordAgentProxyTransportNormalizedError(agentId, input),
6754
6890
  recordFreshnessDecision: (input) => {
6755
6891
  this.recordDaemonTrace("daemon.agent.inbox.freshness_decision", {
6756
6892
  agentId,
@@ -6779,6 +6915,22 @@ var AgentProcessManager = class _AgentProcessManager {
6779
6915
  error_message: input.errorMessage
6780
6916
  }, "error");
6781
6917
  }
6918
+ recordAgentProxyTransportNormalizedError(agentId, input) {
6919
+ this.recordDaemonTrace("daemon.transport.normalized_error", {
6920
+ producer: "daemon",
6921
+ agentId,
6922
+ normalized_code: input.normalizedCode,
6923
+ route_family: input.routeFamily,
6924
+ response_started: input.responseStarted,
6925
+ upstream_layer: input.upstreamLayer,
6926
+ ...typeof input.upstreamStatus === "number" ? { upstream_status: input.upstreamStatus } : {},
6927
+ ...input.originalMessage ? { original_message: input.originalMessage } : {},
6928
+ launchId: input.launchId,
6929
+ target_host_class: input.targetHostClass,
6930
+ downstream_caller: input.downstreamCaller,
6931
+ upstream: input.upstream
6932
+ }, "error");
6933
+ }
6782
6934
  recordFreshnessDecisionActivity(agentId, input) {
6783
6935
  if (input.decision !== "local_hold" && input.decision !== "syncing_hold") return;
6784
6936
  const ap = this.agents.get(agentId);
@@ -7178,7 +7330,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
7178
7330
  slockCliPath: this.slockCliPath,
7179
7331
  daemonApiKey: this.daemonApiKey,
7180
7332
  launchId: launchId || null,
7181
- agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId)
7333
+ agentCredentialProxyInboxCoordinator: this.createAgentProxyInboxCoordinator(agentId),
7334
+ cliTransportTraceDir: this.cliTransportTraceDir
7182
7335
  });
7183
7336
  this.recordDaemonTrace("daemon.agent.spawn.created", {
7184
7337
  ...this.startQueueTraceAttrs(agentId, effectiveConfig, wakeMessage, unreadSummary, resumePrompt, launchId),
@@ -9880,7 +10033,8 @@ var DIAGNOSTIC_ERROR_ATTRS = /* @__PURE__ */ new Set([
9880
10033
  "runtime_error_message_present",
9881
10034
  "runtime_error_message_length_bucket",
9882
10035
  "runtime_error_message_truncated",
9883
- "runtime_error_message_excerpt"
10036
+ "runtime_error_message_excerpt",
10037
+ "original_message"
9884
10038
  ]);
9885
10039
  var LocalRotatingTraceSink = class {
9886
10040
  traceDir;
@@ -9974,14 +10128,13 @@ function sanitizeAttrs(attrs) {
9974
10128
  if (!attrs) return void 0;
9975
10129
  const sanitized = {};
9976
10130
  for (const [key, value] of Object.entries(attrs)) {
10131
+ if (value === null || value === void 0 || value === "") continue;
9977
10132
  if (isDiagnosticIdAttr(key)) {
9978
- if (value === null || value === void 0 || value === "") continue;
9979
10133
  sanitized[key] = sanitizeValue(value);
9980
10134
  continue;
9981
10135
  }
9982
10136
  if (isDiagnosticErrorAttr(key)) {
9983
- if (value === null || value === void 0 || value === "") continue;
9984
- sanitized[key] = sanitizeValue(value);
10137
+ sanitized[key] = sanitizeDiagnosticErrorValue(key, value);
9985
10138
  continue;
9986
10139
  }
9987
10140
  if (shouldDropAttr(key)) continue;
@@ -10001,6 +10154,11 @@ function sanitizeValue(value) {
10001
10154
  }
10002
10155
  return String(value);
10003
10156
  }
10157
+ function sanitizeDiagnosticErrorValue(key, value) {
10158
+ if (key !== "original_message" || typeof value !== "string") return sanitizeValue(value);
10159
+ const normalized = value.replace(/sk_(?:agent|machine|computer)_[A-Za-z0-9_-]+/g, "sk_[redacted]").replace(/sap_[A-Za-z0-9_-]+/g, "sap_[redacted]").replace(/https?:\/\/\S+/g, "[url]").replace(/\s+/g, " ").trim();
10160
+ return normalized.length > 240 ? `${normalized.slice(0, 237)}...` : normalized;
10161
+ }
10004
10162
  function shouldDropAttr(key) {
10005
10163
  const normalized = key.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
10006
10164
  if (/(^|_)(api_key|auth_token|token|secret|password|cookie|credential)(_|$)/i.test(normalized)) {
@@ -10648,6 +10806,7 @@ var DaemonCore = class {
10648
10806
  sink: this.localTraceSink
10649
10807
  });
10650
10808
  this.agentManager.setTracer(this.tracer);
10809
+ this.agentManager.setCliTransportTraceDir(path16.join(machineDir, "traces"));
10651
10810
  }
10652
10811
  installTraceBundleUploader(machineDir) {
10653
10812
  if (!this.shouldEnableLocalTrace()) return;