@botiverse/raft-daemon 0.63.5 → 0.63.6

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.
@@ -11,6 +11,72 @@ function makeIsMember(members) {
11
11
  return (value) => typeof value === "string" && set.has(value);
12
12
  }
13
13
 
14
+ // ../shared/src/generated/runtimeProviderDisplayNames.ts
15
+ var RUNTIME_PROVIDER_DISPLAY_NAMES = {
16
+ "anthropic": "Anthropic",
17
+ "deepseek": "DeepSeek",
18
+ "fusecode": "FuseCode",
19
+ "google": "Google",
20
+ "openai": "OpenAI",
21
+ "opencode": "OpenCode",
22
+ "opencode-go": "OpenCode Go",
23
+ "openrouter": "OpenRouter"
24
+ };
25
+
26
+ // ../shared/src/runtimeProviderDisplay.ts
27
+ function getRuntimeProviderDisplayName(providerId) {
28
+ return RUNTIME_PROVIDER_DISPLAY_NAMES[providerId] ?? humanizeRuntimeProviderSegment(providerId);
29
+ }
30
+ function formatRuntimeProviderModelLabel(modelId) {
31
+ const separatorIndex = modelId.indexOf("/");
32
+ if (separatorIndex <= 0 || separatorIndex === modelId.length - 1) return modelId;
33
+ const providerId = modelId.slice(0, separatorIndex);
34
+ const modelName = modelId.slice(separatorIndex + 1);
35
+ const providerLabel = getRuntimeProviderDisplayName(providerId);
36
+ const modelParts = modelName.split("/");
37
+ const modelLabel = humanizeRuntimeProviderSegment(modelParts[modelParts.length - 1] || modelName);
38
+ if (modelParts.length === 1) return `${modelLabel} \xB7 ${providerLabel}`;
39
+ const upstreamLabel = modelParts.slice(0, -1).map(getRuntimeProviderDisplayName).join(" / ");
40
+ return `${modelLabel} \xB7 ${upstreamLabel} via ${providerLabel}`;
41
+ }
42
+ function humanizeRuntimeProviderSegment(value) {
43
+ return value.replace(/\[(\d+)m\]/gi, "-$1m").split(/[-_/]/).filter(Boolean).map(formatRuntimeProviderLabelToken).join(" ");
44
+ }
45
+ function formatRuntimeProviderLabelToken(token) {
46
+ const normalized = token.toLowerCase();
47
+ const specialCases = {
48
+ ai: "AI",
49
+ api: "API",
50
+ chatgpt: "ChatGPT",
51
+ claude: "Claude",
52
+ codestral: "Codestral",
53
+ deepseek: "DeepSeek",
54
+ flash: "Flash",
55
+ free: "Free",
56
+ gemini: "Gemini",
57
+ glm: "GLM",
58
+ gpt: "GPT",
59
+ hy3: "HY3",
60
+ kimi: "Kimi",
61
+ minimax: "MiniMax",
62
+ nano: "Nano",
63
+ nemotron: "Nemotron",
64
+ omni: "Omni",
65
+ opus: "Opus",
66
+ pro: "Pro",
67
+ sonnet: "Sonnet",
68
+ super: "Super"
69
+ };
70
+ if (specialCases[normalized]) return specialCases[normalized];
71
+ if (normalized === "b" || normalized === "m") return normalized.toUpperCase();
72
+ if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
73
+ if (/^\d+m$/i.test(token)) return token.toUpperCase();
74
+ if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
75
+ if (/^m\d+(\.\d+)?$/i.test(token)) return token.toUpperCase();
76
+ if (/^\d/.test(token)) return token;
77
+ return normalized.charAt(0).toUpperCase() + normalized.slice(1);
78
+ }
79
+
14
80
  // ../shared/src/slockRefs.ts
15
81
  var SLOCK_REF_CHANNEL_NAME_PATTERN = String.raw`[\p{L}\p{N}_-]+`;
16
82
  var SLOCK_REF_USER_NAME_PATTERN = SLOCK_REF_CHANNEL_NAME_PATTERN;
@@ -1039,11 +1105,36 @@ var integrationApproveAgentLoginOperationSchema = z.object({
1039
1105
  scopes: z.array(z.string().trim().min(1).max(120)).max(64),
1040
1106
  draftHint: draftHintSchema
1041
1107
  });
1108
+ var integrationAppDraftFieldsSchema = z.object({
1109
+ name: z.string().trim().min(1).max(120).optional(),
1110
+ description: z.string().trim().max(1e3).optional(),
1111
+ homepageUrl: z.string().trim().max(2e3).optional(),
1112
+ returnUrl: z.string().trim().max(2e3).optional(),
1113
+ agentManifestUrl: z.string().trim().max(2e3).optional()
1114
+ });
1115
+ var integrationRegisterAppOperationSchema = integrationAppDraftFieldsSchema.extend({
1116
+ type: z.literal("integration:register_app"),
1117
+ name: z.string().trim().min(1).max(120),
1118
+ clientKey: z.string().trim().min(1).max(120),
1119
+ returnUrl: z.string().trim().min(1).max(2e3),
1120
+ scopes: z.array(z.string().trim().min(1).max(120)).max(64).default([]),
1121
+ unsafeDemoUrlOverride: z.boolean().optional(),
1122
+ draftHint: draftHintSchema
1123
+ });
1124
+ var integrationUpdateAppRegistrationOperationSchema = integrationAppDraftFieldsSchema.extend({
1125
+ type: z.literal("integration:update_app_registration"),
1126
+ clientKey: z.string().trim().min(1).max(120),
1127
+ scopes: z.array(z.string().trim().min(1).max(120)).max(64).optional(),
1128
+ unsafeDemoUrlOverride: z.boolean().optional(),
1129
+ draftHint: draftHintSchema
1130
+ });
1042
1131
  var actionCardActionSchema = z.discriminatedUnion("type", [
1043
1132
  channelCreateOperationSchema,
1044
1133
  agentCreateOperationSchema,
1045
1134
  channelAddMemberOperationSchema,
1046
- integrationApproveAgentLoginOperationSchema
1135
+ integrationApproveAgentLoginOperationSchema,
1136
+ integrationRegisterAppOperationSchema,
1137
+ integrationUpdateAppRegistrationOperationSchema
1047
1138
  ]);
1048
1139
 
1049
1140
  // ../shared/src/agentInbox.ts
@@ -1362,9 +1453,9 @@ var RUNTIME_MODELS = {
1362
1453
  ],
