@botiverse/raft-daemon 0.72.8 → 1.0.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.
@@ -1359,6 +1359,71 @@ function randomHex(length) {
1359
1359
  }
1360
1360
 
1361
1361
  // ../shared/src/tracing/eventRows.ts
1362
+ var TRACE_EVENT_ROW_V2_PROJECTION_COLUMNS = [
1363
+ ["row_kind", "string"],
1364
+ ["service_name", "string"],
1365
+ ["deployment_environment", "string"],
1366
+ ["service_version", "string"],
1367
+ ["service_revision", "string"],
1368
+ ["service_instance_id", "string"],
1369
+ ["deployment_instance_source", "string"],
1370
+ ["deployment_identity_state", "string"],
1371
+ ["ecs_task_id", "string"],
1372
+ ["ecs_task_family", "string"],
1373
+ ["ecs_task_revision", "string"],
1374
+ ["trace_id", "string"],
1375
+ ["span_id", "string"],
1376
+ ["parent_span_id", "string"],
1377
+ ["span_name", "string"],
1378
+ ["span_kind", "string"],
1379
+ ["span_surface", "string"],
1380
+ ["span_status", "string"],
1381
+ ["span_start_time_ms", "int"],
1382
+ ["span_end_time_ms", "int"],
1383
+ ["event_name", "string"],
1384
+ ["event_kind", "string"],
1385
+ ["event_time", "timestamp"],
1386
+ ["event_time_ms", "int"],
1387
+ ["event_index", "int"],
1388
+ ["server_id", "string"],
1389
+ ["machine_id", "string"],
1390
+ ["agent_id", "string"],
1391
+ ["launch_id", "string"],
1392
+ ["session_id", "string"],
1393
+ ["request_id", "string"],
1394
+ ["route_pattern", "string"],
1395
+ ["caller_kind", "string"],
1396
+ ["outcome", "string"],
1397
+ ["reason", "string"],
1398
+ ["source", "string"],
1399
+ ["authority", "string"],
1400
+ ["activity_write_site", "string"],
1401
+ ["activity_source", "string"],
1402
+ ["hint_source", "string"],
1403
+ ["resolved_activity", "string"],
1404
+ ["previous_activity", "string"],
1405
+ ["next_activity", "string"],
1406
+ ["repair_kind", "string"],
1407
+ ["action", "string"],
1408
+ ["error_class", "string"],
1409
+ ["status_bucket", "string"],
1410
+ ["shadow_agent_id", "string"],
1411
+ ["shadow_signal_site", "string"],
1412
+ ["shadow_observation_class", "string"],
1413
+ ["shadow_prior_projection", "string"],
1414
+ ["shadow_projection", "string"],
1415
+ ["shadow_legacy_outcome", "string"],
1416
+ ["shadow_action", "string"],
1417
+ ["shadow_reason", "string"],
1418
+ ["shadow_direction", "string"],
1419
+ ["shadow_plan_kind", "string"]
1420
+ ];
1421
+ var TRACE_EVENT_ROW_V2_INGEST_STATEMENT = [
1422
+ "SELECT",
1423
+ TRACE_EVENT_ROW_V2_PROJECTION_COLUMNS.map(([column, type]) => `$0["${column}"]::${type} AS ${column}`).join(", "),
1424
+ "INSERT INTO raft.trace_events_v2",
1425
+ `(${TRACE_EVENT_ROW_V2_PROJECTION_COLUMNS.map(([column]) => column).join(", ")})`
1426
+ ].join(" ");
1362
1427
  var PROMOTED_IDENTITY_ATTRS = [
1363
1428
  "server_id",
1364
1429
  "machine_id",
@@ -2020,7 +2085,7 @@ var integrationAppDraftFieldsSchema = z.object({
2020
2085
  var integrationRegisterAppOperationSchema = integrationAppDraftFieldsSchema.extend({
2021
2086
  type: z.literal("integration:register_app"),
2022
2087
  name: z.string().trim().min(1).max(120),
2023
- clientKey: z.string().trim().min(1).max(120),
2088
+ clientKey: z.string().trim().min(1).max(120).optional(),
2024
2089
  returnUrl: z.string().trim().min(1).max(2e3),
2025
2090
  scopes: z.array(z.string().trim().min(1).max(120)).max(64).default([]),
2026
2091
  unsafeDemoUrlOverride: z.boolean().optional(),
@@ -2440,10 +2505,8 @@ var agentApiIntegrationLoginBodySchema = passthroughObject({
2440
2505
  scopes: optionalStringArraySchema,
2441
2506
  target: optionalStringSchema
2442
2507
  });
2443
- var agentApiIntegrationAppPrepareBodySchema = passthroughObject({
2444
- mode: z3.enum(["register", "update"]),
2508
+ var agentApiIntegrationAppPrepareCommonBodyShape = {
2445
2509
  target: z3.string().trim().min(1),
2446
- clientKey: z3.string().trim().min(1),
2447
2510
  name: optionalStringSchema,
2448
2511
  description: optionalStringSchema,
2449
2512
  homepageUrl: optionalStringSchema,
@@ -2452,7 +2515,19 @@ var agentApiIntegrationAppPrepareBodySchema = passthroughObject({
2452
2515
  scopes: optionalStringArraySchema,
2453
2516
  unsafeDemoUrlOverride: optionalBooleanSchema,
2454
2517
  draftHint: optionalStringSchema
2455
- });
2518
+ };
2519
+ var agentApiIntegrationAppPrepareBodySchema = z3.discriminatedUnion("mode", [
2520
+ passthroughObject({
2521
+ mode: z3.literal("register"),
2522
+ ...agentApiIntegrationAppPrepareCommonBodyShape,
2523
+ clientKey: optionalStringSchema
2524
+ }),
2525
+ passthroughObject({
2526
+ mode: z3.literal("update"),
2527
+ ...agentApiIntegrationAppPrepareCommonBodyShape,
2528
+ clientKey: z3.string().trim().min(1)
2529
+ })
2530
+ ]);
2456
2531
  var agentApiIntegrationAppRotateSecretBodySchema = passthroughObject({
2457
2532
  clientKey: z3.string().trim().min(1)
2458
2533
  });
@@ -4032,6 +4107,55 @@ var CANONICAL_MESSAGE_MANIFEST = Object.freeze({
4032
4107
  excludedClientOnly: EXCLUDED_CLIENT_ONLY_MESSAGE_FIELDS
4033
4108
  });
4034
4109
 
4110
+ // ../shared/src/canonicalMessageV2.ts
4111
+ var CANONICAL_MESSAGE_V2_SCHEMA_VERSION = 5;
4112
+ var CANONICAL_MESSAGE_V2_ENVELOPE_KIND = "normalized-message-v2";
4113
+ var CANONICAL_REACTION_PREVIEW_LIMIT = 3;
4114
+ var CANONICAL_REACTION_LIMIT = 30;
4115
+ var CANONICAL_MESSAGE_V2_MANIFEST = Object.freeze({
4116
+ schemaVersion: CANONICAL_MESSAGE_V2_SCHEMA_VERSION,
4117
+ envelopeKind: CANONICAL_MESSAGE_V2_ENVELOPE_KIND,
4118
+ legacyIngressManifestVersion: CANONICAL_MESSAGE_MANIFEST_VERSION,
4119
+ soleApplyEligible: true,
4120
+ reactions: Object.freeze({
4121
+ mergePolicy: "present-overwrite",
4122
+ maxItems: CANONICAL_REACTION_LIMIT,
4123
+ fields: Object.freeze(["count", "emoji", "previewK"]),
4124
+ previewFields: Object.freeze(["displayName", "id"]),
4125
+ previewLimit: CANONICAL_REACTION_PREVIEW_LIMIT,
4126
+ legacyCompatibilityPreviewPolicy: "empty-without-room-common-provenance",
4127
+ deterministicOrder: "emoji-code-unit/actor-id-code-unit"
4128
+ }),
4129
+ viewerOverlayFields: Object.freeze(["reactions.reactedByMe"]),
4130
+ readCacheRelations: Object.freeze(["Message.ReactionActors"]),
4131
+ forbiddenCanonicalPaths: Object.freeze([
4132
+ "reactions[].reactorIds",
4133
+ "reactions[].reactorNames"
4134
+ ])
4135
+ });
4136
+
4137
+ // ../shared/src/discussionGraph.ts
4138
+ var DISCUSSION_RELATION_REGISTRY = Object.freeze({
4139
+ messageReactionActors: Object.freeze({
4140
+ rootKind: "message",
4141
+ relation: "reaction-actors",
4142
+ backing: "read-cache",
4143
+ consistency: "read-page",
4144
+ provenance: Object.freeze({ count: "shared-parent-fold", previewK: "shared-parent-fold" }),
4145
+ invalidation: "parent-scope-epoch",
4146
+ allowedCommands: Object.freeze(["set-interaction"])
4147
+ }),
4148
+ messageReplies: Object.freeze({
4149
+ rootKind: "message",
4150
+ relation: "replies",
4151
+ backing: "sync-scope",
4152
+ consistency: "sync-scope-window",
4153
+ provenance: Object.freeze({ replyCount: "shared-parent-fold" }),
4154
+ invalidation: "own-scope-rebaseline",
4155
+ allowedCommands: Object.freeze(["reply"])
4156
+ })
4157
+ });
4158
+
4035
4159
  // ../shared/src/index.ts
4036
4160
  var COMPUTER_CAPABILITY_SUPERVISOR_MUTATIONS = "computer:supervisor-mutations-v1";
4037
4161
  var RUNTIME_CONFIG_VERSION = 1;
@@ -4558,7 +4682,7 @@ var TRIAL_DURATION_DAYS = (TRIAL_END_DATE.getTime() - TRIAL_START_DATE.getTime()
4558
4682
 
4559
4683
  // src/agentProcessManager.ts
4560
4684
  import { existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync6, readdirSync as readdirSync4, statSync, writeFileSync as writeFileSync4 } from "fs";
4561
- import { mkdir, writeFile, access, readdir as readdir2, stat as stat2, readFile, rm as rm2, lstat, realpath, open } from "fs/promises";
4685
+ import { readdir as readdir2, stat as stat2, readFile, rm as rm2, lstat, realpath, open } from "fs/promises";
4562
4686
  import { createHash as createHash3, randomUUID as randomUUID6 } from "crypto";
4563
4687
  import path14 from "path";
4564
4688
  import { gzipSync } from "zlib";
@@ -4566,8 +4690,9 @@ import os6 from "os";
4566
4690
 
4567
4691
  // src/proxy.ts
4568
4692
  import { HttpsProxyAgent } from "https-proxy-agent";
4569
- import { ProxyAgent } from "undici";
4693
+ import { Agent, ProxyAgent } from "undici";
4570
4694
  var fetchDispatcherCache = /* @__PURE__ */ new Map();
4695
+ var isolatedFetchDispatcherCache = /* @__PURE__ */ new Map();
4571
4696
  function getFetchPreResponseTimeoutMs(env) {
4572
4697
  const parsed = Number.parseInt(env.SLOCK_DAEMON_FETCH_PRE_RESPONSE_TIMEOUT_MS || "", 10);
4573
4698
  return Number.isFinite(parsed) && parsed > 0 ? parsed : 3e4;
@@ -4632,32 +4757,60 @@ function resolveProxyUrl(targetUrl, env) {
4632
4757
  if (shouldBypassProxy(targetUrl, env)) return void 0;
4633
4758
  return proxyUrl;
4634
4759
  }
4760
+ function createProxyDispatcher(proxyUrl, timeoutMs) {
4761
+ return new ProxyAgent({
4762
+ uri: proxyUrl,
4763
+ connect: { timeout: timeoutMs },
4764
+ requestTls: { timeout: timeoutMs },
4765
+ headersTimeout: timeoutMs
4766
+ });
4767
+ }
4768
+ function closeDispatcherBestEffort(dispatcher) {
4769
+ void Promise.resolve().then(() => dispatcher.close()).catch(() => dispatcher.destroy?.(new Error("evicted"))).catch(() => {
4770
+ });
4771
+ }
4635
4772
  function buildFetchDispatcher(targetUrl, env) {
4636
4773
  const proxyUrl = resolveProxyUrl(targetUrl, env);
4637
4774
  if (!proxyUrl) return void 0;
4638
4775
  const cached = fetchDispatcherCache.get(proxyUrl);
4639
4776
  if (cached) return cached;
4640
4777
  const timeoutMs = getFetchPreResponseTimeoutMs(env);
4641
- const dispatcher = new ProxyAgent({
4642
- uri: proxyUrl,
4643
- // All three are pre-response and body-agnostic (see getFetchPreResponseTimeoutMs):
4644
- // headersTimeout = headers-hang leg; requestTls.timeout = CONNECT-tunnel
4645
- // establish leg; connect.timeout = socket to the proxy itself (defensive).
4778
+ const dispatcher = createProxyDispatcher(proxyUrl, timeoutMs);
4779
+ fetchDispatcherCache.set(proxyUrl, dispatcher);
4780
+ return dispatcher;
4781
+ }
4782
+ function getIsolatedDispatcherCacheKey(targetUrl, isolationKey, env) {
4783
+ const proxyUrl = resolveProxyUrl(targetUrl, env);
4784
+ const routeKey = proxyUrl ? `proxy:${proxyUrl}` : `direct:${new URL(targetUrl).origin}`;
4785
+ return { cacheKey: `${isolationKey}\0${routeKey}`, proxyUrl };
4786
+ }
4787
+ function buildIsolatedFetchDispatcher(targetUrl, isolationKey, env) {
4788
+ const { cacheKey, proxyUrl } = getIsolatedDispatcherCacheKey(targetUrl, isolationKey, env);
4789
+ const cached = isolatedFetchDispatcherCache.get(cacheKey);
4790
+ if (cached) return cached;
4791
+ const timeoutMs = getFetchPreResponseTimeoutMs(env);
4792
+ const dispatcher = proxyUrl ? createProxyDispatcher(proxyUrl, timeoutMs) : new Agent({
4646
4793
  connect: { timeout: timeoutMs },
4647
- requestTls: { timeout: timeoutMs },
4648
4794
  headersTimeout: timeoutMs
4649
4795
  });
4650
- fetchDispatcherCache.set(proxyUrl, dispatcher);
4796
+ isolatedFetchDispatcherCache.set(cacheKey, dispatcher);
4651
4797
  return dispatcher;
4652
4798
  }
4799
+ function evictIsolatedFetchDispatcher(targetUrl, isolationKey, env) {
4800
+ const { cacheKey } = getIsolatedDispatcherCacheKey(targetUrl, isolationKey, env);
4801
+ const cached = isolatedFetchDispatcherCache.get(cacheKey);
4802
+ if (!cached) return false;
4803
+ isolatedFetchDispatcherCache.delete(cacheKey);
4804
+ closeDispatcherBestEffort(cached);
4805
+ return true;
4806
+ }
4653
4807
  function evictFetchDispatcher(targetUrl, env) {
4654
4808
  const proxyUrl = resolveProxyUrl(targetUrl, env);
4655
4809
  if (!proxyUrl) return false;
4656
4810
  const cached = fetchDispatcherCache.get(proxyUrl);
4657
4811
  if (!cached) return false;
4658
4812
  fetchDispatcherCache.delete(proxyUrl);
4659
- void Promise.resolve().then(() => cached.close()).catch(() => cached.destroy?.(new Error("evicted"))).catch(() => {
4660
- });
4813
+ closeDispatcherBestEffort(cached);
4661
4814
  return true;
4662
4815
  }
4663
4816
 
@@ -4666,11 +4819,17 @@ function withDaemonFetchProxy(input, init = {}, env = process.env) {
4666
4819
  const dispatcher = buildFetchDispatcher(input.toString(), env);
4667
4820
  return dispatcher ? { ...init, dispatcher } : init;
4668
4821
  }
4669
- async function daemonFetch(input, init, env = process.env) {
4822
+ async function daemonFetch(input, init, env = process.env, options = {}) {
4823
+ const targetUrl = input.toString();
4824
+ const fetchInit = options.isolationKey ? { ...init, dispatcher: buildIsolatedFetchDispatcher(targetUrl, options.isolationKey, env) } : withDaemonFetchProxy(input, init, env);
4670
4825
  try {
4671
- return await fetch(input, withDaemonFetchProxy(input, init, env));
4826
+ return await fetch(input, fetchInit);
4672
4827
  } catch (err) {
4673
- evictFetchDispatcher(input.toString(), env);
4828
+ if (options.isolationKey) {
4829
+ evictIsolatedFetchDispatcher(targetUrl, options.isolationKey, env);
4830
+ } else {
4831
+ evictFetchDispatcher(targetUrl, env);
4832
+ }
4674
4833
  throw err;
4675
4834
  }
4676
4835
  }
@@ -5051,14 +5210,14 @@ Threads are sub-conversations attached to a specific message. They let you discu
5051
5210
  - **Start a new thread**: Use the \`msg=\` field from the header as the thread suffix. For example, if you see \`[target=#general msg=00000000 ...]\`, reply with \`raft message send --target "#general:00000000" <<'${D}'\` followed by the message body and \`${D}\`. The thread will be auto-created if it doesn't exist yet. Example IDs like \`00000000\` are placeholders; real message IDs come from received messages.
5052
5211
  - When you send a message, the response includes the message ID. You can use it to start a thread on your own message.
5053
5212
  - You can read thread history: \`raft message read --target "#general:00000000"\`
5054
- - You can stop receiving ordinary delivery for a thread with \`raft thread unfollow --target "#general:00000000"\`. Only do this when your work in that thread is clearly complete or no longer relevant.
5213
+ - Unfollowing a thread removes its follow record and stops its ordinary delivery while the parent channel is unmuted: \`raft thread unfollow --target "#general:00000000"\`. A parent channel mute already suppresses ordinary delivery from its threads, so do not unfollow solely to mute the parent channel. Only unfollow when your work in that thread is clearly complete or no longer relevant.
5055
5214
  - Threads cannot be nested \u2014 you cannot start a thread inside a thread.`;
5056
5215
  }
5057
5216
  function buildDiscoverySection() {
5058
5217
  return `### Discovering people and channels
5059
5218
 
5060
5219
  Call \`raft server info\` to see all channels in this server, which ones you have joined, other agents, and humans.
5061
- Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`raft message read\` and \`raft channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`raft channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`raft channel leave --target "#channel-name"\`. To mute ordinary Activity delivery without leaving a regular channel, use \`raft channel mute --target "#channel-name"\`; personal @mentions and DMs still pierce (a task pierces only when it personally @mentions you), and thread following is separate. To reverse that setting, use \`raft channel unmute --target "#channel-name"\`. To stop following a thread without leaving its parent channel, use \`raft thread unfollow --target "#channel-name:shortid"\`.
5220
+ Visible public channels may appear even when \`joined=false\`. In that state you can still inspect them with \`raft message read\` and \`raft channel members\`, but you cannot send messages there or receive ordinary channel delivery until you join with \`raft channel join --target "#channel-name"\`. Private channels require a human with access to add you. To leave a regular channel you have joined, use \`raft channel leave --target "#channel-name"\`. To mute ordinary Activity delivery from a regular channel and its threads without leaving, use \`raft channel mute --target "#channel-name"\`; personal @mentions and DMs still pierce (a task pierces only when it personally @mentions you), while existing thread follow records remain. To reverse that setting, use \`raft channel unmute --target "#channel-name"\`. To remove a thread's follow record without leaving its parent channel, use \`raft thread unfollow --target "#channel-name:shortid"\`.
5062
5221
  Private channels are membership-gated. If \`raft server info\` shows a channel as private, treat its name, members, and content as private to that channel; do not disclose that information in other channels, DMs, summaries, or task reports unless a human explicitly asks within an authorized context. In \`raft channel members\`, human role labels such as owner/admin show server-level authority; no role label means ordinary member.`;
5063
5222
  }
5064
5223
  function buildChannelAwarenessSection() {
@@ -6499,6 +6658,7 @@ var HOP_BY_HOP_REQUEST_HEADERS = /* @__PURE__ */ new Set([
6499
6658
  var LOCAL_HELD_CONTEXT_LIMIT = 3;
6500
6659
  var AGENT_CREDENTIAL_PROXY_HOST = "127.0.0.1";
6501
6660
  var AGENT_CREDENTIAL_PROXY_BIND_MAX_ATTEMPTS = 3;
6661
+ var AGENT_CREDENTIAL_PROXY_FETCH_ISOLATION_KEY = "agent-credential-proxy";
6502
6662
  function createProxyRequestHandler() {
6503
6663
  return (req, res) => {
6504
6664
  void handleProxyRequest(req, res);
@@ -6592,6 +6752,32 @@ async function handleProxyRequest(req, res) {
6592
6752
  const method = req.method ?? "GET";
6593
6753
  const correlationId = randomBytes(8).toString("hex");
6594
6754
  let target;
6755
+ const inboundTraceparent = firstRequestHeader(req.headers.traceparent);
6756
+ const parent = parseTraceparent(inboundTraceparent);
6757
+ const traceContextState = parent ? "continued" : inboundTraceparent ? "malformed" : "new_root";
6758
+ let localPathname = "unknown";
6759
+ try {
6760
+ localPathname = new URL2(req.url ?? "/", "http://agent-proxy.local").pathname;
6761
+ } catch {
6762
+ }
6763
+ const proxySpan = registration.tracer.startSpan("daemon.agent_proxy.request", {
6764
+ parent,
6765
+ surface: "daemon",
6766
+ kind: "client",
6767
+ attrs: {
6768
+ route_family: routeFamilyForPath(localPathname),
6769
+ method: normalizedProxyMethod(method),
6770
+ trace_context_state: traceContextState,
6771
+ proxy_launch_id_present: registration.launchId !== null,
6772
+ correlation_id: correlationId
6773
+ }
6774
+ });
6775
+ let proxySpanStatus = "error";
6776
+ let proxySpanEndAttrs = {
6777
+ outcome: "proxy_failure",
6778
+ normalized_code: "transport_failure",
6779
+ response_started: false
6780
+ };
6595
6781
  try {
6596
6782
  target = new URL2(req.url ?? "/", registration.serverUrl);
6597
6783
  const headers = new Headers();
@@ -6612,6 +6798,7 @@ async function handleProxyRequest(req, res) {
6612
6798
  headers.set("X-Agent-Id", registration.agentId);
6613
6799
  headers.set("X-Slock-Client", "cli");
6614
6800
  headers.set("X-Slock-Agent-Active-Capabilities", registration.activeCapabilities);
6801
+ headers.set("traceparent", formatTraceparent(proxySpan.context));
6615
6802
  let body;
6616
6803
  let rawBodyBuffer;
6617
6804
  if (method !== "GET" && method !== "HEAD") {
@@ -6626,6 +6813,12 @@ async function handleProxyRequest(req, res) {
6626
6813
  if (method === "GET" && target.pathname === "/internal/agent-api/inbox") {
6627
6814
  const localInbox = localAgentApiInboxResponse(registration);
6628
6815
  if (localInbox) {
6816
+ proxySpanStatus = "ok";
6817
+ proxySpanEndAttrs = {
6818
+ outcome: "local_response",
6819
+ local_response_kind: "inbox",
6820
+ http_status: localInbox.status
6821
+ };
6629
6822
  res.writeHead(localInbox.status, { "content-type": "application/json" });
6630
6823
  res.end(JSON.stringify(localInbox.body));
6631
6824
  return;
@@ -6634,6 +6827,12 @@ async function handleProxyRequest(req, res) {
6634
6827
  if (method === "GET" && target.pathname === "/internal/agent-api/events") {
6635
6828
  const localEvents = localAgentApiEventsResponse(registration, target);
6636
6829
  if (localEvents) {
6830
+ proxySpanStatus = "ok";
6831
+ proxySpanEndAttrs = {
6832
+ outcome: "local_response",
6833
+ local_response_kind: "events",
6834
+ http_status: localEvents.status
6835
+ };
6637
6836
  res.writeHead(localEvents.status, { "content-type": "application/json" });
6638
6837
  res.end(JSON.stringify(localEvents.body));
6639
6838
  return;
@@ -6643,6 +6842,12 @@ async function handleProxyRequest(req, res) {
6643
6842
  const rawBody = rawBodyBuffer?.toString("utf8") ?? "";
6644
6843
  const prepared = await prepareAgentApiSideEffectForward(registration, headers, rawBody, sideEffectAction);
6645
6844
  if (prepared.localResponse) {
6845
+ proxySpanStatus = "ok";
6846
+ proxySpanEndAttrs = {
6847
+ outcome: "local_response",
6848
+ local_response_kind: "freshness_hold",
6849
+ http_status: 200
6850
+ };
6646
6851
  const responseText = JSON.stringify(prepared.localResponse);
6647
6852
  res.writeHead(200, { "content-type": "application/json" });
6648
6853
  res.end(responseText);
@@ -6653,17 +6858,35 @@ async function handleProxyRequest(req, res) {
6653
6858
  headers.set("content-type", "application/json");
6654
6859
  headers.delete("content-length");
6655
6860
  }
6656
- const upstream = await daemonFetch(target, {
6657
- method,
6658
- headers,
6659
- body
6660
- });
6861
+ const upstream = await daemonFetch(
6862
+ target,
6863
+ {
6864
+ method,
6865
+ headers,
6866
+ body
6867
+ },
6868
+ process.env,
6869
+ { isolationKey: AGENT_CREDENTIAL_PROXY_FETCH_ISOLATION_KEY }
6870
+ );
6661
6871
  if (upstream.status >= 500) {
6662
6872
  const transportError = transportNormalizedErrorForHttpStatus(target, upstream.status, registration.launchId);
6873
+ proxySpanStatus = "error";
6874
+ proxySpanEndAttrs = {
6875
+ outcome: "upstream_5xx",
6876
+ http_status: upstream.status,
6877
+ normalized_code: transportError.normalizedCode,
6878
+ response_started: transportError.responseStarted
6879
+ };
6663
6880
  logger.warn(
6664
6881
  `[Agent Credential Proxy] upstream returned HTTP ${upstream.status} (agent=${registration.agentId}, launch=${registration.launchId ?? "none"}, correlation=${correlationId}, method=${method}, path=${target.pathname}, route_family=${transportError.routeFamily}, target_host_class=${transportError.targetHostClass})`
6665
6882
  );
6666
6883
  registration.inboxCoordinator?.recordTransportNormalizedError?.(transportError);
6884
+ } else {
6885
+ proxySpanStatus = "ok";
6886
+ proxySpanEndAttrs = {
6887
+ outcome: "upstream_response",
6888
+ http_status: upstream.status
6889
+ };
6667
6890
  }
6668
6891
  if (shouldBufferJsonResponse(upstream, target.pathname, registration)) {
6669
6892
  const responseText = await upstream.text();
@@ -6695,8 +6918,28 @@ async function handleProxyRequest(req, res) {
6695
6918
  );
6696
6919
  registration.inboxCoordinator?.recordProxyFailure?.(failure);
6697
6920
  registration.inboxCoordinator?.recordTransportNormalizedError?.(transportError);
6921
+ proxySpanStatus = "error";
6922
+ proxySpanEndAttrs = {
6923
+ outcome: "proxy_failure",
6924
+ normalized_code: transportError.normalizedCode,
6925
+ response_started: transportError.responseStarted,
6926
+ ...transportError.upstreamStatus === void 0 ? {} : { http_status: transportError.upstreamStatus }
6927
+ };
6698
6928
  writeProxyFailureResponse(res, failure);
6929
+ } finally {
6930
+ proxySpan.end(proxySpanStatus, { attrs: proxySpanEndAttrs });
6931
+ }
6932
+ }
6933
+ function firstRequestHeader(value) {
6934
+ if (typeof value === "string") return value;
6935
+ return value?.[0];
6936
+ }
6937
+ function normalizedProxyMethod(method) {
6938
+ const normalized = method.toUpperCase();
6939
+ if (normalized === "GET" || normalized === "POST" || normalized === "PUT" || normalized === "PATCH" || normalized === "DELETE" || normalized === "HEAD" || normalized === "OPTIONS") {
6940
+ return normalized;
6699
6941
  }
6942
+ return "OTHER";
6700
6943
  }
6701
6944
  function writeProxyFailureResponse(res, failure) {
6702
6945
  if (res.writableEnded) return;
@@ -6997,7 +7240,12 @@ async function loadRecentTargetMessages(registration, headers, target) {
6997
7240
  const historyHeaders = new Headers(headers);
6998
7241
  historyHeaders.delete("content-length");
6999
7242
  historyHeaders.delete("content-type");
7000
- const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
7243
+ const res = await daemonFetch(
7244
+ historyUrl,
7245
+ { method: "GET", headers: historyHeaders },
7246
+ process.env,
7247
+ { isolationKey: AGENT_CREDENTIAL_PROXY_FETCH_ISOLATION_KEY }
7248
+ );
7001
7249
  if (!res.ok) return [];
7002
7250
  const parsed = await res.json().catch(() => null);
7003
7251
  return Array.isArray(parsed?.messages) ? normalizeInboxVisibleMessages(parsed.messages, target) : [];
@@ -7113,7 +7361,8 @@ async function registerAgentCredentialProxy(input) {
7113
7361
  agentId: input.agentId,
7114
7362
  launchId: input.launchId ?? null,
7115
7363
  activeCapabilities: input.activeCapabilities,
7116
- inboxCoordinator: input.inboxCoordinator
7364
+ inboxCoordinator: input.inboxCoordinator,
7365
+ tracer: input.tracer ?? noopTracer
7117
7366
  });
7118
7367
  return {
7119
7368
  proxyUrl: server.proxyUrl,
@@ -7372,7 +7621,8 @@ async function prepareCliTransport(ctx, extraEnv = {}, platform = process.platfo
7372
7621
  serverUrl: ctx.config.serverUrl,
7373
7622
  apiKey: agentCredentialKey,
7374
7623
  activeCapabilities: DEFAULT_ACTIVE_CAPABILITIES,
7375
- inboxCoordinator: ctx.agentCredentialProxyInboxCoordinator
7624
+ inboxCoordinator: ctx.agentCredentialProxyInboxCoordinator,
7625
+ tracer: ctx.tracer
7376
7626
  });
7377
7627
  const launchPart = buildCliTransportLaunchPart(ctx.launchId);
7378
7628
  const proxyTokenDir = path2.join(slockHome, "agent-proxy-tokens", safePathPart(ctx.agentId));
@@ -11731,7 +11981,11 @@ function buildBuiltInAgentDir(workingDirectory) {
11731
11981
  async function buildPiSpawnEnv(ctx) {
11732
11982
  return (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
11733
11983
  }
11734
- function seedBuiltInAuthStorage(authStorage, runtimeConfig) {
11984
+ function seedPiSessionAuthStorage(authStorage, runtimeConfig) {
11985
+ if (runtimeConfig.runtime === "pi" && runtimeConfig.provider?.kind === "pi-builtin") {
11986
+ authStorage.setRuntimeApiKey(runtimeConfig.provider.providerId, runtimeConfig.provider.apiKey);
11987
+ return;
11988
+ }
11735
11989
  if (runtimeConfig.runtime !== "builtin") return;
11736
11990
  if (runtimeConfig.provider.kind === "preset") {
11737
11991
  authStorage.setRuntimeApiKey(runtimeConfig.provider.providerId, runtimeConfig.provider.apiKey);
@@ -11744,11 +11998,12 @@ function seedBuiltInAuthStorage(authStorage, runtimeConfig) {
11744
11998
  }
11745
11999
  }
11746
12000
  function buildPiSessionCreateEnvPatch(runtimeConfig, envVars) {
11747
- if (runtimeConfig.runtime !== "builtin" || !envVars) return envVars;
11748
- const providerApiKeyEnvNames = /* @__PURE__ */ new Set([
12001
+ if (!envVars) return null;
12002
+ const providerApiKeyEnvNames = runtimeConfig.runtime === "builtin" ? /* @__PURE__ */ new Set([
11749
12003
  ...Object.values(BUILTIN_RUNTIME_PROVIDER_ENV_KEYS),
11750
12004
  ...Object.values(BUILTIN_RUNTIME_GATEWAY_PROVIDER_ENV_KEYS)
11751
- ]);
12005
+ ]) : runtimeConfig.runtime === "pi" && runtimeConfig.provider?.kind === "pi-builtin" ? /* @__PURE__ */ new Set([PI_BUILTIN_PROVIDER_ENV_KEYS[runtimeConfig.provider.providerId]]) : /* @__PURE__ */ new Set();
12006
+ if (providerApiKeyEnvNames.size === 0) return envVars;
11752
12007
  const filtered = Object.fromEntries(
11753
12008
  Object.entries(envVars).filter(([key]) => !providerApiKeyEnvNames.has(key))
11754
12009
  );
@@ -12106,7 +12361,7 @@ async function createPiAgentSessionForContext(ctx, sessionId, opts = {}) {
12106
12361
  const agentDir = opts.agentDir ?? spawnEnv.PI_CODING_AGENT_DIR ?? getAgentDir();
12107
12362
  mkdirSync3(agentDir, { recursive: true });
12108
12363
  const authStorage = AuthStorage.create(path12.join(agentDir, "auth.json"));
12109
- seedBuiltInAuthStorage(authStorage, runtimeConfig);
12364
+ seedPiSessionAuthStorage(authStorage, runtimeConfig);
12110
12365
  const settingsManager = SettingsManager.create(ctx.workingDirectory, agentDir);
12111
12366
  const providerEnvScope = opts.isolateHostProviderEnv ? BUILTIN_BLOCKED_HOST_PROVIDER_ENV_KEYS : void 0;
12112
12367
  const sessionCreateEnvPatch = buildPiSessionCreateEnvPatch(runtimeConfig, launchRuntimeFields.envVars);
@@ -12229,22 +12484,17 @@ async function createPiAgentSessionForContext(ctx, sessionId, opts = {}) {
12229
12484
  throw error;
12230
12485
  }
12231
12486
  }
12487
+ var piPromptsInFlight = 0;
12232
12488
  var PiSdkRuntimeSession = class {
12233
- constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext, opts = {}) {
12489
+ constructor(ctx, setCurrentSessionId, sessionFactory = createPiAgentSessionForContext) {
12234
12490
  this.ctx = ctx;
12235
12491
  this.setCurrentSessionId = setCurrentSessionId;
12236
12492
  this.sessionFactory = sessionFactory;
12237
12493
  this.mappingState = createPiSdkEventMappingState(ctx.config.sessionId || null);
12238
- const runtimeConfig = hydrateRuntimeConfig(ctx.config);
12239
- const launchRuntimeFields = runtimeConfigToLaunchFields(runtimeConfig);
12240
- this.sdkCallEnvPatch = buildPiSessionCreateEnvPatch(runtimeConfig, launchRuntimeFields.envVars);
12241
- this.sdkCallEnvRemoveFirst = opts.isolateHostProviderEnv ? BUILTIN_BLOCKED_HOST_PROVIDER_ENV_KEYS : void 0;
12242
12494
  }
12243
12495
  descriptor = PI_RUNTIME_SESSION_DESCRIPTOR;
12244
12496
  events = new EventEmitter2();
12245
12497
  mappingState;
12246
- sdkCallEnvPatch;
12247
- sdkCallEnvRemoveFirst;
12248
12498
  session = null;
12249
12499
  unsubscribe = null;
12250
12500
  started = false;
@@ -12377,10 +12627,41 @@ var PiSdkRuntimeSession = class {
12377
12627
  return !this.didClose && this.session === session && !session.isStreaming;
12378
12628
  }
12379
12629
  deferSdkCall(invoke) {
12630
+ const queuedAt = currentTimeMs();
12380
12631
  setImmediate(() => {
12381
12632
  if (this.didClose) return;
12633
+ const span = this.ctx.tracer?.startSpan("daemon.pi.prompt", {
12634
+ surface: "daemon",
12635
+ kind: "internal",
12636
+ attrs: {
12637
+ agentId: this.ctx.agentId,
12638
+ launchId: this.ctx.launchId || void 0,
12639
+ runtime: this.ctx.config.runtime
12640
+ }
12641
+ });
12642
+ const startedAt = currentTimeMs();
12643
+ piPromptsInFlight += 1;
12644
+ span?.addEvent("daemon.pi.prompt.start", {
12645
+ agentId: this.ctx.agentId,
12646
+ queued_ms: startedAt - queuedAt,
12647
+ prompts_in_flight: piPromptsInFlight
12648
+ });
12649
+ let settled = false;
12650
+ const settle = (status) => {
12651
+ if (settled) return;
12652
+ settled = true;
12653
+ piPromptsInFlight -= 1;
12654
+ span?.end(status, {
12655
+ attrs: {
12656
+ queued_ms: startedAt - queuedAt,
12657
+ duration_ms: currentTimeMs() - startedAt,
12658
+ prompts_in_flight_after: piPromptsInFlight
12659
+ }
12660
+ });
12661
+ };
12382
12662
  try {
12383
- void withProcessEnvPatch(this.sdkCallEnvPatch, invoke, { removeFirst: this.sdkCallEnvRemoveFirst }).catch((error) => {
12663
+ void invoke().then(() => settle("ok")).catch((error) => {
12664
+ settle("error");
12384
12665
  if (this.didClose) return;
12385
12666
  this.events.emit("runtime_event", {
12386
12667
  kind: "error",
@@ -12388,6 +12669,7 @@ var PiSdkRuntimeSession = class {
12388
12669
  });
12389
12670
  });
12390
12671
  } catch (error) {
12672
+ settle("error");
12391
12673
  if (this.didClose) return;
12392
12674
  this.events.emit("runtime_event", {
12393
12675
  kind: "error",
@@ -12513,7 +12795,7 @@ var BuiltInDriver = class extends PiDriver {
12513
12795
  exposeLaunchTraceEvidence: true,
12514
12796
  exposeLaunchEnvToTools: false,
12515
12797
  isolateHostProviderEnv: true
12516
- }), { isolateHostProviderEnv: true });
12798
+ }));
12517
12799
  }
12518
12800
  buildSystemPrompt(config, _agentId) {
12519
12801
  return buildCliTransportSystemPrompt(config, {
@@ -12778,8 +13060,27 @@ async function reapOrphanProcesses(pids, logger2, recordTrace) {
12778
13060
  }
12779
13061
 
12780
13062
  // src/workspaces.ts
12781
- import { readdir, rm, stat } from "fs/promises";
13063
+ import { access, mkdir, readdir, rm, stat, writeFile } from "fs/promises";
12782
13064
  import path13 from "path";
13065
+ async function initializeAgentWorkspace(workspacePath, initialMemoryMd, seedFiles) {
13066
+ await mkdir(workspacePath, { recursive: true });
13067
+ const memoryMdPath = path13.join(workspacePath, "MEMORY.md");
13068
+ try {
13069
+ await access(memoryMdPath);
13070
+ } catch {
13071
+ await writeFile(memoryMdPath, initialMemoryMd);
13072
+ }
13073
+ await mkdir(path13.join(workspacePath, "notes"), { recursive: true });
13074
+ for (const { relativePath, content } of seedFiles) {
13075
+ const fullPath = path13.join(workspacePath, relativePath);
13076
+ try {
13077
+ await access(fullPath);
13078
+ } catch {
13079
+ await mkdir(path13.dirname(fullPath), { recursive: true });
13080
+ await writeFile(fullPath, content);
13081
+ }
13082
+ }
13083
+ }
12783
13084
  function isValidWorkspaceDirectoryName(directoryName) {
12784
13085
  return !directoryName.includes("/") && !directoryName.includes("\\") && !directoryName.includes("..");
12785
13086
  }
@@ -17395,29 +17696,12 @@ var AgentProcessManager = class _AgentProcessManager {
17395
17696
  const originalLaunchId = launchId || null;
17396
17697
  try {
17397
17698
  const agentDataDir = path14.join(this.dataDir, agentId);
17398
- await mkdir(agentDataDir, { recursive: true });
17399
17699
  const initialRuntimeConfig = withLocalRuntimeContext(config, agentId, agentDataDir);
17400
- const memoryMdPath = path14.join(agentDataDir, "MEMORY.md");
17401
- try {
17402
- await access(memoryMdPath);
17403
- } catch {
17404
- const initialMemoryMd = buildInitialMemoryMd(initialRuntimeConfig);
17405
- await writeFile(memoryMdPath, initialMemoryMd);
17406
- }
17407
- const notesDir = path14.join(agentDataDir, "notes");
17408
- await mkdir(notesDir, { recursive: true });
17409
- if (getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE) {
17410
- const seedFiles = buildOnboardingSeedFiles();
17411
- for (const { relativePath, content } of seedFiles) {
17412
- const fullPath = path14.join(agentDataDir, relativePath);
17413
- try {
17414
- await access(fullPath);
17415
- } catch {
17416
- await mkdir(path14.dirname(fullPath), { recursive: true });
17417
- await writeFile(fullPath, content);
17418
- }
17419
- }
17420
- }
17700
+ await initializeAgentWorkspace(
17701
+ agentDataDir,
17702
+ buildInitialMemoryMd(initialRuntimeConfig),
17703
+ getOnboardingSeedMode(config) === FIRST_CINDY_SEED_MODE ? buildOnboardingSeedFiles() : []
17704
+ );
17421
17705
  pendingStartRebind = this.lifecycleRecords.getPendingStartRebind(agentId);
17422
17706
  if (pendingStartRebind) {
17423
17707
  this.lifecycleRecords.deletePendingStartRebind(agentId);
@@ -18189,36 +18473,26 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18189
18473
  async buildSpawnConfig(agentId, config) {
18190
18474
  const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
18191
18475
  const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
18192
- if (!this.defaultAgentEnvVarsProvider) {
18193
- return runnerConfig;
18194
- }
18195
- try {
18196
- const defaultEnvVars = await this.defaultAgentEnvVarsProvider({
18197
- runtime: runnerConfig.runtime,
18198
- model: runnerConfig.model,
18199
- envVars: runnerConfig.envVars
18200
- });
18201
- if (!defaultEnvVars || Object.keys(defaultEnvVars).length === 0) {
18202
- return runnerConfig;
18203
- }
18204
- const mergedEnvVars = {
18205
- ...defaultEnvVars,
18206
- ...runnerConfig.envVars ?? {}
18207
- };
18208
- if (this.sameEnvVars(mergedEnvVars, runnerConfig.envVars)) {
18209
- return runnerConfig;
18476
+ let effectiveConfig = runnerConfig;
18477
+ if (this.defaultAgentEnvVarsProvider) {
18478
+ try {
18479
+ const defaultEnvVars = await this.defaultAgentEnvVarsProvider({
18480
+ runtime: runnerConfig.runtime,
18481
+ model: runnerConfig.model,
18482
+ envVars: runnerConfig.envVars
18483
+ });
18484
+ const mergedEnvVars = { ...defaultEnvVars ?? {}, ...runnerConfig.envVars ?? {} };
18485
+ if (!this.sameEnvVars(mergedEnvVars, runnerConfig.envVars)) {
18486
+ effectiveConfig = { ...runnerConfig, envVars: mergedEnvVars };
18487
+ }
18488
+ } catch (error) {
18489
+ const reason = error instanceof Error ? error.message : String(error);
18490
+ logger.warn(
18491
+ `[Agent ${agentId}] Failed to resolve default runtime env vars \u2014 continuing without machine-level defaults (${reason})`
18492
+ );
18210
18493
  }
18211
- return {
18212
- ...runnerConfig,
18213
- envVars: mergedEnvVars
18214
- };
18215
- } catch (error) {
18216
- const reason = error instanceof Error ? error.message : String(error);
18217
- logger.warn(
18218
- `[Agent ${agentId}] Failed to resolve default runtime env vars \u2014 continuing without machine-level defaults (${reason})`
18219
- );
18220
- return runnerConfig;
18221
18494
  }
18495
+ return effectiveConfig;
18222
18496
  }
18223
18497
  async requestManagedRunnerCredentialOnce(agentId, config) {
18224
18498
  const url = new URL(`/internal/computer/runners/${encodeURIComponent(agentId)}/credentials`, this.serverUrl);
@@ -21788,6 +22062,10 @@ var systemClock = {
21788
22062
  clearTimeout: (timer) => clearTimeout(timer)
21789
22063
  };
21790
22064
  var INBOUND_WATCHDOG_MS = 7e4;
22065
+ var LEGACY_MACHINE_KEY_MIGRATED_REASON = "legacy_machine_key_migrated";
22066
+ function isTerminalMigratedKeyRejection(statusCode, reason) {
22067
+ return statusCode === 401 && reason === LEGACY_MACHINE_KEY_MIGRATED_REASON;
22068
+ }
21791
22069
  function normalizeHandshakeReason(value) {
21792
22070
  const raw = Array.isArray(value) ? value[0] : value;
21793
22071
  if (typeof raw !== "string") return null;
@@ -21964,6 +22242,17 @@ var DaemonConnection = class {
21964
22242
  slock_reason_present: Boolean(reason),
21965
22243
  slock_reason: reason
21966
22244
  }, "error");
22245
+ if (isTerminalMigratedKeyRejection(statusCode, reason)) {
22246
+ this.shouldConnect = false;
22247
+ logger.error(
22248
+ '[Daemon] This legacy machine key was already migrated to Raft Computer. Reconnects are stopped because this key cannot authenticate again.\nStop and disable this legacy raft-daemon process. In Raft, open this offline Computer and choose "raft-computer: command not found? Install or re-run setup" to copy the install and setup commands for this server.'
22249
+ );
22250
+ this.trace("daemon.connection.reconnect_stopped", {
22251
+ status_code: statusCode,
22252
+ slock_reason: reason,
22253
+ terminal: true
22254
+ }, "error");
22255
+ }
21967
22256
  this.options.onHandshakeRejected?.({ statusCode, reason });
21968
22257
  response.resume();
21969
22258
  try {
@@ -24556,6 +24845,22 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
24556
24845
  },
24557
24846
  endAttrs: ["outcome", "ackSeq", "deliveryId", "error_class", "pending_count"]
24558
24847
  },
24848
+ "daemon.agent_proxy.request": {
24849
+ spanAttrs: [
24850
+ "route_family",
24851
+ "method",
24852
+ "trace_context_state",
24853
+ "proxy_launch_id_present",
24854
+ "correlation_id"
24855
+ ],
24856
+ endAttrs: [
24857
+ "outcome",
24858
+ "local_response_kind",
24859
+ "http_status",
24860
+ "normalized_code",
24861
+ "response_started"
24862
+ ]
24863
+ },
24559
24864
  "daemon.runtime_profile.control.received": {
24560
24865
  spanAttrs: ["agentId", "control_kind", "key_present", "launchId"],
24561
24866
  endAttrs: ["outcome", "error_class"]
@@ -24580,6 +24885,17 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
24580
24885
  },
24581
24886
  endAttrs: ["outcome", "models_count", "default_model_present", "verified_as", "error_class"]
24582
24887
  },
24888
+ // task #510: per-prompt span. `prompts_in_flight` is the cross-agent concurrency
24889
+ // observable — under the old process-env-patch lock it could never exceed 1
24890
+ // (every Pi prompt serialized process-wide); > 1 proves the queue is gone.
24891
+ // `queued_ms` is the queued -> prompt-start wait that users experienced as the
24892
+ // 60-165s stall.
24893
+ "daemon.pi.prompt": {
24894
+ spanAttrs: ["agentId", "launchId", "runtime", "queued_ms", "duration_ms", "prompts_in_flight_after"],
24895
+ eventAttrs: {
24896
+ "daemon.pi.prompt.start": ["agentId", "queued_ms", "prompts_in_flight"]
24897
+ }
24898
+ },
24583
24899
  "daemon.pi.session.create": {
24584
24900
  spanAttrs: ["agentId", "launchId", "runtime", "model", "session_id_present", "requested_model"],
24585
24901
  eventAttrs: {
@@ -24780,7 +25096,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
24780
25096
  }
24781
25097
  async function runBundledSlockCli(argv) {
24782
25098
  process.argv = [process.execPath, "slock", ...argv];
24783
- await import("./dist-54TOXXOV.js");
25099
+ await import("./dist-YLMDSQ55.js");
24784
25100
  }
24785
25101
  function detectRuntimes(tracer = noopTracer) {
24786
25102
  const ids = [];
@@ -26149,9 +26465,9 @@ var DaemonCore = class {
26149
26465
  });
26150
26466
  }
26151
26467
  emitReadyIfConnected() {
26152
- if (this.connection.connected) this.emitReady();
26468
+ if (this.connection.connected) void this.emitReady();
26153
26469
  }
26154
- emitReady() {
26470
+ async emitReady() {
26155
26471
  const { ids: runtimes, versions: runtimeVersions, diagnostics: runtimeDiagnostics = {} } = this.runtimeDetector();
26156
26472
  const runtimeInfo = runtimes.map((id) => runtimeVersions[id] ? `${id} (${runtimeVersions[id]})` : id);
26157
26473
  logger.info(`[Daemon] Detected runtimes: ${runtimeInfo.join(", ") || "none"}`);
@@ -26161,6 +26477,15 @@ var DaemonCore = class {
26161
26477
  const runningAgentIds = this.agentManager.getRunningAgentIds();
26162
26478
  const idleAgentSessions = this.agentManager.getIdleAgentSessionIds();
26163
26479
  const runtimeProfileReports = this.agentManager.getAgentRuntimeProfileReports();
26480
+ let lifecycleAcks = this.options.getComputerLifecycleAcks?.() ?? [];
26481
+ if (this.options.getComputerLifecycleReadyAcks) {
26482
+ try {
26483
+ lifecycleAcks = await this.options.getComputerLifecycleReadyAcks();
26484
+ } catch (error) {
26485
+ logger.warn(`[Daemon] Computer lifecycle attestation skipped: ${error instanceof Error ? error.message : String(error)}`);
26486
+ lifecycleAcks = [];
26487
+ }
26488
+ }
26164
26489
  this.connection.send({
26165
26490
  type: "ready",
26166
26491
  capabilities: [
@@ -26177,7 +26502,7 @@ var DaemonCore = class {
26177
26502
  daemonVersion: this.daemonVersion,
26178
26503
  ...this.computerVersion ? { computerVersion: this.computerVersion } : {},
26179
26504
  migrationTransport: this.getMigrationTransportReady(),
26180
- ...this.options.getComputerLifecycleAcks ? { lifecycleAcks: this.options.getComputerLifecycleAcks() } : {}
26505
+ ...this.options.getComputerLifecycleAcks || this.options.getComputerLifecycleReadyAcks ? { lifecycleAcks } : {}
26181
26506
  });
26182
26507
  this.recordDaemonTrace("daemon.ready.sent", {
26183
26508
  runtimes_count: runtimes.length,
@@ -26198,20 +26523,25 @@ var DaemonCore = class {
26198
26523
  logger.warn(`[Daemon] opencli wrapper refresh skipped: ${err instanceof Error ? err.message : String(err)}`);
26199
26524
  }
26200
26525
  }
26201
- this.emitReady();
26526
+ void this.emitReady();
26202
26527
  const runningAgentIds = this.agentManager.getRunningAgentIds();
26203
26528
  const idleAgentSessions = this.agentManager.getIdleAgentSessionIds();
26204
26529
  const runtimeProfileReports = this.agentManager.getAgentRuntimeProfileReports();
26205
26530
  if (this.options.onComputerUpgradeReconcile) {
26206
26531
  void Promise.resolve().then(
26207
- () => this.options.onComputerUpgradeReconcile((done) => {
26208
- this.connection.send({ type: "computer:upgrade:done", ...done });
26209
- this.recordDaemonTrace("daemon.computer_upgrade.reconciled", {
26210
- request_id: done.requestId,
26211
- ok: done.ok,
26212
- ...done.newVersion ? { new_version: done.newVersion } : {}
26213
- });
26214
- })
26532
+ () => this.options.onComputerUpgradeReconcile(
26533
+ (done) => {
26534
+ this.connection.send({ type: "computer:upgrade:done", ...done });
26535
+ this.recordDaemonTrace("daemon.computer_upgrade.reconciled", {
26536
+ request_id: done.requestId,
26537
+ ok: done.ok,
26538
+ ...done.newVersion ? { new_version: done.newVersion } : {}
26539
+ });
26540
+ },
26541
+ (progress) => {
26542
+ this.connection.send({ type: "computer:upgrade:progress", ...progress });
26543
+ }
26544
+ )
26215
26545
  ).catch((err) => {
26216
26546
  logger.error(
26217
26547
  `[Daemon] computer upgrade reconcile failed: ${err instanceof Error ? err.message : String(err)}`