@slock-ai/daemon 0.57.0 → 0.57.1-play.20260607140323

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.
@@ -1980,6 +1980,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
1980
1980
  return candidates.filter((candidate) => existsSync(candidate.path));
1981
1981
  }
1982
1982
 
1983
+ // src/authEnv.ts
1984
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
1985
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
1986
+ function scrubDaemonAuthEnv(env) {
1987
+ delete env[DAEMON_API_KEY_ENV];
1988
+ return env;
1989
+ }
1990
+ function scrubDaemonChildEnv(env) {
1991
+ delete env[DAEMON_API_KEY_ENV];
1992
+ delete env[SLOCK_AGENT_TOKEN_ENV];
1993
+ return env;
1994
+ }
1995
+
1983
1996
  // src/agentCredentialProxy.ts
1984
1997
  import { randomBytes } from "crypto";
1985
1998
  import http from "http";
@@ -2235,6 +2248,412 @@ function reduceApmGatedFlushReadiness(state, input) {
2235
2248
  };
2236
2249
  }
2237
2250
 
2251
+ // src/agentInboxStateMachine.ts
2252
+ var DEFAULT_HELD_CONTEXT_LIMIT = 3;
2253
+ function planAgentInboxSideEffect(input) {
2254
+ const heldContextLimit = input.heldContextLimit ?? DEFAULT_HELD_CONTEXT_LIMIT;
2255
+ const trace = [
2256
+ {
2257
+ step: "input",
2258
+ data: compactTraceData({
2259
+ action: input.action,
2260
+ target: input.target,
2261
+ continueAnyway: input.continueAnyway,
2262
+ pendingCount: input.pendingMessages.length,
2263
+ recentCount: input.recentMessages.length,
2264
+ existingSeenUpToSeq: input.existingSeenUpToSeq,
2265
+ modelSeenSeq: input.modelSeenSeq
2266
+ })
2267
+ }
2268
+ ];
2269
+ if (input.continueAnyway) {
2270
+ appendTrace(trace, "continue_anyway_bypass");
2271
+ return forwardPlan(input, {
2272
+ action: input.action,
2273
+ decision: "bypass",
2274
+ target: input.target,
2275
+ inboxTrustState: "trusted",
2276
+ reason: "continue_anyway"
2277
+ }, trace);
2278
+ }
2279
+ if (input.pendingMessages.length > 0) {
2280
+ appendTrace(trace, "pending_messages_found", { pendingCount: input.pendingMessages.length });
2281
+ const pending = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(input.pendingMessages, input.target));
2282
+ const boundary2 = resolveFreshnessBoundary(pending);
2283
+ if (!boundary2.ok) {
2284
+ appendTrace(trace, "pending_context_missing_boundary");
2285
+ return forwardWithoutDecision(input, trace);
2286
+ }
2287
+ const alreadySeenPending = [];
2288
+ const unconsumedMessages = [];
2289
+ for (const message of pending) {
2290
+ if (isMessageModelSeen(input, message)) {
2291
+ alreadySeenPending.push(message);
2292
+ } else {
2293
+ unconsumedMessages.push(message);
2294
+ }
2295
+ }
2296
+ appendTrace(trace, "pending_context_classified", {
2297
+ pendingCount: pending.length,
2298
+ unseenCount: unconsumedMessages.length
2299
+ });
2300
+ if (unconsumedMessages.length === 0) {
2301
+ const contiguousBoundary = maxKnownContiguousBoundary(input);
2302
+ const canAdvanceBoundary = typeof contiguousBoundary === "number" && contiguousBoundary >= boundary2.seenUpToSeq;
2303
+ appendTrace(trace, "pending_context_already_seen", { boundarySeq: boundary2.seenUpToSeq });
2304
+ return forwardPlan(input, {
2305
+ action: input.action,
2306
+ decision: "forward",
2307
+ target: input.target,
2308
+ inboxTrustState: "trusted",
2309
+ reason: "exact_target_pending_already_seen",
2310
+ pendingCount: pending.length,
2311
+ pendingMaxSeq: boundary2.seenUpToSeq,
2312
+ modelSeenSeq: contiguousBoundary,
2313
+ heldMessageCount: 0,
2314
+ omittedMessageCount: 0
2315
+ }, trace, {
2316
+ forwardSeenUpToSeq: input.action === "send" && canAdvanceBoundary ? boundary2.seenUpToSeq : void 0,
2317
+ consumeEffect: {
2318
+ type: "consume_visible_messages",
2319
+ target: input.target,
2320
+ messages: exactSeenConsumeMessages(input, pending),
2321
+ boundarySeq: canAdvanceBoundary ? boundary2.seenUpToSeq : void 0,
2322
+ source: "side_effect_preflight_context"
2323
+ }
2324
+ });
2325
+ }
2326
+ const heldBoundary = resolveFreshnessBoundary(unconsumedMessages);
2327
+ if (heldBoundary.ok) {
2328
+ const heldMessages = latestVisibleMessages(unconsumedMessages, heldContextLimit);
2329
+ const omittedMessageCount = Math.max(0, unconsumedMessages.length - heldMessages.length);
2330
+ const context = {
2331
+ heldMessages,
2332
+ newMessageCount: unconsumedMessages.length,
2333
+ shownMessageCount: heldMessages.length,
2334
+ omittedMessageCount,
2335
+ seenUpToSeq: heldBoundary.seenUpToSeq
2336
+ };
2337
+ appendTrace(trace, "held_context_built", {
2338
+ boundarySeq: boundary2.seenUpToSeq,
2339
+ heldBoundarySeq: heldBoundary.seenUpToSeq,
2340
+ heldCount: context.shownMessageCount,
2341
+ omittedCount: context.omittedMessageCount
2342
+ });
2343
+ return heldPlan(input, {
2344
+ decision: {
2345
+ action: input.action,
2346
+ decision: "local_hold",
2347
+ target: input.target,
2348
+ inboxTrustState: "trusted",
2349
+ reason: "exact_target_pending",
2350
+ pendingCount: input.pendingMessages.length,
2351
+ pendingMaxSeq: heldBoundary.seenUpToSeq,
2352
+ modelSeenSeq: input.modelSeenSeq,
2353
+ heldMessageCount: context.shownMessageCount,
2354
+ omittedMessageCount: context.omittedMessageCount
2355
+ },
2356
+ context,
2357
+ consumeMessages: sortInboxMessagesBySeq([
2358
+ ...exactSeenConsumeMessages(input, alreadySeenPending),
2359
+ ...context.heldMessages
2360
+ ]),
2361
+ consumeBoundarySeq: heldBoundary.seenUpToSeq
2362
+ }, trace);
2363
+ }
2364
+ appendTrace(trace, "pending_unseen_context_missing_boundary");
2365
+ return forwardWithoutDecision(input, trace);
2366
+ }
2367
+ const boundary = Math.max(input.existingSeenUpToSeq ?? 0, input.modelSeenSeq ?? 0);
2368
+ appendTrace(trace, "model_boundary_checked", { boundary });
2369
+ if (boundary > 0) {
2370
+ appendTrace(trace, "model_boundary_selected", { boundary });
2371
+ return forwardPlan(input, {
2372
+ action: input.action,
2373
+ decision: "forward",
2374
+ target: input.target,
2375
+ inboxTrustState: "trusted",
2376
+ reason: "model_seen_boundary",
2377
+ pendingCount: 0,
2378
+ modelSeenSeq: boundary
2379
+ }, trace, { forwardSeenUpToSeq: input.action === "send" ? boundary : void 0 });
2380
+ }
2381
+ if (input.recentMessages.length > 0) {
2382
+ return planFirstTouchRecentContext(input, heldContextLimit, trace);
2383
+ }
2384
+ appendTrace(trace, "no_context_available");
2385
+ return forwardPlan(input, {
2386
+ action: input.action,
2387
+ decision: "forward",
2388
+ target: input.target,
2389
+ inboxTrustState: "trusted",
2390
+ reason: "no_exact_target_pending_or_recent_context",
2391
+ pendingCount: 0,
2392
+ modelSeenSeq: 0
2393
+ }, trace);
2394
+ }
2395
+ function normalizeInboxVisibleMessage(message, target) {
2396
+ const targetFields = target ? parseTargetFields(target) : {};
2397
+ const normalized = {
2398
+ ...targetFields,
2399
+ ...message,
2400
+ message_id: message.message_id ?? message.id,
2401
+ timestamp: message.timestamp ?? message.createdAt,
2402
+ sender_type: message.sender_type ?? message.senderType,
2403
+ sender_name: message.sender_name ?? message.senderName,
2404
+ sender_description: message.sender_description ?? message.senderDescription ?? null
2405
+ };
2406
+ const senderId = messageSenderId(message);
2407
+ if (senderId) normalized.sender_id = senderId;
2408
+ return normalized;
2409
+ }
2410
+ function normalizeInboxVisibleMessages(messages, target) {
2411
+ return messages.map((message) => normalizeInboxVisibleMessage(message, target));
2412
+ }
2413
+ function maxInboxMessageSeq(messages) {
2414
+ let maxSeq = 0;
2415
+ for (const message of messages) {
2416
+ const seq = Math.floor(messageSeq(message));
2417
+ if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
2418
+ }
2419
+ return maxSeq > 0 ? maxSeq : void 0;
2420
+ }
2421
+ function maxKnownContiguousBoundary(input) {
2422
+ const boundary = Math.max(input.existingSeenUpToSeq ?? 0, input.modelSeenSeq ?? 0);
2423
+ return boundary > 0 ? boundary : void 0;
2424
+ }
2425
+ function exactSeenConsumeMessages(input, messages) {
2426
+ const boundary = maxKnownContiguousBoundary(input);
2427
+ return messages.map((message) => {
2428
+ const seq = Math.floor(messageSeq(message));
2429
+ if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) {
2430
+ return message;
2431
+ }
2432
+ const id = typeof message.message_id === "string" && message.message_id.length > 0 ? message.message_id : typeof message.id === "string" && message.id.length > 0 ? message.id : void 0;
2433
+ return id ? { ...message, seq: void 0 } : message;
2434
+ });
2435
+ }
2436
+ function sortInboxMessagesBySeq(messages) {
2437
+ return [...messages].sort((a, b) => messageSeq(a) - messageSeq(b));
2438
+ }
2439
+ function planFirstTouchRecentContext(input, heldContextLimit, trace) {
2440
+ const recent = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(input.recentMessages, input.target));
2441
+ const unconsumedMessages = recent.filter((message) => !isMessageModelSeen(input, message));
2442
+ appendTrace(trace, "recent_context_loaded", {
2443
+ recentCount: recent.length,
2444
+ unseenCount: unconsumedMessages.length
2445
+ });
2446
+ const boundary = resolveFreshnessBoundary(recent);
2447
+ if (!boundary.ok) {
2448
+ appendTrace(trace, "recent_context_missing_boundary");
2449
+ return forwardPlan(input, {
2450
+ action: input.action,
2451
+ decision: "forward",
2452
+ target: input.target,
2453
+ inboxTrustState: "untrusted",
2454
+ reason: "target_first_touch_recent_context_without_seq_boundary",
2455
+ pendingCount: 0,
2456
+ modelSeenSeq: 0
2457
+ }, trace);
2458
+ }
2459
+ appendTrace(trace, "recent_boundary_resolved", { boundarySeq: boundary.seenUpToSeq });
2460
+ if (unconsumedMessages.length === 0) {
2461
+ appendTrace(trace, "recent_context_already_seen");
2462
+ return forwardPlan(input, {
2463
+ action: input.action,
2464
+ decision: "forward",
2465
+ target: input.target,
2466
+ inboxTrustState: "untrusted",
2467
+ reason: "target_first_touch_recent_context_already_seen",
2468
+ pendingCount: 0,
2469
+ pendingMaxSeq: boundary.seenUpToSeq,
2470
+ modelSeenSeq: boundary.seenUpToSeq,
2471
+ heldMessageCount: 0,
2472
+ omittedMessageCount: 0
2473
+ }, trace, {
2474
+ forwardSeenUpToSeq: input.action === "send" ? boundary.seenUpToSeq : void 0,
2475
+ consumeEffect: {
2476
+ type: "consume_visible_messages",
2477
+ target: input.target,
2478
+ messages: recent,
2479
+ boundarySeq: boundary.seenUpToSeq,
2480
+ source: "side_effect_preflight_context"
2481
+ }
2482
+ });
2483
+ }
2484
+ const heldBoundary = resolveFreshnessBoundary(unconsumedMessages);
2485
+ if (!heldBoundary.ok) {
2486
+ appendTrace(trace, "unseen_context_missing_boundary");
2487
+ return forwardPlan(input, {
2488
+ action: input.action,
2489
+ decision: "forward",
2490
+ target: input.target,
2491
+ inboxTrustState: "untrusted",
2492
+ reason: "target_first_touch_unseen_context_without_seq_boundary",
2493
+ pendingCount: 0,
2494
+ modelSeenSeq: 0
2495
+ }, trace);
2496
+ }
2497
+ appendTrace(trace, "unseen_boundary_resolved", { boundarySeq: heldBoundary.seenUpToSeq });
2498
+ const heldMessages = latestVisibleMessages(unconsumedMessages, heldContextLimit);
2499
+ const omittedMessageCount = Math.max(0, unconsumedMessages.length - heldMessages.length);
2500
+ appendTrace(trace, "unseen_hold_selected", {
2501
+ heldCount: heldMessages.length,
2502
+ omittedCount: omittedMessageCount,
2503
+ consumeBoundarySeq: boundary.seenUpToSeq
2504
+ });
2505
+ return heldPlan(input, {
2506
+ decision: {
2507
+ action: input.action,
2508
+ decision: "syncing_hold",
2509
+ target: input.target,
2510
+ inboxTrustState: "untrusted",
2511
+ reason: "target_first_touch_recent_context",
2512
+ pendingCount: 0,
2513
+ pendingMaxSeq: heldBoundary.seenUpToSeq,
2514
+ modelSeenSeq: 0,
2515
+ heldMessageCount: heldMessages.length,
2516
+ omittedMessageCount
2517
+ },
2518
+ context: {
2519
+ heldMessages,
2520
+ newMessageCount: unconsumedMessages.length,
2521
+ shownMessageCount: heldMessages.length,
2522
+ omittedMessageCount,
2523
+ seenUpToSeq: boundary.seenUpToSeq
2524
+ },
2525
+ consumeMessages: recent,
2526
+ consumeBoundarySeq: boundary.seenUpToSeq
2527
+ }, trace);
2528
+ }
2529
+ function heldPlan(input, held, trace) {
2530
+ const producerFactId = buildApmFreshnessDecisionProducerFactId(input.agentId, held.decision);
2531
+ const decision = { ...held.decision, producerFactId };
2532
+ appendTrace(trace, "plan_built", {
2533
+ outcome: "held",
2534
+ decision: decision.decision,
2535
+ effectCount: 2,
2536
+ localResponseState: "held",
2537
+ seenUpToSeq: held.context.seenUpToSeq
2538
+ });
2539
+ return {
2540
+ outcome: "held",
2541
+ target: input.target,
2542
+ effects: [
2543
+ {
2544
+ type: "consume_visible_messages",
2545
+ target: input.target,
2546
+ messages: held.consumeMessages,
2547
+ boundarySeq: held.consumeBoundarySeq,
2548
+ source: "side_effect_preflight_context"
2549
+ },
2550
+ { type: "record_freshness_decision", decision }
2551
+ ],
2552
+ trace,
2553
+ localResponse: projectApmHeldFreshnessEnvelope({
2554
+ producerFactId,
2555
+ action: input.action,
2556
+ heldMessages: held.context.heldMessages,
2557
+ newMessageCount: held.context.newMessageCount,
2558
+ omittedMessageCount: held.context.omittedMessageCount,
2559
+ seenUpToSeq: held.context.seenUpToSeq
2560
+ }).body
2561
+ };
2562
+ }
2563
+ function forwardPlan(input, decision, trace, options = {}) {
2564
+ appendTrace(trace, "plan_built", {
2565
+ outcome: "forward",
2566
+ decision: decision.decision,
2567
+ effectCount: options.consumeEffect ? 2 : 1,
2568
+ forwardSeenUpToSeq: options.forwardSeenUpToSeq
2569
+ });
2570
+ return {
2571
+ outcome: "forward",
2572
+ target: input.target,
2573
+ forwardSeenUpToSeq: options.forwardSeenUpToSeq,
2574
+ effects: [
2575
+ ...options.consumeEffect ? [options.consumeEffect] : [],
2576
+ { type: "record_freshness_decision", decision }
2577
+ ],
2578
+ trace
2579
+ };
2580
+ }
2581
+ function forwardWithoutDecision(input, trace) {
2582
+ appendTrace(trace, "plan_built", {
2583
+ outcome: "forward",
2584
+ decision: "none",
2585
+ effectCount: 0
2586
+ });
2587
+ return {
2588
+ outcome: "forward",
2589
+ target: input.target,
2590
+ effects: [],
2591
+ trace
2592
+ };
2593
+ }
2594
+ function resolveFreshnessBoundary(messages) {
2595
+ const seenUpToSeq = maxInboxMessageSeq(messages);
2596
+ return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
2597
+ }
2598
+ function latestVisibleMessages(messages, limit) {
2599
+ const sorted = sortInboxMessagesBySeq(messages);
2600
+ return sorted.slice(Math.max(0, sorted.length - limit));
2601
+ }
2602
+ function isMessageModelSeen(input, message) {
2603
+ const seq = Math.floor(messageSeq(message));
2604
+ if (Number.isFinite(seq) && seq > 0 && typeof input.modelSeenSeq === "number" && input.modelSeenSeq >= seq) return true;
2605
+ return input.isMessageModelSeen?.({ target: input.target, message }) === true;
2606
+ }
2607
+ function messageSeq(message) {
2608
+ return Number(message.seq ?? 0);
2609
+ }
2610
+ function messageSenderId(message) {
2611
+ if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
2612
+ if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
2613
+ return void 0;
2614
+ }
2615
+ function appendTrace(trace, step, data) {
2616
+ const compacted = compactTraceData(data);
2617
+ trace.push(Object.keys(compacted).length > 0 ? { step, data: compacted } : { step });
2618
+ }
2619
+ function compactTraceData(data) {
2620
+ const compacted = {};
2621
+ if (!data) return compacted;
2622
+ for (const [key, value] of Object.entries(data)) {
2623
+ if (value !== void 0) compacted[key] = value;
2624
+ }
2625
+ return compacted;
2626
+ }
2627
+ function parseTargetFields(target) {
2628
+ if (target.startsWith("dm:@")) {
2629
+ const rest = target.slice("dm:@".length);
2630
+ const [peer, threadId] = rest.split(":", 2);
2631
+ if (threadId) {
2632
+ return {
2633
+ channel_type: "thread",
2634
+ channel_name: threadId,
2635
+ parent_channel_type: "dm",
2636
+ parent_channel_name: peer
2637
+ };
2638
+ }
2639
+ return { channel_type: "dm", channel_name: peer };
2640
+ }
2641
+ if (target.startsWith("#")) {
2642
+ const rest = target.slice(1);
2643
+ const [channel, threadId] = rest.split(":", 2);
2644
+ if (threadId) {
2645
+ return {
2646
+ channel_type: "thread",
2647
+ channel_name: threadId,
2648
+ parent_channel_type: "channel",
2649
+ parent_channel_name: channel
2650
+ };
2651
+ }
2652
+ return { channel_type: "channel", channel_name: channel };
2653
+ }
2654
+ return {};
2655
+ }
2656
+
2238
2657
  // src/agentInboxProjection.ts