1363
1454
  opencode: [
1364
1455
  { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" },
1365
- { id: "deepseek/deepseek-v4-pro", label: "DeepSeek V4 Pro (OpenCode)", verified: "suggestion_only" },
1366
- { id: "openrouter/anthropic/claude-opus-4.5", label: "Claude Opus 4.5 via OpenRouter", verified: "suggestion_only" },
1367
- { id: "fusecode/opus[1m]", label: "Opus 1M via FuseCode", verified: "suggestion_only" }
1456
+ { id: "deepseek/deepseek-v4-pro", label: formatRuntimeProviderModelLabel("deepseek/deepseek-v4-pro"), verified: "suggestion_only" },
1457
+ { id: "openrouter/anthropic/claude-opus-4.5", label: formatRuntimeProviderModelLabel("openrouter/anthropic/claude-opus-4.5"), verified: "suggestion_only" },
1458
+ { id: "fusecode/opus[1m]", label: formatRuntimeProviderModelLabel("fusecode/opus[1m]"), verified: "suggestion_only" }
1368
1459
  ],
1369
1460
  pi: [
1370
1461
  { id: "default", label: "Configured Default / Auto", verified: "suggestion_only" }
@@ -1530,6 +1621,11 @@ function runtimeConfigToLaunchFields(config) {
1530
1621
  ...normalized.command ? { command: normalized.command } : {}
1531
1622
  };
1532
1623
  }
1624
+ var PRO_SEAT_MONTHLY_USD = 10;
1625
+ var PRO_AGENT_SEAT_BLOCK_SIZE = 10;
1626
+ var PRO_AGENT_SEAT_FRACTION = 1 / PRO_AGENT_SEAT_BLOCK_SIZE;
1627
+ var PRO_SEAT_ANNUAL_MONTHLY_USD = 8.8;
1628
+ var PRO_PACK_AGENT_SEATS = PRO_AGENT_SEAT_BLOCK_SIZE;
1533
1629
  var PLAN_CONFIG = {
1534
1630
  free: {
1535
1631
  displayName: "Free",
@@ -1556,9 +1652,9 @@ var PLAN_CONFIG = {
1556
1652
  },
1557
1653
  pro: {
1558
1654
  displayName: "Pro",
1559
- limits: { maxMachines: -1, maxAgents: 10, maxChannels: -1, messageHistoryDays: -1, includedAgents: 10 },
1655
+ limits: { maxMachines: -1, maxAgents: PRO_PACK_AGENT_SEATS, maxChannels: -1, messageHistoryDays: -1, includedAgents: PRO_PACK_AGENT_SEATS },
1560
1656
  comingSoon: false,
1561
- price: 20,
1657
+ price: PRO_SEAT_MONTHLY_USD,
1562
1658
  extraAgentPrice: 0
1563
1659
  }
1564
1660
  };
@@ -1568,8 +1664,8 @@ var DISPLAY_PLAN_CONFIG = {
1568
1664
  displayName: "Pro",
1569
1665
  limits: { maxMachines: -1, maxAgents: -1, maxChannels: -1, messageHistoryDays: -1, includedAgents: -1 },
1570
1666
  comingSoon: false,
1571
- price: 20,
1572
- priceCadence: "/ seat pack / month",
1667
+ price: PRO_SEAT_MONTHLY_USD,
1668
+ priceCadence: "/ seat / month",
1573
1669
  extraAgentPrice: 0,
1574
1670
  displayFeatures: [
1575
1671
  "Everything in Free",
@@ -1579,7 +1675,7 @@ var DISPLAY_PLAN_CONFIG = {
1579
1675
  "More professional features coming soon"
1580
1676
  ],
1581
1677
  displayDescription: "For builders and teams scaling agent collaboration.",
1582
- displayNote: "$17.60 / seat pack / month when billed yearly"
1678
+ displayNote: `$${PRO_SEAT_ANNUAL_MONTHLY_USD.toFixed(2)} / seat / month when billed yearly. Each human uses 1 seat; each agent uses ${PRO_AGENT_SEAT_FRACTION} seat.`
1583
1679
  },
1584
1680
  enterprise: {
1585
1681
  displayName: "Enterprise",
@@ -3426,6 +3522,7 @@ function latestVisibleMessages(messages, limit) {
3426
3522
  return sorted.slice(Math.max(0, sorted.length - limit));
3427
3523
  }
3428
3524
  function isMessageModelSeen(input, message) {
3525
+ if (messageSenderId(message) === input.agentId) return true;
3429
3526
  const seq = Math.floor(messageSeq(message));
3430
3527
  if (Number.isFinite(seq) && seq > 0 && typeof input.modelSeenSeq === "number" && input.modelSeenSeq >= seq) return true;
3431
3528
  return input.isMessageModelSeen?.({ target: input.target, message }) === true;
@@ -8047,14 +8144,6 @@ var SLOCK_AGENT_NAME = "slock";
8047
8144
  var NO_MESSAGE_PROMPT = "No new messages are pending. Stop now.";
8048
8145
  var FIRST_MESSAGE_TASK_PREFIX = "First message task (system-triggered):";
8049
8146
  var MIN_SUPPORTED_OPENCODE_VERSION = "1.14.30";
8050
- var OPENCODE_PROVIDER_LABELS = {
8051
- opencode: "OpenCode",
8052
- "opencode-go": "OpenCode Go",
8053
- openai: "OpenAI",
8054
- openrouter: "OpenRouter",
8055
- deepseek: "DeepSeek",
8056
- fusecode: "FuseCode"
8057
- };
8058
8147
  function parseOpenCodeConfigContent(raw) {
8059
8148
  if (!raw) return {};
8060
8149
  try {
@@ -8192,62 +8281,12 @@ function parseOpenCodeModelsOutput(output) {
8192
8281
  seen.add(line);
8193
8282
  models.push({
8194
8283
  id: line,
8195
- label: formatOpenCodeModelLabel(line),
8284
+ label: formatRuntimeProviderModelLabel(line),
8196
8285
  verified: "launchable"
8197
8286
  });
8198
8287
  }
8199
8288
  return models.length > 0 ? { models } : null;
8200
8289
  }
8201
- function formatOpenCodeModelLabel(modelId) {
8202
- const separatorIndex = modelId.indexOf("/");
8203
- if (separatorIndex <= 0 || separatorIndex === modelId.length - 1) return modelId;
8204
- const providerId = modelId.slice(0, separatorIndex);
8205
- const modelName = modelId.slice(separatorIndex + 1);
8206
- const providerLabel = OPENCODE_PROVIDER_LABELS[providerId] || humanizeOpenCodeSegment(providerId);
8207
- const modelParts = modelName.split("/");
8208
- const modelLabel = humanizeOpenCodeSegment(modelParts[modelParts.length - 1] || modelName);
8209
- if (modelParts.length === 1) return `${modelLabel} \xB7 ${providerLabel}`;
8210
- const upstreamLabel = modelParts.slice(0, -1).map(humanizeOpenCodeSegment).join(" / ");
8211
- return `${modelLabel} \xB7 ${upstreamLabel} via ${providerLabel}`;
8212
- }
8213
- function humanizeOpenCodeSegment(value) {
8214
- return value.replace(/\[(\d+)m\]/gi, "-$1m").split(/[-_]/).filter(Boolean).map(formatOpenCodeLabelToken).join(" ");
8215
- }
8216
- function formatOpenCodeLabelToken(token) {
8217
- const normalized = token.toLowerCase();
8218
- const specialCases = {
8219
- ai: "AI",
8220
- api: "API",
8221
- b: "B",
8222
- chatgpt: "ChatGPT",
8223
- claude: "Claude",
8224
- codestral: "Codestral",
8225
- deepseek: "DeepSeek",
8226
- flash: "Flash",
8227
- free: "Free",
8228
- gemini: "Gemini",
8229
- glm: "GLM",
8230
- gpt: "GPT",
8231
- hy3: "HY3",
8232
- kimi: "Kimi",
8233
- m: "M",
8234
- minimax: "MiniMax",
8235
- nano: "Nano",
8236
- nemotron: "Nemotron",
8237
- omni: "Omni",
8238
- opus: "Opus",
8239
- pro: "Pro",
8240
- sonnet: "Sonnet",
8241
- super: "Super"
8242
- };
8243
- if (specialCases[normalized]) return specialCases[normalized];
8244
- if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
8245
- if (/^\d+m$/i.test(token)) return token.toUpperCase();
8246
- if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
8247
- if (/^m\d+(\.\d+)?$/i.test(token)) return token.toUpperCase();
8248
- if (/^\d/.test(token)) return token;
8249
- return normalized.charAt(0).toUpperCase() + normalized.slice(1);
8250
- }
8251
8290
  function detectOpenCodeModels(home = os5.homedir(), runCommand = runOpenCodeModelsCommand) {
8252
8291
  const commandResult = runCommand(home);
8253
8292
  if (commandResult.error || commandResult.status !== 0) return null;
@@ -8530,12 +8569,6 @@ import {
8530
8569
  } from "@earendil-works/pi-coding-agent";
8531
8570
  var PI_SESSION_DIR = ".pi-sessions";
8532
8571
  var PI_SDK_COMPACTION_ENABLED = true;
8533
- var PI_PROVIDER_LABELS = {
8534
- google: "Google",
8535
- openai: "OpenAI",
8536
- openrouter: "OpenRouter",
8537
- deepseek: "DeepSeek"
8538
- };
8539
8572
  function createPiSdkEventMappingState(sessionId = null) {
8540
8573
  return {
8541
8574
  sessionId,
@@ -8577,7 +8610,7 @@ function detectPiModelsFromRegistry(modelRegistry) {
8577
8610
  seen.add(id);
8578
8611
  models.push({
8579
8612
  id,
8580
- label: `${model.name || humanizePiSegment(model.id)} \xB7 ${PI_PROVIDER_LABELS[model.provider] || humanizePiSegment(model.provider)}`,
8613
+ label: `${model.name || humanizeRuntimeProviderSegment(model.id)} \xB7 ${getRuntimeProviderDisplayName(model.provider)}`,
8581
8614
  verified: "launchable"
8582
8615
  });
8583
8616
  }
@@ -8648,25 +8681,6 @@ async function detectPiModels(modelRegistry, traceContext = {}) {
8648
8681
  );
8649
8682
  return result;
8650
8683
  }
8651
- function humanizePiSegment(value) {
8652
- return value.split(/[-_/]/).filter(Boolean).map(formatPiLabelToken).join(" ");
8653
- }
8654
- function formatPiLabelToken(token) {
8655
- const normalized = token.toLowerCase();
8656
- const specialCases = {
8657
- ai: "AI",
8658
- api: "API",
8659
- flash: "Flash",
8660
- gpt: "GPT",
8661
- pro: "Pro",
8662
- v4: "V4"
8663
- };
8664
- if (specialCases[normalized]) return specialCases[normalized];
8665
- if (/^v\d+(\.\d+)?$/.test(normalized)) return normalized.toUpperCase();
8666
- if (/^\d+[bk]$/i.test(token)) return token.toUpperCase();
8667
- if (/^\d/.test(token)) return token;
8668
- return normalized.charAt(0).toUpperCase() + normalized.slice(1);
8669
- }
8670
8684
  function piErrorMessage(error) {
8671
8685
  if (typeof error === "string" && error.trim()) return error.trim();
8672
8686
  if (error && typeof error === "object") {
@@ -9895,6 +9909,33 @@ function runnerCredentialErrorDetail(error) {
9895
9909
  async function waitForRunnerCredentialRetry() {
9896
9910
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS));
9897
9911
  }
9912
+ function probeSubprocessOsState(pid) {
9913
+ if (typeof pid !== "number") return "unknown";
9914
+ try {
9915
+ const status = readFileSync6(`/proc/${pid}/status`, "utf8");
9916
+ const match = status.match(/^State:\s+(\S)/m);
9917
+ if (match) {
9918
+ const code = match[1];
9919
+ if (code === "R") return "running";
9920
+ if (code === "S") return "sleeping";
9921
+ if (code === "D") return "uninterruptible_sleep";
9922
+ if (code === "Z") return "zombie";
9923
+ if (code === "T" || code === "t") return "stopped";
9924
+ }
9925
+ return "unknown";
9926
+ } catch {
9927
+ return "gone";
9928
+ }
9929
+ }
9930
+ function bucketMs(ms) {
9931
+ if (ms < 1e3) return "<1s";
9932
+ if (ms < 1e4) return "1-10s";
9933
+ if (ms < 6e4) return "10-60s";
9934
+ if (ms < 3e5) return "1-5m";
9935
+ if (ms < 9e5) return "5-15m";
9936
+ if (ms < 36e5) return "15-60m";
9937
+ return ">60m";
9938
+ }
9898
9939
  function stalledRecoverySigtermTimeoutMs() {
9899
9940
  return readNonNegativeIntegerEnv(
9900
9941
  "SLOCK_DAEMON_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS",
@@ -10300,6 +10341,7 @@ var STDIN_NOTIFICATION_INITIAL_DELAY_MS = 3e3;
10300
10341
  var STDIN_NOTIFICATION_RETRY_DELAY_MS = 15e3;
10301
10342
  var RUNTIME_ERROR_DELIVERY_BACKOFF_BASE_MS = 1e4;
10302
10343
  var RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS = 5 * 6e4;
10344
+ var RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD = 3;
10303
10345
  var SPAWN_FAIL_BACKOFF_BASE_MS = 1e3;
10304
10346
  var SPAWN_FAIL_BACKOFF_MAX_MS = 3e4;
10305
10347
  var SPAWN_FAIL_BACKOFF_THRESHOLD = 3;
@@ -11209,6 +11251,7 @@ var AgentProcessManager = class _AgentProcessManager {
11209
11251
  agentStartIntervalMs;
11210
11252
  startingInboxes = /* @__PURE__ */ new Map();
11211
11253
  terminalRuntimeFailures = /* @__PURE__ */ new Map();
11254
+ runtimeErrorFingerprintFences = /* @__PURE__ */ new Map();
11212
11255
  pendingStartRebinds = /* @__PURE__ */ new Map();
11213
11256
  /** Cached configs for agents whose process exited normally — enables auto-restart on next message */
11214
11257
  idleAgentConfigs = /* @__PURE__ */ new Map();
@@ -11236,11 +11279,13 @@ var AgentProcessManager = class _AgentProcessManager {
11236
11279
  // BEFORE ap exists; rate-state must persist across stop/respawn so churn can't bypass).
11237
11280
  // Explicit stop clears (fresh launch resets dedup clock — CC1 lifecycle pattern); silent
11238
11281
  // stop preserves (same-launch respawn must keep counter or churn bypasses the cap).
11282
+ pendingSpawnCause = /* @__PURE__ */ new Map();
11239
11283
  agentSpawnFailBackoff = /* @__PURE__ */ new Map();
11240
11284
  daemonVersion;
11241
11285
  computerVersion;
11242
11286
  workerUrl;
11243
11287
  fetchImpl;
11288
+ serverConnected;
11244
11289
  constructor(sendToServer, daemonApiKey, opts) {
11245
11290
  this.slockCliPath = opts.slockCliPath ?? "";
11246
11291
  this.sendToServer = sendToServer;
@@ -11256,6 +11301,7 @@ var AgentProcessManager = class _AgentProcessManager {
11256
11301
  this.computerVersion = opts.computerVersion?.trim() || null;
11257
11302
  this.workerUrl = opts.workerUrl?.trim() || null;
11258
11303
  this.fetchImpl = opts.fetchImpl ?? daemonFetch;
11304
+ this.serverConnected = opts.serverConnected ?? (() => true);
11259
11305
  this.stdinNotificationRetryMs = Math.max(
11260
11306
  0,
11261
11307
  Math.floor(opts.stdinNotificationRetryMs ?? STDIN_NOTIFICATION_RETRY_DELAY_MS)
@@ -11372,6 +11418,94 @@ var AgentProcessManager = class _AgentProcessManager {
11372
11418
  if (state.timer) clearTimeout(state.timer);
11373
11419
  this.agentSpawnFailBackoff.delete(agentId);
11374
11420
  }
11421
+ // ----- RUNTIME-ERROR FINGERPRINT FENCE (per-agent) ---------------------
11422
+ // The delivery backoff above is scoped to AgentProcess and is cleared when a
11423
+ // runtime process exits. Recoverable terminal errors intentionally keep an
11424
+ // idle restart cache so the next message can wake the agent, but that also
11425
+ // means a deterministic same-fingerprint runtime error can close/restart
11426
+ // forever. Keep this counter outside AgentProcess and reset only on real turn
11427
+ // progress or explicit stop.
11428
+ runtimeErrorFingerprintFenceResetEvent(eventKind) {
11429
+ switch (eventKind) {
11430
+ case "thinking":
11431
+ case "text":
11432
+ case "tool_call":
11433
+ case "tool_output":
11434
+ case "compaction_finished":
11435
+ case "review_finished":
11436
+ case "turn_end":
11437
+ return true;
11438
+ default:
11439
+ return false;
11440
+ }
11441
+ }
11442
+ resetRuntimeErrorFingerprintFence(agentId, resetSource, ap) {
11443
+ const state = this.runtimeErrorFingerprintFences.get(agentId);
11444
+ if (!state) return;
11445
+ this.runtimeErrorFingerprintFences.delete(agentId);
11446
+ this.recordDaemonTrace("daemon.agent.runtime_error_fingerprint_fence.reset", {
11447
+ agentId,
11448
+ runtime: ap?.config.runtime,
11449
+ model: ap?.config.model,
11450
+ launchId: ap?.launchId || state.launchId || void 0,
11451
+ fingerprint: state.fingerprint,
11452
+ attempts: state.attempts,
11453
+ reset_source: resetSource
11454
+ });
11455
+ }
11456
+ formatRuntimeErrorFingerprintFenceDetail(state) {
11457
+ return [
11458
+ `Runtime stopped after ${state.attempts} repeated runtime errors with the same fingerprint (${state.fingerprint}).`,
11459
+ `Last error: ${state.lastRuntimeError}`,
11460
+ "Restart after the runtime/tooling issue is fixed."
11461
+ ].join(" ");
11462
+ }
11463
+ noteRuntimeErrorFingerprintFence(agentId, ap, message, fingerprint, terminalFailure, stickyTerminalFailure) {
11464
+ if (!fingerprint) return null;
11465
+ if (stickyTerminalFailure || terminalFailure?.actionRequired) return null;
11466
+ let state = this.runtimeErrorFingerprintFences.get(agentId);
11467
+ if (!state || state.fingerprint !== fingerprint) {
11468
+ state = {
11469
+ fingerprint,
11470
+ attempts: 0,
11471
+ lastRuntimeError: message,
11472
+ detail: "",
11473
+ launchId: ap.launchId
11474
+ };
11475
+ this.runtimeErrorFingerprintFences.set(agentId, state);
11476
+ }
11477
+ state.attempts += 1;
11478
+ state.lastRuntimeError = message;
11479
+ state.launchId = ap.launchId;
11480
+ state.detail = this.formatRuntimeErrorFingerprintFenceDetail(state);
11481
+ const fenced = state.attempts >= RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD;
11482
+ this.recordDaemonTrace("daemon.agent.runtime_error_fingerprint_fence", {
11483
+ agentId,
11484
+ runtime: ap.config.runtime,
11485
+ model: ap.config.model,
11486
+ launchId: ap.launchId || void 0,
11487
+ fingerprint,
11488
+ attempts: state.attempts,
11489
+ threshold: RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD,
11490
+ fenced,
11491
+ runtime_error_class: buildRuntimeErrorDiagnosticEnvelope(message).spanAttrs.runtime_error_class
11492
+ }, fenced ? "error" : "ok");
11493
+ return fenced ? state : null;
11494
+ }
11495
+ applyRuntimeErrorFingerprintFence(agentId, ap, state) {
11496
+ this.recordDaemonTrace("daemon.agent.runtime_error_fingerprint_fence.tripped", {
11497
+ agentId,
11498
+ runtime: ap.config.runtime,
11499
+ model: ap.config.model,
11500
+ launchId: ap.launchId || void 0,
11501
+ fingerprint: state.fingerprint,
11502
+ attempts: state.attempts,
11503
+ threshold: RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD
11504
+ }, "error");
11505
+ this.idleAgentConfigs.delete(agentId);
11506
+ this.sendAgentStatus(agentId, "inactive", ap.launchId);
11507
+ this.cleanupTerminalRuntimeFailure(agentId, ap, state.detail);
11508
+ }
11375
11509
  scheduleStdinNotification(agentId, ap, delayMs) {
11376
11510
  return ap.notifications.schedule(() => {
11377
11511
  this.sendStdinNotification(agentId);
@@ -11925,6 +12059,18 @@ var AgentProcessManager = class _AgentProcessManager {
11925
12059
  });
11926
12060
  span.end(status);
11927
12061
  }
12062
+ processLifecycleIdentityAttrs(agentId, ap) {
12063
+ const runtimeContext = ap.config.runtimeContext;
12064
+ return {
12065
+ agent_id: agentId,
12066
+ server_id: runtimeContext?.serverId,
12067
+ machine_id: runtimeContext?.machineId,
12068
+ process_instance_id: ap.processInstanceId,
12069
+ runtime: ap.config.runtime,
12070
+ runtime_version: runtimeContext?.daemonVersion,
12071
+ pid: typeof ap.runtime.pid === "number" ? ap.runtime.pid : void 0
12072
+ };
12073
+ }
11928
12074
  startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient = false) {
11929
12075
  return {
11930
12076
  agentId,
@@ -12343,6 +12489,7 @@ var AgentProcessManager = class _AgentProcessManager {
12343
12489
  let prompt;
12344
12490
  let promptSource;
12345
12491
  let wakeMessageDeliveredAsInboxUpdate = false;
12492
+ let startingInboxDeliveredAsInput = false;
12346
12493
  const startingInboxMessages = this.startingInboxes.get(agentId) || [];
12347
12494
  if (runtimeConfig.runtimeProfileControl && !wakeMessage) {
12348
12495
  prompt = driver.supportsNativeStandingPrompt ? NATIVE_STANDING_PROMPT_STARTUP_INPUT : formatRuntimeProfileControlStartupInput(runtimeConfig.runtimeProfileControl, driver);
@@ -12383,6 +12530,10 @@ You also have unread messages in other channels:`;
12383
12530
  Use the inbox/read commands at a natural breakpoint if you choose to inspect those targets.`;
12384
12531
  }
12385
12532
  }
12533
+ } else if (startingInboxMessages.length > 0) {
12534
+ startingInboxDeliveredAsInput = true;
12535
+ prompt = this.formatInboxUpdateRuntimeInput(startingInboxMessages, driver);
12536
+ promptSource = "starting_inbox_update";
12386
12537
  } else if (isResume && unreadSummary && Object.keys(unreadSummary).length > 0) {
12387
12538
  prompt = `You have unread messages from while you were offline:`;
12388
12539
  for (const [ch, count] of Object.entries(unreadSummary)) {
@@ -12459,6 +12610,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12459
12610
  };
12460
12611
  const initialSessionId = this.initialAgentProcessSessionId(driver, liveProcessConfig);
12461
12612
  const restartSessionId = initialSessionId || (driver.requiresSessionInitForDelivery ? liveProcessConfig.sessionId || null : null);
12613
+ const processInstanceId = randomUUID5();
12462
12614
  agentProcess = {
12463
12615
  runtime,
12464
12616
  driver,
@@ -12488,6 +12640,8 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12488
12640
  runtimeErrorSinceProgress: false,
12489
12641
  runtimeErrorDeliveryBackoff: createRuntimeErrorDeliveryBackoffState(),
12490
12642
  spawnError: null,
12643
+ processInstanceId,
12644
+ spawnedAtMs: Date.now(),
12491
12645
  exitCode: null,
12492
12646
  exitSignal: null,
12493
12647
  stalledRecoverySigtermTimer: null,
@@ -12512,6 +12666,10 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12512
12666
  }
12513
12667
  if (wakeMessageDeliveredAsInboxUpdate) {
12514
12668
  this.recordInboxUpdateProjection(agentId, agentProcess, agentProcess.inbox, "spawn_wake_inbox_update", "wake", prompt);
12669
+ } else if (startingInboxDeliveredAsInput) {
12670
+ this.recordInboxUpdateProjection(agentId, agentProcess, startingInboxMessages, "spawn_starting_inbox_update", "wake", prompt);
12671
+ this.consumeVisibleMessages(agentId, { messages: startingInboxMessages, source: "spawn_starting_inbox_update" });
12672
+ this.ackInjectedRuntimeProfileMessages(agentId, startingInboxMessages, agentProcess.launchId);
12515
12673
  } else if (wakeMessage && !wakeMessageTransient) {
12516
12674
  this.consumeVisibleMessages(agentId, { messages: [wakeMessage], source: "spawn_wake_message" });
12517
12675
  this.ackInjectedRuntimeProfileMessages(agentId, [wakeMessage], agentProcess.launchId);
@@ -12574,6 +12732,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12574
12732
  current.exitCode = code;
12575
12733
  current.exitSignal = signal;
12576
12734
  }
12735
+ const exitTraceAttrs = this.runtimeExitTraceAttrs.get(runtime);
12577
12736
  this.recordDaemonTrace("daemon.agent.process.exited", {
12578
12737
  agentId,
12579
12738
  launchId: current?.launchId || void 0,
@@ -12585,8 +12744,22 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12585
12744
  runtime_trace_active: Boolean(current?.runtimeTraceSpan),
12586
12745
  inbox_count: current?.inbox.length ?? 0,
12587
12746
  pending_notification_count: current?.notifications.pendingCount ?? 0,
12588
- ...this.runtimeExitTraceAttrs.get(runtime)
12747
+ ...exitTraceAttrs
12589
12748
  });
12749
+ if (agentProcess) {
12750
+ const stopSource = exitTraceAttrs?.stop_source;
12751
+ const exitCause = stopSource?.includes("stall") ? "stall_kill" : stopSource === "disconnect" ? "disconnect_kill" : stopSource === "daemon_exit" ? "parent_exit" : stopSource === "explicit_request" || stopSource === "daemon_internal" || code === 0 ? "expected_terminate" : "crash";
12752
+ const staleForMs = agentProcess.runtimeProgress.ageMs();
12753
+ this.recordDaemonTrace("daemon.runtime.process.exit", {
12754
+ ...this.processLifecycleIdentityAttrs(agentId, agentProcess),
12755
+ exit_code: code,
12756
+ exit_signal: signal,
12757
+ cause: exitCause,
12758
+ last_event_kind: agentProcess.lastActivity || void 0,
12759
+ last_event_age_ms_bucket: bucketMs(staleForMs),
12760
+ uptime_ms_bucket: bucketMs(Date.now() - agentProcess.spawnedAtMs)
12761
+ }, code === 0 ? "ok" : "error");
12762
+ }
12590
12763
  logger.info(`[Agent ${agentId}] Process exited with code ${code}${signal ? ` (signal ${signal})` : ""}`);
12591
12764
  });
12592
12765
  runtime.on("close", ({ code, signal }) => {
@@ -12650,6 +12823,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12650
12823
  text: `Stored ${runtimeLabel} session ${staleSessionId} ${reasonText}. Falling back to a cold start; earlier runtime context may not be restored.`
12651
12824
  }]
12652
12825
  );
12826
+ this.pendingSpawnCause.set(agentId, "restart_crash");
12653
12827
  this.startAgent(
12654
12828
  agentId,
12655
12829
  restartConfig,
@@ -12659,6 +12833,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12659
12833
  ap.launchId || void 0
12660
12834
  ).catch((err) => {
12661
12835
  logger.error(`[Agent ${agentId}] Cold start recovery failed`, err);
12836
+ this.pendingSpawnCause.delete(agentId);
12662
12837
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
12663
12838
  this.broadcastActivity(agentId, "offline", `Crashed (${summary})`, [], ap.launchId);
12664
12839
  });
@@ -12685,6 +12860,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12685
12860
  });
12686
12861
  this.broadcastActivity(agentId, "working", "Message received");
12687
12862
  this.idleAgentConfigs.delete(agentId);
12863
+ if (expectedTerminationReason === "stalled_recovery") {
12864
+ this.pendingSpawnCause.set(agentId, "restart_stall");
12865
+ }
12688
12866
  this.startAgent(agentId, nextConfig, queuedWakeMessage, unreadSummary2, void 0, ap.launchId || void 0).catch((err) => {
12689
12867
  logger.error(`[Agent ${agentId}] Failed to continue with queued message`, err);
12690
12868
  if (this.reportRunnerCredentialMintFailure(agentId, err, ap.launchId, "queued_continuation")) {
@@ -12769,6 +12947,16 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
12769
12947
  new_session: false,
12770
12948
  process_pid_present: typeof runtime.pid === "number"
12771
12949
  });
12950
+ const pendingCause = this.pendingSpawnCause.get(agentId);
12951
+ this.pendingSpawnCause.delete(agentId);
12952
+ const startCause = pendingCause ?? (isResume ? "session_resume" : wakeMessage ? "wake_message" : "explicit_start");
12953
+ this.recordDaemonTrace("daemon.runtime.process.spawn", {
12954
+ ...this.processLifecycleIdentityAttrs(agentId, agentProcess),
12955
+ start_cause: startCause,
12956
+ credential_type: effectiveConfig.agentCredentialKey ? "managed_runner" : "legacy_machine",
12957
+ session_id_present: Boolean(agentProcess.sessionId),
12958
+ launch_id_present: Boolean(agentProcess.launchId)
12959
+ });
12772
12960
  this.sendAgentStatus(agentId, "active", agentProcess.launchId);
12773
12961
  if (pendingStartRebind && agentProcess.sessionId) {
12774
12962
  this.sendToServer({ type: "agent:session", agentId, sessionId: agentProcess.sessionId, launchId: agentProcess.launchId || void 0 });
@@ -13123,6 +13311,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13123
13311
  this.agentVisibleBoundaries.delete(agentId);
13124
13312
  this.agentVisibleMessageIds.delete(agentId);
13125
13313
  this.resetSpawnFailBackoff(agentId);
13314
+ this.resetRuntimeErrorFingerprintFence(agentId, "explicit_stop", ap);
13126
13315
  }
13127
13316
  this.runtimeExitTraceAttrs.set(ap.runtime, {
13128
13317
  stop_source: silent ? "daemon_internal" : "explicit_request",
@@ -13307,6 +13496,19 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
13307
13496
  return false;
13308
13497
  });
13309
13498
  }
13499
+ if (!transientDelivery && (this.queuedAgentStarts.has(agentId) || this.agentsStarting.has(agentId))) {
13500
+ const pending = this.startingInboxes.get(agentId) || [];
13501
+ pending.push(message);
13502
+ this.startingInboxes.set(agentId, pending);
13503
+ this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
13504
+ outcome: this.agentsStarting.has(agentId) ? "queued_for_starting_process" : "queued_for_queued_start",
13505
+ accepted: true,
13506
+ process_present: false,
13507
+ cached_idle_config_present: false,
13508
+ starting_inbox_count: pending.length
13509
+ }));
13510
+ return true;
13511
+ }
13310
13512
  logger.warn(`[Agent ${agentId}] Delivery received but no running process or cached idle config exists`);
13311
13513
  if (transientDelivery) {
13312
13514
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
@@ -14307,26 +14509,18 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14307
14509
  if (!hasToolStart) {
14308
14510
  entries.push({ kind: "status", activity, detail });
14309
14511
  }
14512
+ const launchId = launchIdOverride || ap?.launchId || void 0;
14513
+ const clientSeq = ap ? this.nextActivityClientSeq(agentId) : void 0;
14310
14514
  this.sendToServer({
14311
14515
  type: "agent:activity",
14312
14516
  agentId,
14313
14517
  activity,
14314
14518
  detail,
14315
14519
  entries,
14316
- launchId: launchIdOverride || ap?.launchId || void 0,
14317
- clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
14318
- });
14319
- this.recordDaemonTrace("daemon.agent.activity.produced", {
14320
- agentId,
14321
- activity,
14322
- detail_present: Boolean(detail),
14323
- entry_kinds: entries.map((e) => e.kind).join(","),
14324
- ap_present: Boolean(ap),
14325
- launch_id_present: Boolean(launchIdOverride || ap?.launchId),
14326
- client_seq_present: ap ? true : false,
14327
- session_id_present: Boolean(ap?.sessionId),
14328
- runtime: ap?.config.runtime
14520
+ launchId,
14521
+ clientSeq
14329
14522
  });
14523
+ this.recordActivityProducedTrace(agentId, activity, detail, entries, ap, launchId, clientSeq);
14330
14524
  if (ap) {
14331
14525
  ap.lastActivity = activity;
14332
14526
  ap.lastActivityDetail = detail;
@@ -14337,14 +14531,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14337
14531
  this.recordRuntimeTraceEvent(agentId, ap, "activity.heartbeat.sent", {
14338
14532
  activity: ap.lastActivity
14339
14533
  });
14534
+ const heartbeatLaunchId = launchIdOverride || ap.launchId || void 0;
14535
+ const heartbeatClientSeq = this.nextActivityClientSeq(agentId);
14340
14536
  this.sendToServer({
14341
14537
  type: "agent:activity",
14342
14538
  agentId,
14343
14539
  activity: ap.lastActivity,
14344
14540
  detail: ap.lastActivityDetail,
14345
- launchId: launchIdOverride || ap.launchId || void 0,
14346
- clientSeq: this.nextActivityClientSeq(agentId)
14541
+ launchId: heartbeatLaunchId,
14542
+ clientSeq: heartbeatClientSeq
14347
14543
  });
14544
+ this.recordActivityProducedTrace(agentId, ap.lastActivity, ap.lastActivityDetail, [], ap, heartbeatLaunchId, heartbeatClientSeq);
14348
14545
  }, ACTIVITY_HEARTBEAT_MS);
14349
14546
  }
14350
14547
  } else {
@@ -14355,6 +14552,29 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14355
14552
  }
14356
14553
  }
14357
14554
  }
14555
+ recordActivityProducedTrace(agentId, activity, detail, entries, ap, launchId, clientSeq) {
14556
+ const runtimeContext = ap?.config.runtimeContext;
14557
+ this.recordDaemonTrace("daemon.agent.activity.produced", {
14558
+ agentId,
14559
+ agent_id: agentId,
14560
+ server_id: runtimeContext?.serverId,
14561
+ machine_id: runtimeContext?.machineId,
14562
+ process_instance_id: ap?.processInstanceId,
14563
+ activity,
14564
+ detail_present: Boolean(detail),
14565
+ entry_kinds: entries.map((e) => e.kind).join(","),
14566
+ ap_present: Boolean(ap),
14567
+ launchId,
14568
+ launch_id: launchId,
14569
+ launch_id_present: Boolean(launchId),
14570
+ clientSeq,
14571
+ client_seq: clientSeq,
14572
+ client_seq_present: typeof clientSeq === "number",
14573
+ correlation_id: `agent:${agentId}:daemonActivity:${launchId ?? "legacy"}:${clientSeq ?? "unsequenced"}`,
14574
+ session_id_present: Boolean(ap?.sessionId),
14575
+ runtime: ap?.config.runtime
14576
+ });
14577
+ }
14358
14578
  /**
14359
14579
  * Respond to a server-issued `agent:activity_probe`. Echoes the
14360
14580
  * agent's current `lastActivity` back through the existing
@@ -14380,15 +14600,18 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14380
14600
  const ap = this.agents.get(agentId);
14381
14601
  const activity = ap?.lastActivity || "offline";
14382
14602
  const detail = ap?.lastActivityDetail || (ap ? "" : "Agent not running");
14603
+ const launchId = ap?.launchId || void 0;
14604
+ const clientSeq = ap ? this.nextActivityClientSeq(agentId) : void 0;
14383
14605
  this.sendToServer({
14384
14606
  type: "agent:activity",
14385
14607
  agentId,
14386
14608
  activity,
14387
14609
  detail,
14388
- launchId: ap?.launchId || void 0,
14610
+ launchId,
14389
14611
  probeId,
14390
- clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
14612
+ clientSeq
14391
14613
  });
14614
+ this.recordActivityProducedTrace(agentId, activity, detail, [], ap, launchId, clientSeq);
14392
14615
  }
14393
14616
  flushPendingTrajectory(agentId) {
14394
14617
  const ap = this.agents.get(agentId);
@@ -14488,6 +14711,12 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14488
14711
  );
14489
14712
  try {
14490
14713
  void runtimeAtSignal.stop({ signal: "SIGKILL", reason: "stalled_recovery_sigterm_timeout" });
14714
+ this.recordDaemonTrace("daemon.runtime.stall.recovery_action", {
14715
+ ...this.processLifecycleIdentityAttrs(agentId, current),
14716
+ action: "sigkill_escalation",
14717
+ outcome: "initiated",
14718
+ delay_ms_bucket: bucketMs(timeoutMs)
14719
+ }, "error");
14491
14720
  } catch (err) {
14492
14721
  const reason = err instanceof Error ? err.message : String(err);
14493
14722
  this.recordDaemonTrace("daemon.agent.stalled_recovery.sigkill_failed", {
@@ -14497,6 +14726,16 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
14497
14726
  model: current.config.model,
14498
14727
  reason
14499
14728
  }, "error");
14729
+ this.recordDaemonTrace("daemon.runtime.stall.recovery_action", {
14730
+ ...this.processLifecycleIdentityAttrs(agentId, current),
14731
+ action: "sigkill_escalation",
14732
+ outcome: "kill_failed",
14733
+ delay_ms_bucket: bucketMs(timeoutMs),
14734
+ kill_signal_sequence: "SIGTERM,SIGKILL",
14735
+ kill_attempts: 2,
14736
+ subprocess_os_state: probeSubprocessOsState(current.runtime.pid),
14737
+ daemon_child_tracking_present: this.agents.has(agentId)
14738
+ }, "error");
14500
14739
  logger.warn(`[Agent ${agentId}] Failed to force kill stalled ${runtimeLabel} process: ${reason}`);
14501
14740
  }
14502
14741
  }, timeoutMs);
@@ -15036,6 +15275,23 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15036
15275
  ...runtimeTraceCounterAttrs(ap),
15037
15276
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
15038
15277
  });
15278
+ let subprocessPidAlive;
15279
+ if (typeof ap.runtime.pid === "number") {
15280
+ try {
15281
+ process.kill(ap.runtime.pid, 0);
15282
+ subprocessPidAlive = true;
15283
+ } catch {
15284
+ subprocessPidAlive = false;
15285
+ }
15286
+ }
15287
+ this.recordDaemonTrace("daemon.runtime.stall.detected", {
15288
+ ...this.processLifecycleIdentityAttrs(agentId, ap),
15289
+ last_event_kind: ap.lastActivity || void 0,
15290
+ last_event_age_ms_bucket: bucketMs(staleForMs),
15291
+ subprocess_pid_alive: subprocessPidAlive,
15292
+ subprocess_socket_alive: !ap.runtime.closed,
15293
+ daemon_connected_to_server: this.serverConnected()
15294
+ }, "error");
15039
15295
  this.broadcastActivity(agentId, "error", diagnostic.detail);
15040
15296
  return true;
15041
15297
  }
@@ -15087,9 +15343,25 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15087
15343
  this.runtimeExitTraceAttrs.set(ap.runtime, projection.processExitAttrs);
15088
15344
  this.startStalledRecoverySigtermWatchdog(agentId, ap, runtimeLabel, ap.inbox.length, staleForMs);
15089
15345
  void ap.runtime.stop({ signal: "SIGTERM", reason: projection.runtimeStopReason });
15346
+ this.recordDaemonTrace("daemon.runtime.stall.recovery_action", {
15347
+ ...this.processLifecycleIdentityAttrs(agentId, ap),
15348
+ action: "terminate_for_restart",
15349
+ outcome: "initiated",
15350
+ delay_ms_bucket: bucketMs(staleForMs)
15351
+ });
15090
15352
  } catch (err) {
15091
15353
  this.clearStalledRecoverySigtermWatchdog(ap);
15092
15354
  const reason = err instanceof Error ? err.message : String(err);
15355
+ this.recordDaemonTrace("daemon.runtime.stall.recovery_action", {
15356
+ ...this.processLifecycleIdentityAttrs(agentId, ap),
15357
+ action: "terminate_for_restart",
15358
+ outcome: "kill_failed",
15359
+ delay_ms_bucket: bucketMs(staleForMs),
15360
+ kill_signal_sequence: "SIGTERM",
15361
+ kill_attempts: 1,
15362
+ subprocess_os_state: probeSubprocessOsState(ap.runtime.pid),
15363
+ daemon_child_tracking_present: this.agents.has(agentId)
15364
+ }, "error");
15093
15365
  logger.warn(`[Agent ${agentId}] Failed to terminate stalled ${runtimeLabel} process: ${reason}`);
15094
15366
  return false;
15095
15367
  }
@@ -15168,6 +15440,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15168
15440
  return;
15169
15441
  }
15170
15442
  this.noteRuntimeProgress(ap, event.kind);
15443
+ if (this.runtimeErrorFingerprintFenceResetEvent(event.kind)) {
15444
+ this.resetRuntimeErrorFingerprintFence(agentId, `runtime_progress:${event.kind}`, ap);
15445
+ }
15171
15446
  recordProgressObservedAfterStall();
15172
15447
  } else if (event.kind !== "internal_progress") {
15173
15448
  this.recordDaemonTrace("daemon.agent.event.received_without_process", {
@@ -15360,6 +15635,7 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15360
15635
  let visibleErrorMessage = event.message;
15361
15636
  if (ap) {
15362
15637
  const runtimeErrorDiagnostics = buildRuntimeErrorDiagnosticEnvelope(event.message);
15638
+ const runtimeErrorFingerprint = typeof runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint === "string" ? runtimeErrorDiagnostics.spanAttrs.runtime_error_fingerprint : null;
15363
15639
  if (runtimeErrorDiagnostics.spanAttrs.runtime_error_action_required === true) {
15364
15640
  visibleErrorMessage = formatRuntimeLoginRequiredMessage(ap.driver.id);
15365
15641
  }
@@ -15376,6 +15652,17 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15376
15652
  terminalWakeable: Boolean(ap.driver.supportsStdinNotification && terminalFailure && !terminalFailure.actionRequired)
15377
15653
  });
15378
15654
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "error" });
15655
+ const fingerprintFence = this.noteRuntimeErrorFingerprintFence(
15656
+ agentId,
15657
+ ap,
15658
+ event.message,
15659
+ runtimeErrorFingerprint,
15660
+ terminalFailure,
15661
+ stickyTerminalFailure
15662
+ );
15663
+ if (fingerprintFence) {
15664
+ visibleErrorMessage = fingerprintFence.detail;
15665
+ }
15379
15666
  this.noteRuntimeErrorDeliveryBackoff(agentId, ap, event.message, terminalFailure, stickyTerminalFailure, backoffReasonOverride);
15380
15667
  if (reduction.shouldDisableToolBoundaryFlush) {
15381
15668
  this.recordGatedSteeringEvent(agentId, ap, "disabled", {
@@ -15396,7 +15683,9 @@ Use ${communicationCommand("read_history")} to catch up on the channels listed a
15396
15683
  ...runtimeTraceCounterAttrs(ap),
15397
15684
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_error")
15398
15685
  });
15399
- if (ap.driver.supportsStdinNotification && terminalFailure) {
15686
+ if (fingerprintFence) {
15687
+ this.applyRuntimeErrorFingerprintFence(agentId, ap, fingerprintFence);
15688
+ } else if (ap.driver.supportsStdinNotification && terminalFailure) {
15400
15689
  if (terminalFailure.actionRequired) {
15401
15690
  logger.warn(`[Agent ${agentId}] ${ap.driver.id} auth requires user action; terminating runtime process`);
15402
15691
  try {
@@ -16430,7 +16719,13 @@ var DIAGNOSTIC_ID_ATTRS = /* @__PURE__ */ new Set([
16430
16719
  "bundleId",
16431
16720
  "deliveryId",
16432
16721
  "deliveryCorrelationId",
16433
- "delivery_correlation_id"
16722
+ "delivery_correlation_id",
16723
+ "agent_id",
16724
+ "server_id",
16725
+ "machine_id",
16726
+ "process_instance_id",
16727
+ "launch_id",
16728
+ "correlation_id"
16434
16729
  ]);
16435
16730
  var DIAGNOSTIC_ERROR_ATTRS = /* @__PURE__ */ new Set([
16436
16731
  "runtime_error_class",
@@ -16680,6 +16975,21 @@ var TRACE_UPLOAD_SCOPE = "daemon-trace-bundle:create";
16680
16975
  var DEFAULT_UPLOAD_INTERVAL_MS = 5 * 60 * 1e3;
16681
16976
  var DEFAULT_MIN_FILE_AGE_MS = 60 * 1e3;
16682
16977
  var DEFAULT_MAX_FILES_PER_RUN = 4;
16978
+ var ALLOWED_PRODUCER_DEPLOYMENT_ENVIRONMENTS = /* @__PURE__ */ new Set([
16979
+ "production",
16980
+ "staging",
16981
+ "dev",
16982
+ "test",
16983
+ "slockdev"
16984
+ ]);
16985
+ function readProducerDeploymentEnvironment() {
16986
+ const raw = process.env.SLOCK_DAEMON_DEPLOYMENT_ENV;
16987
+ if (!raw) return void 0;
16988
+ const trimmed = raw.trim();
16989
+ if (!trimmed) return void 0;
16990
+ if (!ALLOWED_PRODUCER_DEPLOYMENT_ENVIRONMENTS.has(trimmed)) return void 0;
16991
+ return trimmed;
16992
+ }
16683
16993
  var DaemonTraceBundleUploader = class {
16684
16994
  options;
16685
16995
  jitter;
@@ -16798,7 +17108,11 @@ var DaemonTraceBundleUploader = class {
16798
17108
  attestationMetadata: {
16799
17109
  bundleId,
16800
17110
  bundleSha256,
16801
- bundleSizeBytes: gzipped.byteLength
17111
+ bundleSizeBytes: gzipped.byteLength,
17112
+ ...(() => {
17113
+ const env = readProducerDeploymentEnvironment();
17114
+ return env ? { deploymentEnvironment: env } : {};
17115
+ })()
16802
17116
  },
16803
17117
  createBody: {
16804
17118
  bundleSha256,
@@ -16891,9 +17205,10 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
16891
17205
  eventAttrs: {
16892
17206
  "daemon.receive": ["seq", "deliveryId"],
16893
17207
  "daemon.deliver_to_agent_manager": ["accepted"],
17208
+ "daemon.delivery.buffered_for_start": ["pending_count"],
16894
17209
  "daemon.ack.sent": ["seq"]
16895
17210
  },
16896
- endAttrs: ["outcome", "ackSeq", "deliveryId", "error_class"]
17211
+ endAttrs: ["outcome", "ackSeq", "deliveryId", "error_class", "pending_count"]
16897
17212
  },
16898
17213
  "daemon.runtime_profile.control.received": {
16899
17214
  spanAttrs: ["agentId", "control_kind", "key_present", "launchId"],
@@ -16930,7 +17245,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
16930
17245
  spanAttrs: ["running_agents_count", "idle_agents_count"]
16931
17246
  },
16932
17247
  "daemon.agent.activity.produced": {
16933
- spanAttrs: ["agentId", "activity", "detail_present", "entry_kinds", "ap_present", "launch_id_present", "client_seq_present", "session_id_present", "runtime"]
17248
+ spanAttrs: ["agentId", "agent_id", "server_id", "machine_id", "activity", "detail_present", "entry_kinds", "ap_present", "launchId", "launch_id", "launch_id_present", "clientSeq", "client_seq", "client_seq_present", "correlation_id", "session_id_present", "runtime"]
16934
17249
  },
16935
17250
  "daemon.agent.activity.skipped": {
16936
17251
  spanAttrs: ["agentId", "event_kind", "reason", "text_length"]
@@ -17014,7 +17329,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
17014
17329
  }
17015
17330
  async function runBundledSlockCli(argv) {
17016
17331
  process.argv = [process.execPath, "slock", ...argv];
17017
- await import("./dist-KC6QHBPD.js");
17332
+ await import("./dist-S7UP42L2.js");
17018
17333
  }
17019
17334
  function detectRuntimes(tracer = noopTracer) {
17020
17335
  const ids = [];
@@ -17177,6 +17492,8 @@ var DaemonCore = class {
17177
17492
  observedMachineId = null;
17178
17493
  localTraceSink = null;
17179
17494
  traceBundleUploader = null;
17495
+ coreStartingAgentIds = /* @__PURE__ */ new Set();
17496
+ coreStartPendingDeliveries = /* @__PURE__ */ new Map();
17180
17497
  constructor(options) {
17181
17498
  this.options = options;
17182
17499
  this.daemonVersion = options.daemonVersion ?? readDaemonVersion();
@@ -17202,7 +17519,8 @@ var DaemonCore = class {
17202
17519
  tracer: this.tracer,
17203
17520
  daemonVersion: this.daemonVersion,
17204
17521
  computerVersion: this.computerVersion,
17205
- workerUrl: traceUploadDisabled ? void 0 : process.env.SLOCK_DAEMON_TRACE_UPLOAD_URL || DEFAULT_TRACE_UPLOAD_URL
17522
+ workerUrl: traceUploadDisabled ? void 0 : process.env.SLOCK_DAEMON_TRACE_UPLOAD_URL || DEFAULT_TRACE_UPLOAD_URL,
17523
+ serverConnected: () => connection?.connected ?? false
17206
17524
  };
17207
17525
  this.agentManager = options.agentManagerFactory ? options.agentManagerFactory((msg) => connection.send(msg), options.apiKey, agentManagerOptions) : new AgentProcessManager((msg) => connection.send(msg), options.apiKey, agentManagerOptions);
17208
17526
  const connectionFactory = options.connectionFactory ?? ((connOptions) => new DaemonConnection(connOptions));
@@ -17453,19 +17771,63 @@ var DaemonCore = class {
17453
17771
  }
17454
17772
  );
17455
17773
  }
17774
+ sendDeliveryAck(msg, traceparent) {
17775
+ const ackSeq = msg.seq > 0 ? msg.seq : msg.message.seq ?? 0;
17776
+ const ack = {
17777
+ type: "agent:deliver:ack",
17778
+ agentId: msg.agentId,
17779
+ seq: ackSeq,
17780
+ deliveryId: msg.deliveryId,
17781
+ ...traceparent ? { traceparent } : {}
17782
+ };
17783
+ this.connection.send(ack);
17784
+ }
17456
17785
  async startAgentFromMessage(msg) {
17457
- this.observeRuntimeContext(msg.config);
17458
- const agentCredential = await this.mintRunnerCredential(msg.agentId, msg.config);
17459
- const config = { ...msg.config, agentCredentialKey: agentCredential.apiKey, agentCredentialId: agentCredential.credentialId };
17460
- await this.agentManager.startAgent(
17461
- msg.agentId,
17462
- config,
17463
- msg.wakeMessage,
17464
- msg.unreadSummary,
17465
- msg.resumePrompt,
17466
- msg.launchId,
17467
- msg.wakeMessageTransient ?? false
17468
- );
17786
+ this.coreStartingAgentIds.add(msg.agentId);
17787
+ let wakeDeliveryAck = null;
17788
+ let replayDeliveries = [];
17789
+ try {
17790
+ this.observeRuntimeContext(msg.config);
17791
+ const agentCredential = await this.mintRunnerCredential(msg.agentId, msg.config);
17792
+ const config = { ...msg.config, agentCredentialKey: agentCredential.apiKey, agentCredentialId: agentCredential.credentialId };
17793
+ const pendingDeliveries = this.coreStartPendingDeliveries.get(msg.agentId) || [];
17794
+ this.coreStartPendingDeliveries.delete(msg.agentId);
17795
+ let wakeMessage = msg.wakeMessage;
17796
+ let wakeMessageTransient = msg.wakeMessageTransient ?? false;
17797
+ replayDeliveries = [...pendingDeliveries];
17798
+ if (!wakeMessage) {
17799
+ const wakeIndex = replayDeliveries.findIndex((delivery) => delivery.transient !== true);
17800
+ if (wakeIndex >= 0) {
17801
+ const [wakeDelivery] = replayDeliveries.splice(wakeIndex, 1);
17802
+ if (wakeDelivery) {
17803
+ wakeDeliveryAck = wakeDelivery;
17804
+ wakeMessage = wakeDelivery.message;
17805
+ wakeMessageTransient = wakeDelivery.transient ?? false;
17806
+ }
17807
+ }
17808
+ }
17809
+ await this.agentManager.startAgent(
17810
+ msg.agentId,
17811
+ config,
17812
+ wakeMessage,
17813
+ msg.unreadSummary,
17814
+ msg.resumePrompt,
17815
+ msg.launchId,
17816
+ wakeMessageTransient
17817
+ );
17818
+ this.coreStartingAgentIds.delete(msg.agentId);
17819
+ if (wakeDeliveryAck) {
17820
+ this.sendDeliveryAck(wakeDeliveryAck);
17821
+ }
17822
+ for (const delivery of replayDeliveries) {
17823
+ this.handleMessage(delivery);
17824
+ }
17825
+ } catch (err) {
17826
+ this.coreStartPendingDeliveries.delete(msg.agentId);
17827
+ throw err;
17828
+ } finally {
17829
+ this.coreStartingAgentIds.delete(msg.agentId);
17830
+ }
17469
17831
  }
17470
17832
  handleMessage(msg) {
17471
17833
  const summary = summarizeIncomingMessage(msg);
@@ -17520,6 +17882,14 @@ var DaemonCore = class {
17520
17882
  logger.info(`[Agent ${msg.agentId}] Delivery received (seq=${msg.seq}, from=@${msg.message.sender_name}, target=${formatChannelTarget(msg)})`);
17521
17883
  try {
17522
17884
  span.addEvent("daemon.receive", { seq: msg.seq, deliveryId: msg.deliveryId });
17885
+ if (this.coreStartingAgentIds.has(msg.agentId)) {
17886
+ const pending = this.coreStartPendingDeliveries.get(msg.agentId) || [];
17887
+ pending.push(msg);
17888
+ this.coreStartPendingDeliveries.set(msg.agentId, pending);
17889
+ span.addEvent("daemon.delivery.buffered_for_start", { pending_count: pending.length });
17890
+ span.end("ok", { attrs: { outcome: "buffered-for-start", pending_count: pending.length } });
17891
+ break;
17892
+ }
17523
17893
  const acceptedOrPromise = this.agentManager.deliverMessage(msg.agentId, msg.message, {
17524
17894
  deliveryId: msg.deliveryId,
17525
17895
  transient: msg.transient ?? false
@@ -17532,13 +17902,7 @@ var DaemonCore = class {
17532
17902
  }
17533
17903
  const ackSeq = msg.seq > 0 ? msg.seq : msg.message.seq ?? 0;
17534
17904
  span.addEvent("daemon.ack.sent", { seq: ackSeq });
17535
- this.connection.send({
17536
- type: "agent:deliver:ack",
17537
- agentId: msg.agentId,
17538
- seq: ackSeq,
17539
- traceparent: formatTraceparent(span.context),
17540
- deliveryId: msg.deliveryId
17541
- });
17905
+ this.sendDeliveryAck(msg, formatTraceparent(span.context));
17542
17906
  span.end("ok", { attrs: { outcome: "ack-sent", ackSeq, deliveryId: msg.deliveryId } });
17543
17907
  }, (err) => {
17544
17908
  logger.error(`[Agent ${msg.agentId}] Delivery handling failed`, err);