2239
2658
  function projectAgentInboxSnapshot(messages) {
2240
2659
  const buckets = /* @__PURE__ */ new Map();
@@ -2264,16 +2683,16 @@ function projectBucket(target, messages) {
2264
2683
  channelType: latest.channel_type,
2265
2684
  pendingCount: messages.length,
2266
2685
  firstPendingMsgId: messageId(first),
2267
- firstPendingSeq: messageSeq(first),
2686
+ firstPendingSeq: messageSeq2(first),
2268
2687
  latestMsgId: messageId(latest),
2269
- latestSeq: messageSeq(latest),
2688
+ latestSeq: messageSeq2(latest),
2270
2689
  latestSenderName: latest.sender_name ?? latest.senderName,
2271
2690
  latestSenderType: normalizeSenderType(latest.sender_type ?? latest.senderType),
2272
2691
  flags: [...flags].sort()
2273
2692
  });
2274
2693
  }
2275
2694
  function compareInboxMessages(a, b) {
2276
- return (messageSeq(a) ?? 0) - (messageSeq(b) ?? 0) || (messageId(a) ?? "").localeCompare(messageId(b) ?? "");
2695
+ return (messageSeq2(a) ?? 0) - (messageSeq2(b) ?? 0) || (messageId(a) ?? "").localeCompare(messageId(b) ?? "");
2277
2696
  }
2278
2697
  function formatInboxMessageTarget(message) {
2279
2698
  if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
@@ -2289,7 +2708,7 @@ function messageId(message) {
2289
2708
  if (!message) return void 0;
2290
2709
  return nonEmptyString(message.message_id) ?? nonEmptyString(message.id);
2291
2710
  }
2292
- function messageSeq(message) {
2711
+ function messageSeq2(message) {
2293
2712
  if (!message || typeof message.seq !== "number" || !Number.isFinite(message.seq) || message.seq <= 0) return void 0;
2294
2713
  return Math.floor(message.seq);
2295
2714
  }
@@ -2814,42 +3233,9 @@ async function readRequestBody(req) {
2814
3233
  }
2815
3234
  return Buffer.concat(chunks);
2816
3235
  }
2817
- function messageSeq2(message) {
3236
+ function messageSeq3(message) {
2818
3237
  return Number(message.seq ?? 0);
2819
3238
  }
2820
- function maxMessageSeq(messages) {
2821
- let maxSeq = 0;
2822
- for (const message of messages) {
2823
- const seq = Math.floor(messageSeq2(message));
2824
- if (Number.isFinite(seq) && seq > 0) maxSeq = Math.max(maxSeq, seq);
2825
- }
2826
- return maxSeq > 0 ? maxSeq : void 0;
2827
- }
2828
- function messageSenderId(message) {
2829
- if (typeof message.sender_id === "string" && message.sender_id.length > 0) return message.sender_id;
2830
- if (typeof message.senderId === "string" && message.senderId.length > 0) return message.senderId;
2831
- return void 0;
2832
- }
2833
- function isSelfAuthoredMessage(registration, message) {
2834
- return messageSenderId(message) === registration.agentId;
2835
- }
2836
- function isMessageModelSeen(coordinator, target, message) {
2837
- const seq = Math.floor(messageSeq2(message));
2838
- const boundary = coordinator.getBoundary(target);
2839
- if (Number.isFinite(seq) && seq > 0 && typeof boundary === "number" && boundary >= seq) return true;
2840
- return coordinator.isMessageModelSeen?.({ target, message }) === true;
2841
- }
2842
- function resolveFreshnessBoundary(messages) {
2843
- const seenUpToSeq = maxMessageSeq(messages);
2844
- return typeof seenUpToSeq === "number" ? { ok: true, seenUpToSeq } : { ok: false, reason: "missing_seq_boundary" };
2845
- }
2846
- function sortBySeq(messages) {
2847
- return [...messages].sort((a, b) => messageSeq2(a) - messageSeq2(b));
2848
- }
2849
- function latestVisibleMessages(messages, limit) {
2850
- const sorted = sortBySeq(messages);
2851
- return sorted.slice(Math.max(0, sorted.length - limit));
2852
- }
2853
3239
  function localAgentApiInboxResponse(registration) {
2854
3240
  const coordinator = registration.inboxCoordinator;
2855
3241
  if (!coordinator) return void 0;
@@ -2897,9 +3283,9 @@ function localAgentApiEventsResponse(registration, target) {
2897
3283
  return { status: 400, body: parsedQuery.error };
2898
3284
  }
2899
3285
  if (pending.length === 0) return void 0;
2900
- const normalized = sortBySeq(normalizeVisibleMessages(pending));
3286
+ const normalized = sortInboxMessagesBySeq(normalizeInboxVisibleMessages(pending));
2901
3287
  const filtered = parsedQuery.sinceSeq !== null ? normalized.filter((message) => {
2902
- const seq = messageSeq2(message);
3288
+ const seq = messageSeq3(message);
2903
3289
  return Number.isFinite(seq) && seq > parsedQuery.sinceSeq;
2904
3290
  }) : normalized;
2905
3291
  const events = filtered.slice(0, parsedQuery.limit);
@@ -2908,7 +3294,7 @@ function localAgentApiEventsResponse(registration, target) {
2908
3294
  const lastSeenMsgId = newestEvent?.message_id ?? newestEvent?.id ?? null;
2909
3295
  const lastSeenSeq = newestEvent?.seq ?? parsedQuery.sinceSeq;
2910
3296
  if (events.length > 0) {
2911
- coordinator.consumeVisibleMessages({ messages: events, source: "agent_api_events" });
3297
+ coordinator.consumeVisibleMessages({ messages: events, source: "agent_api_events_local" });
2912
3298
  }
2913
3299
  coordinator.recordDrainOutcome?.({
2914
3300
  source: "daemon_pending",
@@ -2930,30 +3316,6 @@ function localAgentApiEventsResponse(registration, target) {
2930
3316
  }
2931
3317
  };
2932
3318
  }
2933
- function localHeldContext(input) {
2934
- if (input.messages.length === 0) return { ok: false, reason: "empty_context" };
2935
- const normalized = sortBySeq(normalizeVisibleMessages(input.messages, input.target));
2936
- const heldMessages = latestVisibleMessages(normalized, LOCAL_HELD_CONTEXT_LIMIT);
2937
- const omittedMessageCount = Math.max(0, normalized.length - heldMessages.length);
2938
- const boundary = resolveFreshnessBoundary(normalized);
2939
- if (!boundary.ok) return boundary;
2940
- input.coordinator.consumeVisibleMessages({
2941
- target: input.target,
2942
- messages: heldMessages,
2943
- boundarySeq: boundary.seenUpToSeq,
2944
- source: input.source
2945
- });
2946
- return {
2947
- ok: true,
2948
- context: {
2949
- heldMessages,
2950
- newMessageCount: normalized.length,
2951
- shownMessageCount: heldMessages.length,
2952
- omittedMessageCount,
2953
- seenUpToSeq: boundary.seenUpToSeq
2954
- }
2955
- };
2956
- }
2957
3319
  function recordFreshnessDecision(coordinator, decision) {
2958
3320
  coordinator?.recordFreshnessDecision?.(decision);
2959
3321
  }
@@ -2967,53 +3329,6 @@ function sideEffectTarget(action, body) {
2967
3329
  const field = action === "send" ? body.target : body.channel;
2968
3330
  return typeof field === "string" && field.length > 0 ? field : void 0;
2969
3331
  }
2970
- function parseTargetFields(target) {
2971
- if (target.startsWith("dm:@")) {
2972
- const rest = target.slice("dm:@".length);
2973
- const [peer, threadId] = rest.split(":", 2);
2974
- if (threadId) {
2975
- return {
2976
- channel_type: "thread",
2977
- channel_name: threadId,
2978
- parent_channel_type: "dm",
2979
- parent_channel_name: peer
2980
- };
2981
- }
2982
- return { channel_type: "dm", channel_name: peer };
2983
- }
2984
- if (target.startsWith("#")) {
2985
- const rest = target.slice(1);
2986
- const [channel, threadId] = rest.split(":", 2);
2987
- if (threadId) {
2988
- return {
2989
- channel_type: "thread",
2990
- channel_name: threadId,
2991
- parent_channel_type: "channel",
2992
- parent_channel_name: channel
2993
- };
2994
- }
2995
- return { channel_type: "channel", channel_name: channel };
2996
- }
2997
- return {};
2998
- }
2999
- function normalizeVisibleMessage(message, target) {
3000
- const targetFields = target ? parseTargetFields(target) : {};
3001
- const normalized = {
3002
- ...targetFields,
3003
- ...message,
3004
- message_id: message.message_id ?? message.id,
3005
- timestamp: message.timestamp ?? message.createdAt,
3006
- sender_type: message.sender_type ?? message.senderType,
3007
- sender_name: message.sender_name ?? message.senderName,
3008
- sender_description: message.sender_description ?? message.senderDescription ?? null
3009
- };
3010
- const senderId = messageSenderId(message);
3011
- if (senderId) normalized.sender_id = senderId;
3012
- return normalized;
3013
- }
3014
- function normalizeVisibleMessages(messages, target) {
3015
- return messages.map((message) => normalizeVisibleMessage(message, target));
3016
- }
3017
3332
  async function loadRecentTargetMessages(registration, headers, target) {
3018
3333
  const historyUrl = new URL2("/internal/agent-api/history", registration.serverUrl);
3019
3334
  historyUrl.searchParams.set("channel", target);
@@ -3023,181 +3338,57 @@ async function loadRecentTargetMessages(registration, headers, target) {
3023
3338
  historyHeaders.delete("content-type");
3024
3339
  const res = await fetch(historyUrl, { method: "GET", headers: historyHeaders });
3025
3340
  if (!res.ok) return [];
3026
- const parsed = await res.json().catch(() => null);
3027
- return Array.isArray(parsed?.messages) ? normalizeVisibleMessages(parsed.messages, target) : [];
3028
- }
3029
- async function prepareAgentApiSideEffectForward(registration, headers, rawBody, action) {
3030
- let body;
3031
- try {
3032
- body = rawBody ? JSON.parse(rawBody) : {};
3033
- } catch {
3034
- return { bodyText: rawBody };
3035
- }
3036
- const target = sideEffectTarget(action, body);
3037
- const coordinator = registration.inboxCoordinator;
3038
- if (!target || !coordinator) {
3039
- return { bodyText: JSON.stringify(body), target };
3040
- }
3041
- if (action === "send" && body.continueAnyway === true) {
3042
- recordFreshnessDecision(coordinator, {
3043
- action,
3044
- decision: "bypass",
3045
- target,
3046
- inboxTrustState: "trusted",
3047
- reason: "continue_anyway"
3048
- });
3049
- return { bodyText: JSON.stringify(body), target };
3050
- }
3051
- const pending = coordinator.getPendingMessages(target);
3052
- if (pending.length > 0) {
3053
- const modelSeenSeq = coordinator.getBoundary(target);
3054
- const contextResult = localHeldContext({
3055
- target,
3056
- messages: pending,
3057
- coordinator,
3058
- source: "side_effect_preflight_context"
3059
- });
3060
- if (contextResult.ok) {
3061
- const { context } = contextResult;
3062
- const decision = {
3063
- action,
3064
- decision: "local_hold",
3065
- target,
3066
- inboxTrustState: "trusted",
3067
- reason: "exact_target_pending",
3068
- pendingCount: pending.length,
3069
- pendingMaxSeq: context.seenUpToSeq,
3070
- modelSeenSeq,
3071
- heldMessageCount: context.shownMessageCount,
3072
- omittedMessageCount: context.omittedMessageCount
3073
- };
3074
- const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
3075
- const localResponse = projectApmHeldFreshnessEnvelope({
3076
- producerFactId,
3077
- action,
3078
- heldMessages: context.heldMessages,
3079
- newMessageCount: context.newMessageCount,
3080
- omittedMessageCount: context.omittedMessageCount,
3081
- seenUpToSeq: context.seenUpToSeq
3082
- }).body;
3083
- recordFreshnessDecision(coordinator, { ...decision, producerFactId });
3084
- return {
3085
- bodyText: JSON.stringify(body),
3086
- target,
3087
- localResponse
3088
- };
3089
- }
3090
- return {
3091
- bodyText: JSON.stringify(body),
3092
- target
3093
- };
3094
- }
3095
- const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
3096
- const loadedBoundary = coordinator.getBoundary(target);
3097
- const boundary = Math.max(existingBoundary ?? 0, loadedBoundary ?? 0);
3098
- if (boundary > 0) {
3099
- if (action === "send") body.seenUpToSeq = boundary;
3100
- recordFreshnessDecision(coordinator, {
3101
- action,
3102
- decision: "forward",
3103
- target,
3104
- inboxTrustState: "trusted",
3105
- reason: "model_seen_boundary",
3106
- pendingCount: 0,
3107
- modelSeenSeq: boundary
3108
- });
3109
- return { bodyText: JSON.stringify(body), target };
3110
- }
3111
- const recent = await loadRecentTargetMessages(registration, headers, target);
3112
- if (recent.length > 0) {
3113
- const unconsumedCounterparty = recent.filter(
3114
- (message) => !isSelfAuthoredMessage(registration, message) && !isMessageModelSeen(coordinator, target, message)
3115
- );
3116
- const boundary2 = resolveFreshnessBoundary(recent);
3117
- if (!boundary2.ok) {
3118
- recordFreshnessDecision(coordinator, {
3119
- action,
3120
- decision: "forward",
3121
- target,
3122
- inboxTrustState: "untrusted",
3123
- reason: "target_first_touch_recent_context_without_seq_boundary",
3124
- pendingCount: 0,
3125
- modelSeenSeq: 0
3126
- });
3127
- return { bodyText: JSON.stringify(body), target };
3128
- }
3129
- if (unconsumedCounterparty.length === 0) {
3130
- const { seenUpToSeq: seenUpToSeq2 } = boundary2;
3131
- if (action === "send") body.seenUpToSeq = seenUpToSeq2;
3132
- coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq2, source: "side_effect_preflight_context" });
3133
- recordFreshnessDecision(coordinator, {
3134
- action,
3135
- decision: "forward",
3136
- target,
3137
- inboxTrustState: "untrusted",
3138
- reason: "target_first_touch_recent_context_already_seen",
3139
- pendingCount: 0,
3140
- pendingMaxSeq: seenUpToSeq2,
3141
- modelSeenSeq: seenUpToSeq2,
3142
- heldMessageCount: 0,
3143
- omittedMessageCount: 0
3144
- });
3145
- return { bodyText: JSON.stringify(body), target };
3341
+ const parsed = await res.json().catch(() => null);
3342
+ return Array.isArray(parsed?.messages) ? normalizeInboxVisibleMessages(parsed.messages, target) : [];
3343
+ }
3344
+ function applyAgentInboxStateMachineEffects(coordinator, effects) {
3345
+ for (const effect of effects) {
3346
+ if (effect.type === "record_freshness_decision") {
3347
+ recordFreshnessDecision(coordinator, effect.decision);
3348
+ continue;
3146
3349
  }
3147
- const heldBoundary = resolveFreshnessBoundary(unconsumedCounterparty);
3148
- if (!heldBoundary.ok) {
3149
- recordFreshnessDecision(coordinator, {
3150
- action,
3151
- decision: "forward",
3152
- target,
3153
- inboxTrustState: "untrusted",
3154
- reason: "target_first_touch_counterparty_context_without_seq_boundary",
3155
- pendingCount: 0,
3156
- modelSeenSeq: 0
3157
- });
3158
- return { bodyText: JSON.stringify(body), target };
3159
- }
3160
- const heldMessages = latestVisibleMessages(unconsumedCounterparty, LOCAL_HELD_CONTEXT_LIMIT);
3161
- const omittedMessageCount = Math.max(0, unconsumedCounterparty.length - heldMessages.length);
3162
- const { seenUpToSeq } = boundary2;
3163
- coordinator.consumeVisibleMessages({ target, messages: recent, boundarySeq: seenUpToSeq, source: "side_effect_preflight_context" });
3164
- const decision = {
3165
- action,
3166
- decision: "syncing_hold",
3167
- target,
3168
- inboxTrustState: "untrusted",
3169
- reason: "target_first_touch_recent_context",
3170
- pendingCount: 0,
3171
- pendingMaxSeq: heldBoundary.seenUpToSeq,
3172
- modelSeenSeq: 0,
3173
- heldMessageCount: heldMessages.length,
3174
- omittedMessageCount
3175
- };
3176
- const producerFactId = buildApmFreshnessDecisionProducerFactId(registration.agentId, decision);
3177
- recordFreshnessDecision(coordinator, { ...decision, producerFactId });
3178
- return {
3179
- bodyText: JSON.stringify(body),
3180
- target,
3181
- localResponse: projectApmHeldFreshnessEnvelope({
3182
- producerFactId,
3183
- action,
3184
- heldMessages,
3185
- newMessageCount: unconsumedCounterparty.length,
3186
- omittedMessageCount,
3187
- seenUpToSeq
3188
- }).body
3189
- };
3350
+ coordinator.consumeVisibleMessages({
3351
+ target: effect.target,
3352
+ messages: effect.messages,
3353
+ boundarySeq: effect.boundarySeq,
3354
+ source: effect.source
3355
+ });
3356
+ }
3357
+ }
3358
+ async function prepareAgentApiSideEffectForward(registration, headers, rawBody, action) {
3359
+ let body;
3360
+ try {
3361
+ body = rawBody ? JSON.parse(rawBody) : {};
3362
+ } catch {
3363
+ return { bodyText: rawBody };
3364
+ }
3365
+ const target = sideEffectTarget(action, body);
3366
+ const coordinator = registration.inboxCoordinator;
3367
+ if (!target || !coordinator) {
3368
+ return { bodyText: JSON.stringify(body), target };
3190
3369
  }
3191
- recordFreshnessDecision(coordinator, {
3370
+ const pending = coordinator.getPendingMessages(target);
3371
+ const existingBoundary = typeof body.seenUpToSeq === "number" && Number.isFinite(body.seenUpToSeq) ? Math.max(0, Math.floor(body.seenUpToSeq)) : void 0;
3372
+ const continueAnyway = action === "send" && body.continueAnyway === true;
3373
+ const shouldLoadRecent = pending.length === 0 && !continueAnyway && Math.max(existingBoundary ?? 0, coordinator.getBoundary(target) ?? 0) <= 0;
3374
+ const recent = shouldLoadRecent ? await loadRecentTargetMessages(registration, headers, target) : [];
3375
+ const plan = planAgentInboxSideEffect({
3376
+ agentId: registration.agentId,
3192
3377
  action,
3193
- decision: "forward",
3194
3378
  target,
3195
- inboxTrustState: "trusted",
3196
- reason: "no_exact_target_pending_or_recent_context",
3197
- pendingCount: 0,
3198
- modelSeenSeq: 0
3379
+ continueAnyway,
3380
+ existingSeenUpToSeq: existingBoundary,
3381
+ modelSeenSeq: coordinator.getBoundary(target),
3382
+ pendingMessages: pending,
3383
+ recentMessages: recent,
3384
+ isMessageModelSeen: (messageInput) => coordinator.isMessageModelSeen?.(messageInput) === true,
3385
+ heldContextLimit: LOCAL_HELD_CONTEXT_LIMIT
3199
3386
  });
3200
- return { bodyText: JSON.stringify(body), target };
3387
+ applyAgentInboxStateMachineEffects(coordinator, plan.effects);
3388
+ if (typeof plan.forwardSeenUpToSeq === "number") {
3389
+ if (action === "send") body.seenUpToSeq = plan.forwardSeenUpToSeq;
3390
+ }
3391
+ return { bodyText: JSON.stringify(body), target, localResponse: plan.localResponse };
3201
3392
  }
3202
3393
  function shouldBufferJsonResponse(upstream, pathname, registration) {
3203
3394
  if (!registration.inboxCoordinator) return false;
@@ -3217,27 +3408,26 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
3217
3408
  if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "held" && Array.isArray(parsed.heldMessages)) {
3218
3409
  coordinator.consumeVisibleMessages({
3219
3410
  target: sendTarget,
3220
- messages: normalizeVisibleMessages(parsed.heldMessages, sendTarget),
3411
+ messages: normalizeInboxVisibleMessages(parsed.heldMessages, sendTarget),
3221
3412
  boundarySeq: typeof parsed.seenUpToSeq === "number" ? parsed.seenUpToSeq : void 0,
3222
3413
  source: "server_held_context"
3223
3414
  });
3224
3415
  return;
3225
3416
  }
3226
3417
  if (targetUrl.pathname === "/internal/agent-api/send" && parsed.state === "sent") {
3227
- const messageSeq3 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
3228
- if (sendTarget && messageSeq3 && messageSeq3 > 0) {
3418
+ const messageSeq4 = typeof parsed.messageSeq === "number" && Number.isFinite(parsed.messageSeq) ? Math.floor(parsed.messageSeq) : void 0;
3419
+ if (sendTarget && messageSeq4 && messageSeq4 > 0) {
3229
3420
  coordinator.consumeVisibleMessages({
3230
3421
  target: sendTarget,
3231
- messages: normalizeVisibleMessages([{ seq: messageSeq3, id: parsed.messageId }], sendTarget),
3232
- boundarySeq: messageSeq3,
3422
+ messages: normalizeInboxVisibleMessages([{ id: parsed.messageId }], sendTarget),
3233
3423
  source: "agent_api_send_commit"
3234
3424
  });
3235
3425
  }
3236
3426
  return;
3237
3427
  }
3238
3428
  if (targetUrl.pathname === "/internal/agent-api/events" && Array.isArray(parsed.events)) {
3239
- const messages = normalizeVisibleMessages(parsed.events);
3240
- coordinator.consumeVisibleMessages({ messages, source: "agent_api_events" });
3429
+ const messages = normalizeInboxVisibleMessages(parsed.events);
3430
+ coordinator.consumeVisibleMessages({ messages, source: "agent_api_events_server" });
3241
3431
  coordinator.recordDrainOutcome?.({
3242
3432
  source: "server_events",
3243
3433
  sinceCursorKind: parseAgentApiEventsQuery(targetUrl).sinceCursorKind,
@@ -3249,8 +3439,8 @@ function consumeVisibleResponse(registration, targetUrl, sendTarget, responseTex
3249
3439
  }
3250
3440
  if (targetUrl.pathname === "/internal/agent-api/history" && Array.isArray(parsed.messages)) {
3251
3441
  const target = targetUrl.searchParams.get("channel") ?? void 0;
3252
- const messages = normalizeVisibleMessages(parsed.messages, target);
3253
- coordinator.consumeVisibleMessages({ target, messages, boundarySeq: maxMessageSeq(messages), source: "agent_api_history" });
3442
+ const messages = normalizeInboxVisibleMessages(parsed.messages, target);
3443
+ coordinator.consumeVisibleMessages({ target, messages, boundarySeq: maxInboxMessageSeq(messages), source: "agent_api_history" });
3254
3444
  }
3255
3445
  }
3256
3446
  async function registerAgentCredentialProxy(input) {
@@ -3289,7 +3479,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
3289
3479
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
3290
3480
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
3291
3481
  var RAW_CREDENTIAL_ENV_DENYLIST = [
3292
- "SLOCK_AGENT_CREDENTIAL_KEY"
3482
+ "SLOCK_AGENT_TOKEN",
3483
+ "SLOCK_AGENT_CREDENTIAL_KEY",
3484
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
3293
3485
  ];
3294
3486
  var cachedOpencliBinPath;
3295
3487
  function resolveOpencliBinPath() {
@@ -3504,7 +3696,7 @@ exec ${shellSingleQuote(process.execPath)} ${shellSingleQuote(opencliBinPath)} "
3504
3696
  ...agentCredentialProxy ? {} : { SLOCK_AGENT_TOKEN_FILE: tokenFile },
3505
3697
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
3506
3698
  };
3507
- delete spawnEnv.SLOCK_AGENT_TOKEN;
3699
+ scrubDaemonChildEnv(spawnEnv);
3508
3700
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
3509
3701
  delete spawnEnv[key];
3510
3702
  }
@@ -3933,7 +4125,7 @@ function resolveCommandOnWindows(command, env, execFileSyncFn, existsSyncFn) {
3933
4125
  }
3934
4126
  function resolveCommandOnPath(command, deps = {}) {
3935
4127
  const platform = deps.platform ?? process.platform;
3936
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4128
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3937
4129
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3938
4130
  const existsSyncFn = deps.existsSyncFn ?? existsSync2;
3939
4131
  if (platform === "win32") {
@@ -3959,7 +4151,7 @@ function firstExistingPath(candidates, deps = {}) {
3959
4151
  return null;
3960
4152
  }
3961
4153
  function readCommandVersion(command, args = [], deps = {}) {
3962
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
4154
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
3963
4155
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
3964
4156
  try {
3965
4157
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -5313,11 +5505,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
5313
5505
  return parseCursorModelsOutput(String(result.stdout || ""));
5314
5506
  }
5315
5507
  function buildCursorModelProbeEnv(deps = {}) {
5316
- return withWindowsUserEnvironment({
5508
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
5317
5509
  ...deps.env ?? process.env,
5318
5510
  FORCE_COLOR: "0",
5319
5511
  NO_COLOR: "1"
5320
- }, deps);
5512
+ }, deps));
5321
5513
  }
5322
5514
  function runCursorModelsCommand() {
5323
5515
  return spawnSync("cursor-agent", ["models"], {
@@ -5373,7 +5565,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
5373
5565
  }
5374
5566
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
5375
5567
  const existsSyncFn = deps.existsSyncFn ?? existsSync4;
5376
- const env = deps.env ?? process.env;
5568
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
5377
5569
  const winPath = path6.win32;
5378
5570
  let geminiEntry = null;
5379
5571
  try {
@@ -5513,12 +5705,15 @@ var GeminiDriver = class {
5513
5705
  // src/drivers/kimi.ts
5514
5706
  import { randomUUID as randomUUID2 } from "crypto";
5515
5707
  import { spawn as spawn7 } from "child_process";
5516
- import { existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5708
+ import { chmodSync, existsSync as existsSync5, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
5517
5709
  import os3 from "os";
5518
5710
  import path7 from "path";
5519
5711
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
5520
5712
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
5521
5713
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
5714
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
5715
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
5716
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
5522
5717
  function parseToolArguments(raw) {
5523
5718
  if (typeof raw !== "string") return raw;
5524
5719
  try {
@@ -5527,6 +5722,73 @@ function parseToolArguments(raw) {
5527
5722
  return raw;
5528
5723
  }
5529
5724
  }
5725
+ function readKimiConfigSource(home = os3.homedir(), env = process.env) {
5726
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5727
+ if (inlineConfig && inlineConfig.trim()) {
5728
+ return {
5729
+ raw: inlineConfig,
5730
+ explicitPath: null,
5731
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
5732
+ };
5733
+ }
5734
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
5735
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path7.join(home, ".kimi", "config.toml");
5736
+ try {
5737
+ return {
5738
+ raw: readFileSync3(configPath, "utf8"),
5739
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5740
+ sourcePath: configPath
5741
+ };
5742
+ } catch {
5743
+ return {
5744
+ raw: null,
5745
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
5746
+ sourcePath: configPath
5747
+ };
5748
+ }
5749
+ }
5750
+ function buildKimiSpawnEnv(env = process.env) {
5751
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
5752
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
5753
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
5754
+ return scrubDaemonChildEnv(spawnEnv);
5755
+ }
5756
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
5757
+ return {
5758
+ ...process.env,
5759
+ ...ctx.config.envVars || {},
5760
+ ...overrideEnv || {}
5761
+ };
5762
+ }
5763
+ function buildKimiLaunchOptions(ctx, opts = {}) {
5764
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
5765
+ const source = readKimiConfigSource(opts.home ?? os3.homedir(), env);
5766
+ const args = [];
5767
+ let configFilePath = null;
5768
+ let configContent = null;
5769
+ if (source.explicitPath) {
5770
+ configFilePath = source.explicitPath;
5771
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
5772
+ configFilePath = path7.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
5773
+ configContent = source.raw;
5774
+ if (opts.writeGeneratedConfig !== false) {
5775
+ writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
5776
+ chmodSync(configFilePath, 384);
5777
+ }
5778
+ }
5779
+ if (configFilePath) {
5780
+ args.push("--config-file", configFilePath);
5781
+ }
5782
+ if (ctx.config.model && ctx.config.model !== "default") {
5783
+ args.push("--model", ctx.config.model);
5784
+ }
5785
+ return {
5786
+ args,
5787
+ env: buildKimiSpawnEnv(env),
5788
+ configFilePath,
5789
+ configContent
5790
+ };
5791
+ }
5530
5792
  function resolveKimiSpawn(commandArgs, deps = {}) {
5531
5793
  return {
5532
5794
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -5550,7 +5812,25 @@ var KimiDriver = class {
5550
5812
  };
5551
5813
  model = {
5552
5814
  detectedModelsVerifiedAs: "launchable",
5553
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
5815
+ toLaunchSpec: (modelId, ctx, opts) => {
5816
+ if (!ctx) return { args: ["--model", modelId] };
5817
+ const launchCtx = {
5818
+ ...ctx,
5819
+ config: {
5820
+ ...ctx.config,
5821
+ model: modelId
5822
+ }
5823
+ };
5824
+ const launch = buildKimiLaunchOptions(launchCtx, {
5825
+ home: opts?.home,
5826
+ writeGeneratedConfig: false
5827
+ });
5828
+ return {
5829
+ args: launch.args,
5830
+ env: launch.env,
5831
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
5832
+ };
5833
+ }
5554
5834
  };
5555
5835
  supportsStdinNotification = true;
5556
5836
  mcpToolPrefix = "";
@@ -5576,21 +5856,23 @@ var KimiDriver = class {
5576
5856
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
5577
5857
  ""
5578
5858
  ].join("\n"), "utf8");
5859
+ const launch = buildKimiLaunchOptions(ctx);
5579
5860
  const args = [
5580
5861
  "--wire",
5581
5862
  "--yolo",
5582
5863
  "--agent-file",
5583
5864
  agentFilePath,
5584
5865
  "--session",
5585
- this.sessionId
5866
+ this.sessionId,
5867
+ ...launch.args
5586
5868
  ];
5587
5869
  const launchRuntimeFields = runtimeConfigToLaunchFields(ctx.config);
5588
5870
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
5589
5871
  args.push("--model", launchRuntimeFields.model);
5590
5872
  }
5591
5873
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
5592
- const launch = resolveKimiSpawn(args);
5593
- const proc = spawn7(launch.command, launch.args, {
5874
+ const spawnTarget = resolveKimiSpawn(args);
5875
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
5594
5876
  cwd: ctx.workingDirectory,
5595
5877
  stdio: ["pipe", "pipe", "pipe"],
5596
5878
  env: spawnEnv,
@@ -5598,7 +5880,7 @@ var KimiDriver = class {
5598
5880
  // and has an 8191-character command-line limit. Kimi's official
5599
5881
  // installer/uv entrypoint is an executable, so launch it directly and
5600
5882
  // keep prompts on stdin / files instead of routing through cmd.exe.
5601
- shell: launch.shell
5883
+ shell: spawnTarget.shell
5602
5884
  });
5603
5885
  proc.stdin?.write(JSON.stringify({
5604
5886
  jsonrpc: "2.0",
@@ -5712,14 +5994,9 @@ var KimiDriver = class {
5712
5994
  return detectKimiModels();
5713
5995
  }
5714
5996
  };
5715
- function detectKimiModels(home = os3.homedir()) {
5716
- const configPath = path7.join(home, ".kimi", "config.toml");
5717
- let raw;
5718
- try {
5719
- raw = readFileSync3(configPath, "utf8");
5720
- } catch {
5721
- return null;
5722
- }
5997
+ function detectKimiModels(home = os3.homedir(), opts = {}) {
5998
+ const raw = readKimiConfigSource(home, opts.env).raw;
5999
+ if (raw === null) return null;
5723
6000
  const models = [];
5724
6001
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
5725
6002
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -5959,7 +6236,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
5959
6236
  const platform = deps.platform ?? process.platform;
5960
6237
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
5961
6238
  const result = spawnSyncFn("opencode", ["models"], {
5962
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
6239
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
5963
6240
  encoding: "utf8",
5964
6241
  timeout: 5e3,
5965
6242
  shell: platform === "win32"
@@ -7204,9 +7481,26 @@ var RuntimeProgressState = class {
7204
7481
  };
7205
7482
 
7206
7483
  // src/runtimeNotificationState.ts
7484
+ function computeInboxNoticeFingerprint(messages) {
7485
+ const keys = [];
7486
+ for (const m of messages) {
7487
+ const seq = typeof m.seq === "number" && Number.isFinite(m.seq) && m.seq > 0 ? Math.floor(m.seq) : null;
7488
+ if (seq !== null) {
7489
+ keys.push(`s:${seq}`);
7490
+ continue;
7491
+ }
7492
+ const id = typeof m.message_id === "string" && m.message_id.length > 0 ? m.message_id : typeof m.id === "string" && m.id.length > 0 ? m.id : "";
7493
+ if (id.length > 0) keys.push(`m:${id}`);
7494
+ }
7495
+ if (keys.length === 0) return "";
7496
+ keys.sort();
7497
+ return keys.join(",");
7498
+ }
7207
7499
  var RuntimeNotificationState = class {
7208
7500
  timerValue = null;
7209
7501
  pendingCountValue = 0;
7502
+ lastNoticeFingerprint = null;
7503
+ lastNoticeSessionId = null;
7210
7504
  get pendingCount() {
7211
7505
  return this.pendingCountValue;
7212
7506
  }
@@ -7237,6 +7531,25 @@ var RuntimeNotificationState = class {
7237
7531
  clear() {
7238
7532
  this.clearPending();
7239
7533
  this.clearTimer();
7534
+ this.clearNoticeFingerprint();
7535
+ }
7536
+ /**
7537
+ * True iff this exact unread-set was already successfully written in THIS
7538
+ * session (per-(agent,session) scope — never suppress across a session
7539
+ * change). Empty fingerprint always returns false (fail toward sending).
7540
+ */
7541
+ isDuplicateNotice(fingerprint, sessionId) {
7542
+ if (fingerprint.length === 0) return false;
7543
+ return this.lastNoticeFingerprint === fingerprint && this.lastNoticeSessionId === sessionId;
7544
+ }
7545
+ /** Register a fingerprint as written — call ONLY after a successful stdin write. */
7546
+ recordNoticeWritten(fingerprint, sessionId) {
7547
+ this.lastNoticeFingerprint = fingerprint;
7548
+ this.lastNoticeSessionId = sessionId;
7549
+ }
7550
+ clearNoticeFingerprint() {
7551
+ this.lastNoticeFingerprint = null;
7552
+ this.lastNoticeSessionId = null;
7240
7553
  }
7241
7554
  schedule(callback, delayMs) {
7242
7555
  if (this.timerValue) return false;
@@ -7368,34 +7681,24 @@ function toLocalTime(iso) {
7368
7681
  function formatChannelLabel(message) {
7369
7682
  return message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
7370
7683
  }
7371
- function formatMessageTarget(message) {
7372
- if (message.channel_type === "thread" && message.parent_channel_name) {
7373
- const shortId = getMessageShortId(message.channel_name);
7374
- if (message.parent_channel_type === "dm") {
7375
- return `dm:@${message.parent_channel_name}:${shortId}`;
7684
+ function computeTarget(channelType, channelName, parentChannelName, parentChannelType) {
7685
+ if (channelType === "thread" && parentChannelName) {
7686
+ const shortId = getMessageShortId(String(channelName ?? ""));
7687
+ if (parentChannelType === "dm") {
7688
+ return `dm:@${parentChannelName}:${shortId}`;
7376
7689
  }
7377
- return `#${message.parent_channel_name}:${shortId}`;
7690
+ return `#${parentChannelName}:${shortId}`;
7378
7691
  }
7379
- if (message.channel_type === "dm") {
7380
- return `dm:@${message.channel_name}`;
7692
+ if (channelType === "dm") {
7693
+ return `dm:@${channelName}`;
7381
7694
  }
7382
- return `#${message.channel_name}`;
7695
+ return `#${channelName}`;
7696
+ }
7697
+ function formatMessageTarget(message) {
7698
+ return computeTarget(message.channel_type, message.channel_name, message.parent_channel_name, message.parent_channel_type);
7383
7699
  }
7384
7700
  function formatVisibleMessageTarget(message) {
7385
- if (message.channel_type === "thread" && message.parent_channel_name && message.channel_name) {
7386
- const shortId = getMessageShortId(String(message.channel_name));
7387
- if (message.parent_channel_type === "dm") {
7388
- return `dm:@${message.parent_channel_name}:${shortId}`;
7389
- }
7390
- return `#${message.parent_channel_name}:${shortId}`;
7391
- }
7392
- if (message.channel_type === "dm" && message.channel_name) {
7393
- return `dm:@${message.channel_name}`;
7394
- }
7395
- if (message.channel_name) {
7396
- return `#${message.channel_name}`;
7397
- }
7398
- return null;
7701
+ return computeTarget(message.channel_type, message.channel_name, message.parent_channel_name, message.parent_channel_type);
7399
7702
  }
7400
7703
  function getMessageShortId(messageId2) {
7401
7704
  return messageId2.startsWith("thread-") ? messageId2.slice(7) : messageId2.slice(0, 8);
@@ -7617,6 +7920,8 @@ var TRAJECTORY_COALESCE_MS = 350;
7617
7920
  var ACTIVITY_HEARTBEAT_MS = 6e4;
7618
7921
  var STDIN_NOTIFICATION_INITIAL_DELAY_MS = 3e3;
7619
7922
  var STDIN_NOTIFICATION_RETRY_DELAY_MS = 15e3;
7923
+ var RUNTIME_ERROR_DELIVERY_BACKOFF_BASE_MS = 1e4;
7924
+ var RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS = 5 * 6e4;
7620
7925
  var COMPACTION_STALE_MS = 5 * 6e4;
7621
7926
  var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
7622
7927
  var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
@@ -8030,6 +8335,14 @@ function createRuntimeTraceCounters() {
8030
8335
  thinkingEvents: 0
8031
8336
  };
8032
8337
  }
8338
+ function createRuntimeErrorDeliveryBackoffState() {
8339
+ return {
8340
+ attempts: 0,
8341
+ untilMs: 0,
8342
+ timer: null,
8343
+ reason: null
8344
+ };
8345
+ }
8033
8346
  function cleanupAgentCredentialProxy(agentId, launchId) {
8034
8347
  unregisterAgentCredentialProxyForLaunch({ agentId, launchId });
8035
8348
  }
@@ -8474,6 +8787,10 @@ var AgentProcessManager = class _AgentProcessManager {
8474
8787
  defaultAgentEnvVarsProvider;
8475
8788
  tracer;
8476
8789
  stdinNotificationRetryMs;
8790
+ runtimeErrorDeliveryBackoffBaseMs;
8791
+ runtimeErrorDeliveryBackoffMaxMs;
8792
+ runtimeErrorDeliveryBackoffJitterRatio;
8793
+ runtimeErrorDeliveryBackoffFailPointForTesting;
8477
8794
  cliTransportTraceDir = null;
8478
8795
  deliveryTraceContexts = /* @__PURE__ */ new WeakMap();
8479
8796
  runtimeExitTraceAttrs = /* @__PURE__ */ new WeakMap();
@@ -8497,6 +8814,19 @@ var AgentProcessManager = class _AgentProcessManager {
8497
8814
  0,
8498
8815
  Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
8499
8816
  );
8817
+ this.runtimeErrorDeliveryBackoffBaseMs = Math.max(
8818
+ 0,
8819
+ Math.floor(opts.runtimeErrorDeliveryBackoff?.baseMs ?? RUNTIME_ERROR_DELIVERY_BACKOFF_BASE_MS)
8820
+ );
8821
+ this.runtimeErrorDeliveryBackoffMaxMs = Math.max(
8822
+ this.runtimeErrorDeliveryBackoffBaseMs,
8823
+ Math.floor(opts.runtimeErrorDeliveryBackoff?.maxMs ?? RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS)
8824
+ );
8825
+ this.runtimeErrorDeliveryBackoffJitterRatio = Math.max(
8826
+ 0,
8827
+ Math.min(1, opts.runtimeErrorDeliveryBackoff?.jitterRatio ?? 0.1)
8828
+ );
8829
+ this.runtimeErrorDeliveryBackoffFailPointForTesting = opts.runtimeErrorDeliveryBackoff?.failPointForTesting ?? null;
8500
8830
  this.maxConcurrentAgentStarts = Math.max(
8501
8831
  1,
8502
8832
  Math.floor(
@@ -8550,6 +8880,183 @@ var AgentProcessManager = class _AgentProcessManager {
8550
8880
  this.sendStdinNotification(agentId);
8551
8881
  }, delayMs);
8552
8882
  }
8883
+ clearRuntimeErrorDeliveryBackoff(ap) {
8884
+ if (ap.runtimeErrorDeliveryBackoff.timer) {
8885
+ clearTimeout(ap.runtimeErrorDeliveryBackoff.timer);
8886
+ }
8887
+ ap.runtimeErrorDeliveryBackoff = createRuntimeErrorDeliveryBackoffState();
8888
+ }
8889
+ clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, eventKind) {
8890
+ if (ap.runtimeErrorDeliveryBackoff.attempts === 0 && ap.runtimeErrorDeliveryBackoff.untilMs === 0) return;
8891
+ const attempts = ap.runtimeErrorDeliveryBackoff.attempts;
8892
+ const reason = ap.runtimeErrorDeliveryBackoff.reason;
8893
+ this.clearRuntimeErrorDeliveryBackoff(ap);
8894
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.reset", {
8895
+ agentId,
8896
+ runtime: ap.config.runtime,
8897
+ model: ap.config.model,
8898
+ launchId: ap.launchId || void 0,
8899
+ reason: reason || void 0,
8900
+ attempts,
8901
+ reset_source: eventKind
8902
+ });
8903
+ }
8904
+ runtimeErrorDeliveryBackoffRemainingMs(ap) {
8905
+ return Math.max(0, ap.runtimeErrorDeliveryBackoff.untilMs - Date.now());
8906
+ }
8907
+ scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap) {
8908
+ const delayMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
8909
+ if (delayMs <= 0 || ap.runtimeErrorDeliveryBackoff.timer) return false;
8910
+ ap.runtimeErrorDeliveryBackoff.timer = setTimeout(() => {
8911
+ ap.runtimeErrorDeliveryBackoff.timer = null;
8912
+ this.flushRuntimeErrorDeliveryBackoff(agentId);
8913
+ }, delayMs);
8914
+ ap.runtimeErrorDeliveryBackoff.timer.unref?.();
8915
+ return true;
8916
+ }
8917
+ recoverableRuntimeDeliveryBackoffReason(message, terminalFailure, stickyTerminalFailure, reasonOverride) {
8918
+ if (stickyTerminalFailure || terminalFailure?.actionRequired) return null;
8919
+ if (reasonOverride !== void 0) return reasonOverride;
8920
+ if (terminalFailure) return "recoverable_terminal_runtime_error";
8921
+ const runtimeErrorClass = buildRuntimeErrorDiagnosticEnvelope(message).spanAttrs.runtime_error_class;
8922
+ switch (runtimeErrorClass) {
8923
+ case "RateLimitError":
8924
+ return "rate_limited";
8925
+ case "ProviderServerError":
8926
+ return "provider_server_error";
8927
+ case "ProviderConnectionError":
8928
+ return "provider_connection_error";
8929
+ case "ProviderStreamError":
8930
+ return "provider_stream_error";
8931
+ default:
8932
+ return null;
8933
+ }
8934
+ }
8935
+ noteRuntimeErrorDeliveryBackoff(agentId, ap, message, terminalFailure, stickyTerminalFailure, reasonOverride) {
8936
+ const reason = this.recoverableRuntimeDeliveryBackoffReason(message, terminalFailure, stickyTerminalFailure, reasonOverride);
8937
+ if (!reason) return false;
8938
+ const attempts = ap.runtimeErrorDeliveryBackoff.attempts + 1;
8939
+ const exponentialDelayMs = this.runtimeErrorDeliveryBackoffBaseMs * 2 ** Math.max(0, attempts - 1);
8940
+ const cappedDelayMs = Math.min(this.runtimeErrorDeliveryBackoffMaxMs, exponentialDelayMs);
8941
+ const jitterMs = Math.floor(cappedDelayMs * this.runtimeErrorDeliveryBackoffJitterRatio * Math.random());
8942
+ const delayMs = Math.min(this.runtimeErrorDeliveryBackoffMaxMs, cappedDelayMs + jitterMs);
8943
+ if (ap.runtimeErrorDeliveryBackoff.timer) {
8944
+ clearTimeout(ap.runtimeErrorDeliveryBackoff.timer);
8945
+ ap.runtimeErrorDeliveryBackoff.timer = null;
8946
+ }
8947
+ ap.runtimeErrorDeliveryBackoff.attempts = attempts;
8948
+ ap.runtimeErrorDeliveryBackoff.reason = reason;
8949
+ ap.runtimeErrorDeliveryBackoff.untilMs = Date.now() + delayMs;
8950
+ if (ap.inbox.length > 0) {
8951
+ this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
8952
+ }
8953
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff", {
8954
+ agentId,
8955
+ runtime: ap.config.runtime,
8956
+ model: ap.config.model,
8957
+ launchId: ap.launchId || void 0,
8958
+ reason,
8959
+ attempts,
8960
+ delay_ms: delayMs,
8961
+ until_ms: ap.runtimeErrorDeliveryBackoff.untilMs,
8962
+ inbox_count: ap.inbox.length,
8963
+ pending_notification_count: ap.notifications.pendingCount
8964
+ });
8965
+ return true;
8966
+ }
8967
+ queueDeliveryForRuntimeErrorBackoff(agentId, ap, message) {
8968
+ const remainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
8969
+ if (remainingMs <= 0) return false;
8970
+ ap.inbox.push(message);
8971
+ if (!ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
8972
+ ap.notifications.add();
8973
+ }
8974
+ const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
8975
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
8976
+ outcome: "queued_runtime_error_backoff",
8977
+ accepted: true,
8978
+ process_present: true,
8979
+ runtime: ap.config.runtime,
8980
+ session_id_present: Boolean(ap.sessionId),
8981
+ launchId: ap.launchId || void 0,
8982
+ is_idle: ap.isIdle,
8983
+ inbox_count: ap.inbox.length,
8984
+ pending_notification_count: ap.notifications.pendingCount,
8985
+ runtime_error_backoff_remaining_ms: remainingMs,
8986
+ runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
8987
+ runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
8988
+ runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null
8989
+ }));
8990
+ return true;
8991
+ }
8992
+ flushRuntimeErrorDeliveryBackoff(agentId) {
8993
+ const ap = this.agents.get(agentId);
8994
+ if (!ap) return false;
8995
+ const remainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
8996
+ if (remainingMs > 0) {
8997
+ this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
8998
+ return false;
8999
+ }
9000
+ if (ap.inbox.length === 0) return false;
9001
+ const reason = ap.runtimeErrorDeliveryBackoff.reason || "runtime_error_backoff";
9002
+ if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
9003
+ const messages = [...ap.inbox];
9004
+ ap.notifications.clearPending();
9005
+ ap.notifications.clearTimer();
9006
+ this.commitApmIdleState(agentId, ap, false);
9007
+ this.startRuntimeTrace(agentId, ap, "runtime-error-backoff-idle-delivery", messages);
9008
+ this.broadcastActivity(agentId, "working", "Message received");
9009
+ const accepted2 = this.deliverInboxUpdateViaStdin(
9010
+ agentId,
9011
+ ap,
9012
+ messages,
9013
+ "idle",
9014
+ "runtime_error_backoff_idle_delivery"
9015
+ );
9016
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
9017
+ agentId,
9018
+ runtime: ap.config.runtime,
9019
+ model: ap.config.model,
9020
+ launchId: ap.launchId || void 0,
9021
+ reason,
9022
+ mode: "idle",
9023
+ outcome: accepted2 ? "written" : "not_written",
9024
+ inbox_count: ap.inbox.length,
9025
+ messages_count: messages.length
9026
+ });
9027
+ return accepted2;
9028
+ }
9029
+ if (!ap.driver.supportsStdinNotification || !ap.sessionId) {
9030
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
9031
+ agentId,
9032
+ runtime: ap.config.runtime,
9033
+ model: ap.config.model,
9034
+ launchId: ap.launchId || void 0,
9035
+ reason,
9036
+ outcome: "queued_without_stdin",
9037
+ inbox_count: ap.inbox.length,
9038
+ session_id_present: Boolean(ap.sessionId),
9039
+ supports_stdin_notification: ap.driver.supportsStdinNotification
9040
+ });
9041
+ return false;
9042
+ }
9043
+ if (ap.notifications.pendingCount === 0) {
9044
+ ap.notifications.add(ap.inbox.length);
9045
+ }
9046
+ const accepted = this.sendStdinNotification(agentId);
9047
+ this.recordDaemonTrace("daemon.agent.runtime_error_delivery_backoff.flush", {
9048
+ agentId,
9049
+ runtime: ap.config.runtime,
9050
+ model: ap.config.model,
9051
+ launchId: ap.launchId || void 0,
9052
+ reason,
9053
+ mode: "busy",
9054
+ outcome: accepted ? "written" : "not_written",
9055
+ inbox_count: ap.inbox.length,
9056
+ pending_notification_count: ap.notifications.pendingCount
9057
+ });
9058
+ return accepted;
9059
+ }
8553
9060
  allPendingVisibleMessages(agentId) {
8554
9061
  const collect = (messages) => (messages ?? []).filter((message) => typeof message.seq === "number" && message.seq > 0);
8555
9062
  return [
@@ -8596,10 +9103,13 @@ var AgentProcessManager = class _AgentProcessManager {
8596
9103
  if (byTarget.size === 0) return;
8597
9104
  const boundaryMap = this.visibleBoundaryMap(agentId);
8598
9105
  const visibleIds = this.visibleMessageIdMap(agentId);
9106
+ const advancesBoundary = input.source === "spawn_wake_message" || input.source === "agent_api_events_local" || input.source.startsWith("stdin_");
8599
9107
  for (const [target, bucket] of byTarget) {
8600
- const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
8601
- const previous = boundaryMap.get(target) ?? 0;
8602
- boundaryMap.set(target, Math.max(previous, highWaterSeq));
9108
+ if (advancesBoundary) {
9109
+ const highWaterSeq = Math.max(bucket.maxSeq, bucket.boundarySeq);
9110
+ const previous = boundaryMap.get(target) ?? 0;
9111
+ boundaryMap.set(target, Math.max(previous, highWaterSeq));
9112
+ }
8603
9113
  if (bucket.ids.size > 0) {
8604
9114
  let targetIds = visibleIds.get(target);
8605
9115
  if (!targetIds) {
@@ -8663,6 +9173,7 @@ var AgentProcessManager = class _AgentProcessManager {
8663
9173
  active.notifications.remove(removedActive);
8664
9174
  if (active.inbox.length === 0) {
8665
9175
  active.notifications.clear();
9176
+ this.clearRuntimeErrorDeliveryBackoff(active);
8666
9177
  }
8667
9178
  }
8668
9179
  const removedCount = removedActive + removedStarting;
@@ -9177,6 +9688,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9177
9688
  recentStdout: [],
9178
9689
  recentStderr: [],
9179
9690
  lastRuntimeError: null,
9691
+ runtimeErrorDeliveryBackoff: createRuntimeErrorDeliveryBackoffState(),
9180
9692
  spawnError: null,
9181
9693
  exitCode: null,
9182
9694
  exitSignal: null,
@@ -9279,6 +9791,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9279
9791
  const ap = this.agents.get(agentId);
9280
9792
  if (ap.runtime !== runtime) return;
9281
9793
  ap.notifications.clearTimer();
9794
+ this.clearRuntimeErrorDeliveryBackoff(ap);
9282
9795
  if (ap.pendingTrajectory?.timer) {
9283
9796
  clearTimeout(ap.pendingTrajectory.timer);
9284
9797
  }
@@ -9396,6 +9909,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9396
9909
  logger.warn(`[Agent ${agentId}] Recoverable provider stream failure (${reason}) \u2014 keeping agent wakeable`);
9397
9910
  this.sendAgentStatus(agentId, "active", ap.launchId);
9398
9911
  } else if (startupTimeoutTermination) {
9912
+ this.cacheStartupTimeoutRetryConfig(agentId, ap);
9399
9913
  logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
9400
9914
  } else {
9401
9915
  this.idleAgentConfigs.delete(agentId);
@@ -9466,6 +9980,23 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9466
9980
  this.agents.delete(agentId);
9467
9981
  this.idleAgentConfigs.delete(agentId);
9468
9982
  }
9983
+ cacheStartupTimeoutRetryConfig(agentId, ap) {
9984
+ const retryConfig = {
9985
+ ...stripManagedRunnerCredential(ap.config),
9986
+ sessionId: ap.sessionId
9987
+ };
9988
+ this.idleAgentConfigs.set(agentId, {
9989
+ config: retryConfig,
9990
+ sessionId: ap.sessionId,
9991
+ launchId: ap.launchId
9992
+ });
9993
+ this.recordDaemonTrace("daemon.agent.startup_timeout.retry_config_cached", {
9994
+ agentId,
9995
+ launchId: ap.launchId || void 0,
9996
+ runtime: ap.config.runtime,
9997
+ session_id_present: Boolean(ap.sessionId)
9998
+ });
9999
+ }
9469
10000
  async buildSpawnConfig(agentId, config) {
9470
10001
  const baseConfig = config.serverUrl === this.serverUrl ? config : { ...config, serverUrl: this.serverUrl };
9471
10002
  const runnerConfig = await this.ensureManagedRunnerCredential(agentId, baseConfig);
@@ -9693,6 +10224,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9693
10224
  return;
9694
10225
  }
9695
10226
  ap.notifications.clearTimer();
10227
+ this.clearRuntimeErrorDeliveryBackoff(ap);
9696
10228
  if (ap.activityHeartbeat) {
9697
10229
  clearInterval(ap.activityHeartbeat);
9698
10230
  }
@@ -9747,6 +10279,14 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9747
10279
  this.deliveryTraceContexts.set(message, traceContext);
9748
10280
  }
9749
10281
  const transientDelivery = this.isTransientDelivery(message);
10282
+ if (!transientDelivery && this.isVisibleMessageModelSeen(agentId, formatMessageTarget(message), message)) {
10283
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
10284
+ outcome: "dropped_already_consumed",
10285
+ accepted: true,
10286
+ process_present: Boolean(this.agents.get(agentId))
10287
+ }));
10288
+ return true;
10289
+ }
9750
10290
  const ap = this.agents.get(agentId);
9751
10291
  if (!ap) {
9752
10292
  if (this.agentsStarting.has(agentId) || this.queuedAgentStarts.has(agentId)) {
@@ -9890,6 +10430,9 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
9890
10430
  return true;
9891
10431
  }
9892
10432
  }
10433
+ if (!transientDelivery && this.queueDeliveryForRuntimeErrorBackoff(agentId, ap, message)) {
10434
+ return true;
10435
+ }
9893
10436
  if (ap.isIdle && ap.driver.supportsStdinNotification && ap.sessionId) {
9894
10437
  if (transientDelivery) {
9895
10438
  this.commitApmIdleState(agentId, ap, false);
@@ -10942,6 +11485,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
10942
11485
  return written;
10943
11486
  }
10944
11487
  case "deliver_stdin": {
11488
+ const runtimeErrorBackoffRemainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
11489
+ if (runtimeErrorBackoffRemainingMs > 0) {
11490
+ const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
11491
+ this.recordApmGatedSteeringEffectTrace(agentId, ap, effect, {
11492
+ outcome: "suppressed_runtime_error_backoff",
11493
+ runtime_error_backoff_remaining_ms: runtimeErrorBackoffRemainingMs,
11494
+ runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
11495
+ runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
11496
+ runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null,
11497
+ delivered_messages_count: 0
11498
+ });
11499
+ return false;
11500
+ }
10945
11501
  const messages = [...ap.inbox];
10946
11502
  ap.notifications.clear();
10947
11503
  if (messages.length === 0) {
@@ -11051,7 +11607,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11051
11607
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} did not emit a startup runtime event within ${timeoutMs}ms; terminating process`);
11052
11608
  this.broadcastActivity(agentId, "error", detail, [{ kind: "text", text: `Error: ${detail}` }], ap.launchId);
11053
11609
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
11054
- this.idleAgentConfigs.delete(agentId);
11610
+ this.cacheStartupTimeoutRetryConfig(agentId, ap);
11055
11611
  try {
11056
11612
  this.runtimeExitTraceAttrs.set(ap.runtime, {
11057
11613
  stop_source: "startup_timeout",
@@ -11182,6 +11738,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11182
11738
  }
11183
11739
  if (event.kind === "internal_progress") {
11184
11740
  ap.runtimeProgress.noteInternalProgress();
11741
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11185
11742
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.internal_observed", {
11186
11743
  turn_outcome: "held",
11187
11744
  turn_subtype: "runtime_progress",
@@ -11206,6 +11763,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11206
11763
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
11207
11764
  this.queueTrajectoryText(agentId, "thinking", event.text);
11208
11765
  if (ap) {
11766
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11209
11767
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
11210
11768
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "thinking" });
11211
11769
  }
@@ -11215,6 +11773,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11215
11773
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
11216
11774
  this.queueTrajectoryText(agentId, "text", event.text);
11217
11775
  if (ap) {
11776
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11218
11777
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
11219
11778
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "text" });
11220
11779
  }
@@ -11225,6 +11784,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11225
11784
  this.flushPendingTrajectory(agentId);
11226
11785
  const invocation = normalizeToolDisplayInvocation(event.name, event.input);
11227
11786
  if (ap) {
11787
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11228
11788
  const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_call" });
11229
11789
  this.recordRuntimeTraceEvent(agentId, ap, "tool.call.started", { tool: invocation.toolName });
11230
11790
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, {
@@ -11244,6 +11804,7 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11244
11804
  case "tool_output": {
11245
11805
  const invocation = normalizeToolDisplayInvocation(event.name, {});
11246
11806
  if (ap) {
11807
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
11247
11808
  const reduction = reduceApmGatedToolUse(ap.gatedSteering, { kind: "tool_output" });
11248
11809
  this.recordRuntimeTraceEvent(agentId, ap, "tool.output.observed", { tool: invocation.toolName });
11249
11810
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.continuation.expected");
@@ -11303,6 +11864,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11303
11864
  this.commitApmIdleState(agentId, ap, true);
11304
11865
  if (stickyTerminalFailure) {
11305
11866
  this.broadcastActivity(agentId, "error", stickyTerminalFailure.detail);
11867
+ } else if (ap.lastRuntimeError && ap.runtimeErrorDeliveryBackoff.attempts > 0) {
11868
+ this.broadcastActivity(agentId, "error", ap.lastRuntimeError);
11306
11869
  } else {
11307
11870
  this.broadcastActivity(agentId, "online", "Idle");
11308
11871
  }
@@ -11352,12 +11915,19 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11352
11915
  visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
11353
11916
  }
11354
11917
  const shouldDisableToolBoundaryFlush = ap.runtime.descriptor.busyDelivery === "gated" && this.isThinkingBlockMutationError(event.message);
11355
- const terminalFailure = classifyTerminalFailure(ap);
11918
+ const backoffFailPoint = this.runtimeErrorDeliveryBackoffFailPointForTesting?.({
11919
+ agentId,
11920
+ message: event.message
11921
+ }) ?? null;
11922
+ const terminalFailure = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "terminalFailure") ? backoffFailPoint.terminalFailure ?? null : classifyTerminalFailure(ap);
11923
+ const stickyTerminalFailure = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "stickyTerminalFailure") ? backoffFailPoint.stickyTerminalFailure ?? null : classifyStickyTerminalFailure(ap);
11924
+ const backoffReasonOverride = backoffFailPoint && Object.prototype.hasOwnProperty.call(backoffFailPoint, "reason") ? backoffFailPoint.reason ?? null : void 0;
11356
11925
  const reduction = reduceApmGatedError(ap.gatedSteering, {
11357
11926
  disableToolBoundaryFlush: shouldDisableToolBoundaryFlush,
11358
11927
  terminalWakeable: Boolean(ap.driver.supportsStdinNotification && terminalFailure && !terminalFailure.actionRequired)
11359
11928
  });
11360
11929
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "error" });
11930
+ this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
11361
11931
  if (reduction.shouldDisableToolBoundaryFlush) {
11362
11932
  this.recordGatedSteeringEvent(agentId, ap, "disabled", {
11363
11933
  reason: "thinking_block_mutation_error",
@@ -11378,7 +11948,6 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11378
11948
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
11379
11949
  });
11380
11950
  if (ap.driver.supportsStdinNotification && terminalFailure) {
11381
- const stickyTerminalFailure = classifyStickyTerminalFailure(ap);
11382
11951
  if (terminalFailure.actionRequired) {
11383
11952
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
11384
11953
  try {
@@ -11396,7 +11965,8 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11396
11965
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
11397
11966
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} terminal runtime error requires explicit recovery`);
11398
11967
  } else {
11399
- ap.notifications.clear();
11968
+ ap.notifications.clearPending();
11969
+ ap.notifications.clearTimer();
11400
11970
  logger.info(`[Agent ${agentId}] Marked ${ap.driver.id} wakeable after terminal runtime error`);
11401
11971
  }
11402
11972
  }
@@ -11507,6 +12077,27 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11507
12077
  if (count === 0) return false;
11508
12078
  if (ap.isIdle) return false;
11509
12079
  if (!ap.sessionId) return false;
12080
+ const runtimeErrorBackoffRemainingMs = this.runtimeErrorDeliveryBackoffRemainingMs(ap);
12081
+ if (runtimeErrorBackoffRemainingMs > 0) {
12082
+ ap.notifications.add(count);
12083
+ const scheduled = this.scheduleRuntimeErrorDeliveryBackoffFlush(agentId, ap);
12084
+ this.recordDaemonTrace("daemon.agent.stdin_notification", {
12085
+ agentId,
12086
+ runtime: ap.config.runtime,
12087
+ model: ap.config.model,
12088
+ launchId: ap.launchId || void 0,
12089
+ outcome: "suppressed_runtime_error_backoff",
12090
+ mode: "busy",
12091
+ pending_notification_count: count,
12092
+ inbox_count: ap.inbox.length,
12093
+ session_id_present: true,
12094
+ runtime_error_backoff_remaining_ms: runtimeErrorBackoffRemainingMs,
12095
+ runtime_error_backoff_attempts: ap.runtimeErrorDeliveryBackoff.attempts,
12096
+ runtime_error_backoff_reason: ap.runtimeErrorDeliveryBackoff.reason || void 0,
12097
+ runtime_error_backoff_timer_scheduled: scheduled || ap.runtimeErrorDeliveryBackoff.timer !== null
12098
+ });
12099
+ return false;
12100
+ }
11510
12101
  if (ap.gatedSteering.compacting) {
11511
12102
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.compaction_boundary.delivery_suppressed", {
11512
12103
  pendingNotificationCount: count,
@@ -11522,6 +12113,22 @@ Use ${communicationCommand(driver, "read_history")} to catch up on the channels
11522
12113
  const inboxCount = ap.inbox.length;
11523
12114
  if (inboxCount === 0) return false;
11524
12115
  const changedMessages = ap.inbox.slice(Math.max(0, ap.inbox.length - count));
12116
+ const noticeFingerprint = computeInboxNoticeFingerprint(changedMessages);
12117
+ if (ap.notifications.isDuplicateNotice(noticeFingerprint, ap.sessionId)) {
12118
+ this.recordDaemonTrace("daemon.agent.stdin_notification", {
12119
+ agentId,
12120
+ runtime: ap.config.runtime,
12121
+ model: ap.config.model,
12122
+ launchId: ap.launchId || void 0,
12123
+ outcome: "suppressed_duplicate",
12124
+ mode: "busy",
12125
+ pending_notification_count: count,
12126
+ inbox_count: ap.inbox.length,
12127
+ session_id_present: true
12128
+ });
12129
+ logger.info(`[Agent ${agentId}] Suppressing duplicate stdin inbox notice (unread-set unchanged since last write); pending=${ap.inbox.length}`);
12130
+ return false;
12131
+ }
11525
12132
  const inboxRows = projectAgentInboxSnapshot(changedMessages);
11526
12133
  const notification = `[Slock inbox notice:
11527
12134
  ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
@@ -11559,6 +12166,7 @@ ${formatAgentInboxDelta(inboxRows, { totalPendingMessages: inboxCount })}]`;
11559
12166
  inbox_target_count: inboxRows.length,
11560
12167
  session_id_present: true
11561
12168
  });
12169
+ ap.notifications.recordNoticeWritten(noticeFingerprint, ap.sessionId);
11562
12170
  return true;
11563
12171
  } else {
11564
12172
  ap.notifications.add(count);
@@ -11855,6 +12463,8 @@ var DaemonConnection = class {
11855
12463
  lastDroppedSendLogAt = 0;
11856
12464
  lastInboundAt = null;
11857
12465
  lastInboundMessageKind = null;
12466
+ pendingActivityByAgent = /* @__PURE__ */ new Map();
12467
+ latestObservedLaunchIdByAgent = /* @__PURE__ */ new Map();
11858
12468
  constructor(options) {
11859
12469
  this.options = options;
11860
12470
  this.clock = options.clock ?? systemClock;
@@ -11868,6 +12478,7 @@ var DaemonConnection = class {
11868
12478
  }
11869
12479
  disconnect() {
11870
12480
  this.shouldConnect = false;
12481
+ this.pendingActivityByAgent.clear();
11871
12482
  this.clearWatchdog();
11872
12483
  if (this.reconnectTimer) {
11873
12484
  this.clock.clearTimeout(this.reconnectTimer);
@@ -11881,9 +12492,15 @@ var DaemonConnection = class {
11881
12492
  }
11882
12493
  send(msg) {
11883
12494
  if (this.ws?.readyState === WebSocket.OPEN) {
12495
+ this.observeLaunchIdentity(msg);
12496
+ if (msg.type === "agent:activity") {
12497
+ this.pendingActivityByAgent.delete(msg.agentId);
12498
+ }
11884
12499
  this.ws.send(JSON.stringify(msg));
11885
12500
  return;
11886
12501
  }
12502
+ this.observeLaunchIdentity(msg);
12503
+ this.queueReplayableMessage(msg);
11887
12504
  const now = this.clock.now();
11888
12505
  if (now - this.lastDroppedSendLogAt > 5e3) {
11889
12506
  this.lastDroppedSendLogAt = now;
@@ -11926,6 +12543,7 @@ var DaemonConnection = class {
11926
12543
  reconnect_attempt: priorReconnectAttempt,
11927
12544
  inbound_watchdog_ms: this.options.inboundWatchdogMs ?? INBOUND_WATCHDOG_MS
11928
12545
  });
12546
+ this.flushPendingActivity(ws);
11929
12547
  this.options.onConnect();
11930
12548
  });
11931
12549
  ws.on("message", (data) => {
@@ -12023,6 +12641,65 @@ var DaemonConnection = class {
12023
12641
  this.lastInboundAt = this.clock.now();
12024
12642
  this.lastInboundMessageKind = messageKind;
12025
12643
  }
12644
+ queueReplayableMessage(msg) {
12645
+ if (msg.type !== "agent:activity") return;
12646
+ const latestLaunchId = this.latestObservedLaunchIdByAgent.get(msg.agentId);
12647
+ if (msg.launchId && latestLaunchId && msg.launchId !== latestLaunchId) {
12648
+ this.trace("daemon.connection.pending_activity_invalidated", {
12649
+ reason: "launch_changed",
12650
+ message_type: msg.type,
12651
+ agentId: msg.agentId,
12652
+ stale_launch_id_present: true,
12653
+ next_launch_id_present: true
12654
+ });
12655
+ return;
12656
+ }
12657
+ this.pendingActivityByAgent.set(msg.agentId, msg);
12658
+ }
12659
+ observeLaunchIdentity(msg) {
12660
+ const identity = this.agentLaunchIdentity(msg);
12661
+ if (!identity?.launchId) return;
12662
+ if (msg.type !== "agent:activity") {
12663
+ this.latestObservedLaunchIdByAgent.set(identity.agentId, identity.launchId);
12664
+ }
12665
+ const pending = this.pendingActivityByAgent.get(identity.agentId);
12666
+ if (!pending || pending.launchId === identity.launchId) return;
12667
+ this.pendingActivityByAgent.delete(identity.agentId);
12668
+ this.trace("daemon.connection.pending_activity_invalidated", {
12669
+ reason: "launch_changed",
12670
+ message_type: msg.type,
12671
+ agentId: identity.agentId,
12672
+ stale_launch_id_present: Boolean(pending.launchId),
12673
+ next_launch_id_present: true
12674
+ });
12675
+ }
12676
+ agentLaunchIdentity(msg) {
12677
+ switch (msg.type) {
12678
+ case "agent:activity":
12679
+ case "agent:status":
12680
+ case "agent:session":
12681
+ case "agent:runtime_profile":
12682
+ case "agent:runtime_profile:migration:ack":
12683
+ case "agent:runtime_profile:migration_done":
12684
+ case "agent:runtime_profile:daemon_release_notice:ack":
12685
+ return { agentId: msg.agentId, launchId: msg.launchId };
12686
+ default:
12687
+ return null;
12688
+ }
12689
+ }
12690
+ flushPendingActivity(ws) {
12691
+ if (this.pendingActivityByAgent.size === 0) return;
12692
+ if (this.ws !== ws || ws.readyState !== WebSocket.OPEN) return;
12693
+ const pending = [...this.pendingActivityByAgent.values()];
12694
+ this.pendingActivityByAgent.clear();
12695
+ for (const msg of pending) {
12696
+ ws.send(JSON.stringify(msg));
12697
+ }
12698
+ this.trace("daemon.connection.outbound_replayed", {
12699
+ message_type: "agent:activity",
12700
+ message_count: pending.length
12701
+ });
12702
+ }
12026
12703
  lastInboundAgeBucket() {
12027
12704
  return durationMsBucket(this.lastInboundAt == null ? null : this.clock.now() - this.lastInboundAt);
12028
12705
  }
@@ -12845,7 +13522,7 @@ var DEFAULT_TRACE_UPLOAD_URL = "https://slock-trace-upload.botiverse.dev";
12845
13522
  var RUNNER_CREDENTIAL_SCOPES = ["send", "read", "mentions", "tasks", "reactions", "server", "channels", "knowledge"];
12846
13523
  var RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS2 = 3;
12847
13524
  var RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2 = 250;
12848
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
13525
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
12849
13526
  var RunnerCredentialMintError2 = class extends Error {
12850
13527
  code;
12851
13528
  retryable;
@@ -12881,9 +13558,9 @@ function runnerCredentialErrorDetail2(error) {
12881
13558
  async function waitForRunnerCredentialRetry2() {
12882
13559
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
12883
13560
  }
12884
- function parseDaemonCliArgs(args) {
13561
+ function parseDaemonCliArgs(args, env = {}) {
12885
13562
  let serverUrl = "";
12886
- let apiKey = "";
13563
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
12887
13564
  for (let i = 0; i < args.length; i++) {
12888
13565
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
12889
13566
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -13652,6 +14329,8 @@ var DaemonCore = class {
13652
14329
  };
13653
14330
 
13654
14331
  export {
14332
+ DAEMON_API_KEY_ENV,
14333
+ scrubDaemonAuthEnv,
13655
14334
  subscribeDaemonLogs,
13656
14335
  resolveWorkspaceDirectoryPath,
13657
14336
  scanWorkspaceDirectories,