@botiverse/raft-daemon 0.67.0 → 0.68.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3303,6 +3303,14 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
3303
3303
  "synthetic_repair",
3304
3304
  "slock_action",
3305
3305
  "system_message",
3306
+ // Generic structured runtime-progress heartbeat (Claude --include-partial-messages
3307
+ // stream events / system status) — a "working" liveness signal with no rendered
3308
+ // content. NOT a subagent. (APM 1.6 6a)
3309
+ "runtime_progress",
3310
+ // A subagent (Claude `Agent` tool) lifecycle/activity row, marked from explicit
3311
+ // parent_tool_use_id / task-lifecycle lineage — never inferred from display text.
3312
+ // (APM 1.6 6b)
3313
+ "subagent_activity",
3306
3314
  "other"
3307
3315
  ];
3308
3316
  var isAgentActivityDetailKind = makeIsMember(AGENT_ACTIVITY_DETAIL_KINDS);
@@ -5875,27 +5883,44 @@ function writeProxyFailureResponse(res, failure) {
5875
5883
  res.destroy();
5876
5884
  return;
5877
5885
  }
5878
- res.writeHead(502, { "content-type": "application/json" });
5879
- res.end(JSON.stringify({
5880
- error: "failed to proxy local agent request",
5881
- code: "agent_proxy_failed",
5886
+ const body = {
5887
+ error: failure.responseError ?? "failed to proxy local agent request",
5888
+ code: failure.responseCode ?? "agent_proxy_failed",
5882
5889
  detail: failure.errorMessage
5883
- }));
5890
+ };
5891
+ if (failure.lifecycleInvalidAgentId) body.agent_id = failure.lifecycleInvalidAgentId;
5892
+ if (failure.lifecycleInvalidContext) body.invariant_context = failure.lifecycleInvalidContext;
5893
+ res.writeHead(failure.responseStatusCode ?? 502, { "content-type": "application/json" });
5894
+ res.end(JSON.stringify(body));
5884
5895
  }
5885
5896
  function proxyFailureForError(method, target, err) {
5886
5897
  const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
5887
5898
  const cause = err instanceof Error ? err.cause : void 0;
5899
+ const errorMessage = truncateProxyErrorMessage(err instanceof Error ? err.message : String(err));
5900
+ const lifecycleInvalid = parseAgentNoProcessResidencyInvariant(errorMessage);
5888
5901
  const failure = {
5889
5902
  method,
5890
5903
  pathname: target?.pathname ?? "unknown",
5891
5904
  queryKeys,
5892
5905
  errorName: err instanceof Error ? err.name : typeof err,
5893
- errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
5906
+ errorMessage
5894
5907
  };
5908
+ if (lifecycleInvalid) {
5909
+ failure.responseStatusCode = 409;
5910
+ failure.responseCode = "agent_lifecycle_state_invalid";
5911
+ failure.responseError = "local daemon agent lifecycle state invalid";
5912
+ failure.lifecycleInvalidAgentId = lifecycleInvalid.agentId;
5913
+ failure.lifecycleInvalidContext = lifecycleInvalid.context;
5914
+ }
5895
5915
  const errorCause = describeProxyErrorCause(cause);
5896
5916
  if (errorCause) failure.errorCause = errorCause;
5897
5917
  return failure;
5898
5918
  }
5919
+ function parseAgentNoProcessResidencyInvariant(message) {
5920
+ const match = /^Agent no-process residency invariant violation after ([^:]+): .+ for ([0-9a-f-]{36})\b/i.exec(message);
5921
+ if (!match) return void 0;
5922
+ return { context: match[1], agentId: match[2] };
5923
+ }
5899
5924
  function describeProxyErrorCause(cause) {
5900
5925
  if (!cause) return void 0;
5901
5926
  if (cause instanceof Error) {
@@ -5932,18 +5957,21 @@ function transportNormalizedErrorForHttpStatus(target, status, launchId) {
5932
5957
  };
5933
5958
  }
5934
5959
  function transportNormalizedErrorForError(target, err, launchId) {
5960
+ const localDaemonStateInvalid = parseAgentNoProcessResidencyInvariant(
5961
+ sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err))
5962
+ );
5935
5963
  return {
5936
- normalizedCode: "transport_failure",
5964
+ normalizedCode: localDaemonStateInvalid ? "local_daemon_state_invalid" : "transport_failure",
5937
5965
  routeFamily: routeFamilyForPath(target?.pathname ?? "unknown"),
5938
5966
  responseStarted: false,
5939
- upstreamLayer: target ? upstreamLayerForProxyError(err) : "unknown",
5967
+ upstreamLayer: localDaemonStateInvalid ? "unknown" : target ? upstreamLayerForProxyError(err) : "unknown",
5940
5968
  originalMessage: sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err)),
5941
5969
  launchId,
5942
- targetHostClass: target ? daemonUpstreamTargetHostClass(target) : "custom_server",
5970
+ targetHostClass: localDaemonStateInvalid ? "local_daemon" : target ? daemonUpstreamTargetHostClass(target) : "custom_server",
5943
5971
  // Today the local credential proxy is only used by CLI wrappers. If runtime
5944
5972
  // or daemon-internal callers use it later, plumb the caller identity here.
5945
5973
  downstreamCaller: "cli",
5946
- upstream: "server"
5974
+ upstream: localDaemonStateInvalid ? "local_daemon" : "server"
5947
5975
  };
5948
5976
  }
5949
5977
  function routeFamilyForPath(pathname) {
@@ -6628,6 +6656,28 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6628
6656
  }
6629
6657
 
6630
6658
  // src/drivers/claudeEventNormalizer.ts
6659
+ function extractSubagentLineage(event) {
6660
+ const parentToolUseId = finiteString(event.parent_tool_use_id);
6661
+ const subagentType = finiteString(event.subagent_type);
6662
+ if (!parentToolUseId && !subagentType) return void 0;
6663
+ return {
6664
+ ...parentToolUseId ? { parentToolUseId } : {},
6665
+ ...subagentType ? { subagentType } : {},
6666
+ phase: "active"
6667
+ };
6668
+ }
6669
+ function taskLifecyclePhase(subtype) {
6670
+ switch (subtype) {
6671
+ case "task_started":
6672
+ return "started";
6673
+ case "task_progress":
6674
+ return "progress";
6675
+ case "task_notification":
6676
+ return "notification";
6677
+ default:
6678
+ return null;
6679
+ }
6680
+ }
6631
6681
  function collectResultErrorDetail(message, fallback) {
6632
6682
  const parts = [];
6633
6683
  if (Array.isArray(message.errors)) {
@@ -6801,6 +6851,25 @@ var ClaudeEventNormalizer = class {
6801
6851
  if (event.subtype === "compact_boundary") {
6802
6852
  events.push({ kind: "compaction_finished" });
6803
6853
  }
6854
+ {
6855
+ const phase = typeof event.subtype === "string" ? taskLifecyclePhase(event.subtype) : null;
6856
+ if (phase) {
6857
+ events.push({
6858
+ kind: "subagent_progress",
6859
+ source: "claude_task_lifecycle",
6860
+ phase,
6861
+ ...finiteString(event.task_id) ? { taskId: finiteString(event.task_id) } : {},
6862
+ // `system` task-lifecycle envelopes carry the outer Agent tool id
6863
+ // as `tool_use_id`; belt-and-suspenders fall back to
6864
+ // `parent_tool_use_id` in case a Claude version names it that way
6865
+ // on the envelope (confirmed field name = tool_use_id, @Huarong).
6866
+ ...finiteString(event.tool_use_id) ?? finiteString(event.parent_tool_use_id) ? { parentToolUseId: finiteString(event.tool_use_id) ?? finiteString(event.parent_tool_use_id) } : {},
6867
+ ...finiteString(event.subagent_type) ? { subagentType: finiteString(event.subagent_type) } : {},
6868
+ ...finiteString(event.last_tool_name) ? { lastToolName: finiteString(event.last_tool_name) } : {},
6869
+ payloadBytes: Buffer.byteLength(line, "utf8")
6870
+ });
6871
+ }
6872
+ }
6804
6873
  break;
6805
6874
  case "stream_event":
6806
6875
  events.push({
@@ -6812,19 +6881,21 @@ var ClaudeEventNormalizer = class {
6812
6881
  break;
6813
6882
  case "assistant": {
6814
6883
  const content = event.message?.content;
6884
+ const subagent = extractSubagentLineage(event);
6885
+ const withLineage = subagent ? { subagent } : {};
6815
6886
  if (Array.isArray(content)) {
6816
6887
  const hasToolUse = content.some((block) => block?.type === "tool_use");
6817
6888
  for (const block of content) {
6818
6889
  if (block.type === "thinking" && block.thinking) {
6819
- events.push({ kind: "thinking", text: block.thinking });
6890
+ events.push({ kind: "thinking", text: block.thinking, ...withLineage });
6820
6891
  } else if (block.type === "text" && block.text) {
6821
6892
  if (isProviderApiFailureText(block.text, hasToolUse)) {
6822
6893
  events.push({ kind: "error", message: block.text });
6823
6894
  } else {
6824
- events.push({ kind: "text", text: block.text });
6895
+ events.push({ kind: "text", text: block.text, ...withLineage });
6825
6896
  }
6826
6897
  } else if (block.type === "tool_use") {
6827
- events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
6898
+ events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input, ...withLineage });
6828
6899
  }
6829
6900
  }
6830
6901
  }
@@ -6832,10 +6903,12 @@ var ClaudeEventNormalizer = class {
6832
6903
  }
6833
6904
  case "user": {
6834
6905
  const content = event.message?.content;
6906
+ const subagent = extractSubagentLineage(event);
6907
+ const withLineage = subagent ? { subagent } : {};
6835
6908
  if (Array.isArray(content)) {
6836
6909
  for (const block of content) {
6837
6910
  if (block.type === "tool_result") {
6838
- events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
6911
+ events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result", ...withLineage });
6839
6912
  }
6840
6913
  }
6841
6914
  }
@@ -10465,6 +10538,7 @@ var OpenCodeDriver = class {
10465
10538
  };
10466
10539
  supportsStdinNotification = false;
10467
10540
  busyDeliveryMode = "none";
10541
+ supportsNativeStandingPrompt = true;
10468
10542
  terminateProcessOnTurnEnd = true;
10469
10543
  deferSpawnUntilMessage = true;
10470
10544
  shouldDeferWakeMessage(message) {
@@ -12107,6 +12181,18 @@ var AgentLifecycleRecords = class {
12107
12181
  pendingSpawnCauses = /* @__PURE__ */ new Map();
12108
12182
  runtimeErrorFingerprintFences = /* @__PURE__ */ new Map();
12109
12183
  activityClientSeqs = /* @__PURE__ */ new Map();
12184
+ stopEpochs = /* @__PURE__ */ new Map();
12185
+ recordStop(agentId) {
12186
+ const next = (this.stopEpochs.get(agentId) ?? 0) + 1;
12187
+ this.stopEpochs.set(agentId, next);
12188
+ return next;
12189
+ }
12190
+ stopEpoch(agentId) {
12191
+ return this.stopEpochs.get(agentId) ?? 0;
12192
+ }
12193
+ stopEpochChanged(agentId, epoch) {
12194
+ return this.stopEpoch(agentId) !== epoch;
12195
+ }
12110
12196
  nextActivityClientSeq(agentId) {
12111
12197
  const next = (this.activityClientSeqs.get(agentId) ?? 0) + 1;
12112
12198
  this.activityClientSeqs.set(agentId, next);
@@ -13008,6 +13094,36 @@ var WORKSPACE_IMAGE_MIME_BY_EXTENSION = {
13008
13094
  ".png": "image/png",
13009
13095
  ".webp": "image/webp"
13010
13096
  };
13097
+ var WORKSPACE_NEVER_VISIBLE_HIDDEN_NAMES = /* @__PURE__ */ new Set([
13098
+ ".aws",
13099
+ ".gnupg",
13100
+ ".ssh",
13101
+ ".slock",
13102
+ ".slock-runtime"
13103
+ ]);
13104
+ var WORKSPACE_SECRET_FILE_PATTERNS = [
13105
+ /^\.env(?:\.|$)/i,
13106
+ /(?:^|[._-])secret(?:s)?(?:[._-]|$)/i,
13107
+ /(?:^|[._-])credential(?:s)?(?:[._-]|$)/i,
13108
+ /(?:^|[._-])token(?:s)?(?:[._-]|$)/i
13109
+ ];
13110
+ function isWorkspaceNeverVisibleHiddenEntry(name) {
13111
+ return WORKSPACE_NEVER_VISIBLE_HIDDEN_NAMES.has(name) || name.startsWith(".slock-");
13112
+ }
13113
+ function workspacePathParts(relativePath) {
13114
+ return relativePath.split(path14.sep).filter(Boolean);
13115
+ }
13116
+ function isWorkspaceHiddenPath(relativePath) {
13117
+ return workspacePathParts(relativePath).some((part) => part.startsWith("."));
13118
+ }
13119
+ function isWorkspaceNeverVisibleHiddenPath(relativePath) {
13120
+ return workspacePathParts(relativePath).some(isWorkspaceNeverVisibleHiddenEntry);
13121
+ }
13122
+ function isWorkspaceSecretFilePath(filePath) {
13123
+ return workspacePathParts(filePath).some(
13124
+ (part) => WORKSPACE_SECRET_FILE_PATTERNS.some((pattern) => pattern.test(part))
13125
+ );
13126
+ }
13011
13127
  function readPositiveIntegerEnv(name, fallback) {
13012
13128
  const raw = process.env[name];
13013
13129
  if (!raw) return fallback;
@@ -13102,9 +13218,6 @@ function toLocalTime(iso) {
13102
13218
  const pad = (n) => String(n).padStart(2, "0");
13103
13219
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
13104
13220
  }
13105
- function formatChannelLabel(message) {
13106
- return message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
13107
- }
13108
13221
  function formatMessageTarget(message) {
13109
13222
  return formatAgentMessageVisibleTarget(message);
13110
13223
  }
@@ -13437,15 +13550,6 @@ function formatRuntimeProfileControlStartupInput(control, driver) {
13437
13550
  control.message
13438
13551
  ].join("\n");
13439
13552
  }
13440
- function buildUnreadSummary(messages, excludeChannel) {
13441
- const summary = /* @__PURE__ */ new Map();
13442
- for (const message of messages) {
13443
- const label = formatChannelLabel(message);
13444
- if (excludeChannel && label === excludeChannel) continue;
13445
- summary.set(label, (summary.get(label) || 0) + 1);
13446
- }
13447
- return summary.size > 0 ? Object.fromEntries(summary) : void 0;
13448
- }
13449
13553
  var MAX_TRAJECTORY_TEXT = 2e3;
13450
13554
  var TRAJECTORY_COALESCE_MS = 350;
13451
13555
  var ACTIVITY_HEARTBEAT_MS = 6e4;
@@ -14096,6 +14200,39 @@ function runtimeRecoveryTraceAttrs(event) {
14096
14200
  details_present: Boolean(event.details)
14097
14201
  };
14098
14202
  }
14203
+ function subagentProgressDetail(event) {
14204
+ const role = event.subagentType ? ` (${event.subagentType})` : "";
14205
+ switch (event.phase) {
14206
+ case "started":
14207
+ return `Subagent started${role}`;
14208
+ case "progress":
14209
+ return `Subagent working${role}`;
14210
+ case "notification":
14211
+ return `Subagent update${role}`;
14212
+ default:
14213
+ return `Subagent working${role}`;
14214
+ }
14215
+ }
14216
+ function subagentLineageFromEvent(event) {
14217
+ return {
14218
+ ...event.parentToolUseId ? { parentToolUseId: event.parentToolUseId } : {},
14219
+ ...event.subagentType ? { subagentType: event.subagentType } : {},
14220
+ ...event.taskId ? { taskId: event.taskId } : {},
14221
+ phase: event.phase
14222
+ };
14223
+ }
14224
+ function subagentProgressTraceAttrs(event) {
14225
+ return {
14226
+ kind: event.kind,
14227
+ source: event.source,
14228
+ phase: event.phase,
14229
+ parent_tool_use_id_present: Boolean(event.parentToolUseId),
14230
+ subagent_type_present: Boolean(event.subagentType),
14231
+ task_id_present: Boolean(event.taskId),
14232
+ last_tool_name_present: Boolean(event.lastToolName),
14233
+ payloadBytes: event.payloadBytes
14234
+ };
14235
+ }
14099
14236
  function currentErrorCandidates(ap) {
14100
14237
  return ap.decisionErrorWindow.currentErrorCandidates();
14101
14238
  }
@@ -14465,6 +14602,7 @@ var AgentProcessManager = class _AgentProcessManager {
14465
14602
  this.cliTransportTraceDir = traceDir;
14466
14603
  }
14467
14604
  assertStartPendingDeliveryInvariants(context) {
14605
+ this.repairNonresidentRuntimeErrorFingerprintFences(context);
14468
14606
  const residencySnapshot = this.noProcessResidencySnapshot();
14469
14607
  AgentNoProcessResidency.assertInvariants(context, residencySnapshot);
14470
14608
  this.lifecycleRecords.assertInvariants(context, this.agentLifecycleRecordSnapshot());
@@ -14602,6 +14740,17 @@ var AgentProcessManager = class _AgentProcessManager {
14602
14740
  reset_source: resetSource
14603
14741
  });
14604
14742
  }
14743
+ resetRuntimeErrorFingerprintFenceIfNonresident(agentId, resetSource, ap) {
14744
+ if (this.agents.has(agentId)) return;
14745
+ if (this.lifecycleRecords.getRestartSnapshot(agentId)) return;
14746
+ if (this.lifecycleRecords.getTerminalFailure(agentId)) return;
14747
+ this.resetRuntimeErrorFingerprintFence(agentId, resetSource, ap);
14748
+ }
14749
+ repairNonresidentRuntimeErrorFingerprintFences(context) {
14750
+ for (const agentId of [...this.lifecycleRecords.runtimeErrorFingerprintFenceAgentIds()]) {
14751
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, `invariant_repair:${context}`);
14752
+ }
14753
+ }
14605
14754
  formatRuntimeErrorFingerprintFenceDetail(state) {
14606
14755
  return [
14607
14756
  `Runtime stopped after ${state.attempts} repeated runtime errors with the same fingerprint (${state.fingerprint}).`,
@@ -14769,7 +14918,7 @@ var AgentProcessManager = class _AgentProcessManager {
14769
14918
  this.broadcastActivity(agentId, "working", "Message received", [], void 0, "message_received");
14770
14919
  }
14771
14920
  toolActivityDetailKind(toolName) {
14772
- switch (toolName) {
14921
+ switch (resolveToolSemantic(toolName) ?? toolName) {
14773
14922
  case "bash":
14774
14923
  return "running_command";
14775
14924
  case "check_messages":
@@ -15119,7 +15268,11 @@ var AgentProcessManager = class _AgentProcessManager {
15119
15268
  path: input.pathname,
15120
15269
  query_keys: input.queryKeys,
15121
15270
  error_name: input.errorName,
15122
- error_message: input.errorMessage
15271
+ error_message: input.errorMessage,
15272
+ response_status_code: input.responseStatusCode,
15273
+ response_code: input.responseCode,
15274
+ lifecycle_invalid_agent_id: input.lifecycleInvalidAgentId,
15275
+ lifecycle_invalid_context: input.lifecycleInvalidContext
15123
15276
  }, "error");
15124
15277
  }
15125
15278
  recordAgentProxyTransportNormalizedError(agentId, input) {
@@ -15180,6 +15333,38 @@ var AgentProcessManager = class _AgentProcessManager {
15180
15333
  ...runtimeDiagnosticTraceAttrs(event)
15181
15334
  });
15182
15335
  }
15336
+ /**
15337
+ * APM 1.6 6a — surface a generic structured "working" signal from an
15338
+ * internal_progress liveness tick during a long turn. Only transitions INTO
15339
+ * the runtime-progress state once (idempotent while already in it) so the
15340
+ * high-frequency Claude partial-message stream does not spam a rendered status
15341
+ * line every tick; the activity heartbeat then keeps the signal live. Carries
15342
+ * no raw content — a bounded "Working" detail only. Never marks a subagent.
15343
+ */
15344
+ maybeBroadcastRuntimeProgressActivity(agentId, ap) {
15345
+ const alreadyLive = ap.lastActivityKind === "working" || ap.lastActivityKind === "thinking";
15346
+ if (alreadyLive) return;
15347
+ this.broadcastActivity(agentId, "working", "Working", [], void 0, "runtime_progress");
15348
+ }
15349
+ recordSubagentProgressActivity(agentId, ap, event) {
15350
+ this.flushPendingTrajectory(agentId);
15351
+ this.broadcastActivity(
15352
+ agentId,
15353
+ "working",
15354
+ subagentProgressDetail(event),
15355
+ [],
15356
+ void 0,
15357
+ "subagent_activity",
15358
+ "working",
15359
+ subagentLineageFromEvent(event)
15360
+ );
15361
+ this.recordDaemonTrace("daemon.runtime.subagent.progress", {
15362
+ agentId,
15363
+ launchId: ap.launchId || void 0,
15364
+ runtime: ap.config.runtime,
15365
+ ...subagentProgressTraceAttrs(event)
15366
+ });
15367
+ }
15183
15368
  recordRuntimeRecoveryActivity(agentId, ap, event) {
15184
15369
  this.sendToServer({
15185
15370
  type: "agent:activity",
@@ -15416,7 +15601,10 @@ var AgentProcessManager = class _AgentProcessManager {
15416
15601
  rebindRunningStart(agentId, start, reason) {
15417
15602
  const ap = this.agents.get(agentId);
15418
15603
  if (!ap) {
15419
- this.lifecycleRecords.setPendingStartRebind(agentId, start);
15604
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15605
+ ...start,
15606
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15607
+ });
15420
15608
  return false;
15421
15609
  }
15422
15610
  const previousLaunchId = ap.launchId;
@@ -15466,7 +15654,16 @@ var AgentProcessManager = class _AgentProcessManager {
15466
15654
  ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages),
15467
15655
  reason: "already_starting"
15468
15656
  });
15469
- this.lifecycleRecords.setPendingStartRebind(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages });
15657
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15658
+ config,
15659
+ wakeMessage,
15660
+ unreadSummary,
15661
+ resumePrompt,
15662
+ launchId,
15663
+ wakeMessageTransient,
15664
+ resumeMessages,
15665
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15666
+ });
15470
15667
  logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
15471
15668
  return;
15472
15669
  }
@@ -15544,7 +15741,10 @@ var AgentProcessManager = class _AgentProcessManager {
15544
15741
  if (this.agents.has(item.agentId)) {
15545
15742
  this.rebindRunningStart(item.agentId, item, "already_running_or_starting");
15546
15743
  } else {
15547
- this.lifecycleRecords.setPendingStartRebind(item.agentId, item);
15744
+ this.lifecycleRecords.setPendingStartRebind(item.agentId, {
15745
+ ...item,
15746
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(item.agentId)
15747
+ });
15548
15748
  }
15549
15749
  logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
15550
15750
  item.resolve();
@@ -15642,10 +15842,20 @@ var AgentProcessManager = class _AgentProcessManager {
15642
15842
  ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages),
15643
15843
  reason: "already_starting"
15644
15844
  });
15645
- this.lifecycleRecords.setPendingStartRebind(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages });
15845
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15846
+ config,
15847
+ wakeMessage,
15848
+ unreadSummary,
15849
+ resumePrompt,
15850
+ launchId,
15851
+ wakeMessageTransient,
15852
+ resumeMessages,
15853
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15854
+ });
15646
15855
  logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
15647
15856
  return;
15648
15857
  }
15858
+ let startStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
15649
15859
  this.agentStarts.markStarting(agentId);
15650
15860
  let agentProcess = null;
15651
15861
  let pendingStartRebind;
@@ -15691,6 +15901,7 @@ var AgentProcessManager = class _AgentProcessManager {
15691
15901
  resumePrompt = pendingStartRebind.resumePrompt;
15692
15902
  launchId = pendingStartRebind.launchId || launchId;
15693
15903
  resumeMessages = pendingStartRebind.resumeMessages;
15904
+ startStopEpoch = pendingStartRebind.stopEpochAtRebind ?? startStopEpoch;
15694
15905
  if (pendingStartRebind.wakeMessage) {
15695
15906
  wakeMessage = pendingStartRebind.wakeMessage;
15696
15907
  wakeMessageTransient = pendingStartRebind.wakeMessageTransient === true;
@@ -15834,6 +16045,11 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
15834
16045
  this.agentStarts.clearStarting(agentId);
15835
16046
  this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "defer_until_concrete_message" });
15836
16047
  this.assertStartPendingDeliveryInvariants("defer-empty-start-drain");
16048
+ if (this.lifecycleRecords.stopEpochChanged(agentId, startStopEpoch)) {
16049
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16050
+ logger.info(`[Agent ${agentId}] Deferred ${driver.id} spawn suppressed by stop request`);
16051
+ return;
16052
+ }
15837
16053
  this.lifecycleRecords.setRestartSnapshot(agentId, {
15838
16054
  config: this.buildRestartSafeConfig(runtimeConfig, runtimeConfig.sessionId || null),
15839
16055
  sessionId: runtimeConfig.sessionId || null,
@@ -15919,6 +16135,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
15919
16135
  };
15920
16136
  this.startingInboxes.drainOnSpawn(agentId);
15921
16137
  this.agents.set(agentId, agentProcess);
16138
+ if (this.lifecycleRecords.stopEpochChanged(agentId, startStopEpoch)) {
16139
+ await this.cleanupStoppedRuntimeStart(agentId, agentProcess);
16140
+ return;
16141
+ }
15922
16142
  this.lifecycleRecords.setRestartSnapshot(agentId, {
15923
16143
  config: this.buildRestartSafeConfig(runtimeConfig, restartSessionId),
15924
16144
  sessionId: restartSessionId,
@@ -16108,16 +16328,16 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16108
16328
  return;
16109
16329
  }
16110
16330
  if (processEndedCleanly) {
16331
+ const pendingRestartMessages = ap.inbox.splice(0).filter((message) => !this.isTransientDelivery(message));
16111
16332
  let queuedWakeMessage;
16112
- if (!ap.driver.supportsStdinNotification || expectedTerminationReason === "stalled_recovery") {
16113
- while (ap.inbox.length > 0) {
16114
- const candidate = ap.inbox.shift();
16115
- if (this.shouldDeferWakeMessage(agentId, ap.driver, candidate)) continue;
16116
- queuedWakeMessage = candidate;
16117
- break;
16333
+ const bufferedRestartMessages = [];
16334
+ for (const message of pendingRestartMessages) {
16335
+ if (!queuedWakeMessage && !this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
16336
+ queuedWakeMessage = message;
16337
+ } else {
16338
+ bufferedRestartMessages.push(message);
16118
16339
  }
16119
16340
  }
16120
- const unreadSummary2 = queuedWakeMessage ? buildUnreadSummary(ap.inbox, formatChannelLabel(queuedWakeMessage)) : void 0;
16121
16341
  if (queuedWakeMessage) {
16122
16342
  logger.info(`[Agent ${agentId}] Turn completed; restarting immediately for queued message`);
16123
16343
  const nextConfig = this.buildRestartSafeConfig(ap.config, ap.sessionId);
@@ -16131,7 +16351,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16131
16351
  if (expectedTerminationReason === "stalled_recovery") {
16132
16352
  this.lifecycleRecords.setPendingSpawnCause(agentId, "restart_stall");
16133
16353
  }
16134
- this.startAgent(agentId, nextConfig, queuedWakeMessage, unreadSummary2, void 0, ap.launchId || void 0).catch((err) => {
16354
+ const startPromise = this.startAgent(agentId, nextConfig, queuedWakeMessage, void 0, void 0, ap.launchId || void 0);
16355
+ if (bufferedRestartMessages.length > 0) {
16356
+ this.startingInboxes.bufferMessagesDuringStart(agentId, bufferedRestartMessages);
16357
+ this.assertStartPendingDeliveryInvariants("clean-exit-pending-inbox-transfer");
16358
+ }
16359
+ startPromise.catch((err) => {
16135
16360
  logger.error(`[Agent ${agentId}] Failed to continue with queued message`, err);
16136
16361
  if (this.reportRunnerCredentialMintFailure(agentId, err, ap.launchId, "queued_continuation")) {
16137
16362
  this.lifecycleRecords.setRestartSnapshot(agentId, {
@@ -16187,9 +16412,11 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16187
16412
  logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
16188
16413
  } else if (startupRequestErrorTermination) {
16189
16414
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
16415
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "startup_request_error_cleanup", ap);
16190
16416
  logger.warn(`[Agent ${agentId}] Startup request failure cleanup completed (${reason})`);
16191
16417
  } else {
16192
16418
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
16419
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "nonrecoverable_process_close", ap);
16193
16420
  logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
16194
16421
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
16195
16422
  }
@@ -16278,6 +16505,47 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16278
16505
  throw err;
16279
16506
  }
16280
16507
  }
16508
+ async cleanupStoppedRuntimeStart(agentId, ap) {
16509
+ if (this.agents.get(agentId) !== ap) return;
16510
+ this.agentStarts.clearStarting(agentId);
16511
+ this.lifecycleRecords.deletePendingStartRebind(agentId);
16512
+ this.lifecycleRecords.deletePendingSpawnCause(agentId);
16513
+ this.lifecycleRecords.deleteRestartSnapshot(agentId);
16514
+ this.startingInboxes.cancelStart(agentId);
16515
+ ap.notifications.clearTimer();
16516
+ this.disposeAgentProcessTimers(ap);
16517
+ this.closeRuntimeReadinessTransition(ap, "terminal");
16518
+ this.closeActivationTransition(ap, "terminal");
16519
+ cleanupAgentCredentialProxy(agentId, ap.launchId);
16520
+ this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
16521
+ this.agents.delete(agentId);
16522
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16523
+ this.assertStartPendingDeliveryInvariants("runtime-start-stop-epoch-fence");
16524
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
16525
+ stop_source: "explicit_request",
16526
+ stop_wait_requested: false,
16527
+ stop_silent: false,
16528
+ stop_epoch_fence: true
16529
+ });
16530
+ try {
16531
+ await ap.runtime.stop({ signal: "SIGTERM", reason: "explicit_request" });
16532
+ } catch (err) {
16533
+ const reason = err instanceof Error ? err.message : String(err);
16534
+ logger.warn(`[Agent ${agentId}] Failed to stop runtime suppressed by stop epoch: ${reason}`);
16535
+ }
16536
+ logger.info(`[Agent ${agentId}] Runtime start discarded after stop request`);
16537
+ }
16538
+ suppressFailedRestartAfterStop(agentId, stopEpochAtRestart, source) {
16539
+ if (!this.lifecycleRecords.stopEpochChanged(agentId, stopEpochAtRestart)) return false;
16540
+ this.lifecycleRecords.deleteRestartSnapshot(agentId);
16541
+ this.lifecycleRecords.deletePendingStartRebind(agentId);
16542
+ this.lifecycleRecords.deletePendingSpawnCause(agentId);
16543
+ this.startingInboxes.cancelStart(agentId);
16544
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16545
+ this.assertStartPendingDeliveryInvariants(`${source}-stop-epoch-fence`);
16546
+ logger.info(`[Agent ${agentId}] Failed ${source} restart suppressed after stop request`);
16547
+ return true;
16548
+ }
16281
16549
  cleanupFailedRuntimeStart(agentId, ap, err) {
16282
16550
  if (!ap) return;
16283
16551
  if (this.agents.get(agentId) !== ap) return;
@@ -16298,6 +16566,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16298
16566
  if (this.lifecycleRecords.deleteTerminalFailure(agentId)) {
16299
16567
  this.closeNoProcessResidency(agentId, "terminal", { negativeEvidenceBucket: "runtime_start_failed" });
16300
16568
  }
16569
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "runtime_start_failed_cleanup", ap);
16301
16570
  }
16302
16571
  cleanupTerminalRuntimeFailure(agentId, ap, detail) {
16303
16572
  if (this.agents.get(agentId) !== ap) return;
@@ -16583,6 +16852,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16583
16852
  return messages.some((message) => !runtimeProfileNotificationFromMessage(message));
16584
16853
  }
16585
16854
  async stopAgent(agentId, { wait = false, silent = false } = {}) {
16855
+ this.lifecycleRecords.recordStop(agentId);
16586
16856
  this.cancelQueuedAgentStart(agentId, "stop requested");
16587
16857
  this.lifecycleRecords.deletePendingStartRebind(agentId);
16588
16858
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
@@ -16759,6 +17029,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16759
17029
  return true;
16760
17030
  }
16761
17031
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
17032
+ const restartStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
16762
17033
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
16763
17034
  outcome: "auto_restart_from_idle",
16764
17035
  accepted: true,
@@ -16774,6 +17045,9 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16774
17045
  return true;
16775
17046
  }, (err) => {
16776
17047
  logger.error(`[Agent ${agentId}] Failed to auto-restart`, err);
17048
+ if (this.suppressFailedRestartAfterStop(agentId, restartStopEpoch, "idle-auto-restart")) {
17049
+ return false;
17050
+ }
16777
17051
  if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "idle_auto_restart")) {
16778
17052
  this.lifecycleRecords.setRestartSnapshot(agentId, cached);
16779
17053
  const report2 = this.recordSpawnFailure(agentId, "runner_credential_mint");
@@ -17428,12 +17702,23 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17428
17702
  return true;
17429
17703
  }
17430
17704
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
17705
+ const restartStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
17431
17706
  return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => {
17432
17707
  this.resetSpawnFailBackoff(agentId);
17433
17708
  this.assertStartPendingDeliveryInvariants("runtime-profile-auto-restart-success");
17434
17709
  return true;
17435
17710
  }, (err) => {
17436
17711
  logger.error(`[Agent ${agentId}] Failed to auto-restart for runtime profile notification`, err);
17712
+ if (this.suppressFailedRestartAfterStop(agentId, restartStopEpoch, "runtime-profile-auto-restart")) {
17713
+ span.end("ok", {
17714
+ attrs: {
17715
+ outcome: "suppressed_after_stop",
17716
+ runtime: cached.config.runtime,
17717
+ launchId: cached.launchId || void 0
17718
+ }
17719
+ });
17720
+ return false;
17721
+ }
17437
17722
  if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "runtime_profile_auto_restart")) {
17438
17723
  this.lifecycleRecords.setRestartSnapshot(agentId, cached);
17439
17724
  const report2 = this.recordSpawnFailure(agentId, "runner_credential_mint");
@@ -17574,7 +17859,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17574
17859
  return deleteWorkspaceDirectory(this.dataDir, directoryName);
17575
17860
  }
17576
17861
  // Workspace file browsing
17577
- async getFileTree(agentId, dirPath) {
17862
+ async getFileTree(agentId, dirPath, includeHidden = false) {
17578
17863
  const agentDir = path14.join(this.dataDir, agentId);
17579
17864
  try {
17580
17865
  await stat2(agentDir);
@@ -17587,9 +17872,16 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17587
17872
  if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
17588
17873
  return [];
17589
17874
  }
17875
+ const relativePath = path14.relative(agentDir, resolved);
17876
+ if (isWorkspaceNeverVisibleHiddenPath(relativePath)) {
17877
+ return [];
17878
+ }
17879
+ if (!includeHidden && isWorkspaceHiddenPath(relativePath)) {
17880
+ return [];
17881
+ }
17590
17882
  targetDir = resolved;
17591
17883
  }
17592
- return this.listDirectoryChildren(targetDir, agentDir);
17884
+ return this.listDirectoryChildren(targetDir, agentDir, includeHidden);
17593
17885
  }
17594
17886
  async readFile(agentId, filePath) {
17595
17887
  const agentDir = path14.join(this.dataDir, agentId);
@@ -17597,6 +17889,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17597
17889
  if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
17598
17890
  throw new Error("Access denied");
17599
17891
  }
17892
+ const relativePath = path14.relative(agentDir, resolved);
17893
+ if (isWorkspaceNeverVisibleHiddenPath(relativePath) || isWorkspaceSecretFilePath(relativePath)) {
17894
+ throw new Error("Preview is disabled for sensitive workspace files");
17895
+ }
17600
17896
  const info = await stat2(resolved);
17601
17897
  if (info.isDirectory()) throw new Error("Cannot read a directory");
17602
17898
  const ext = path14.extname(resolved).toLowerCase();
@@ -17909,12 +18205,19 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17909
18205
  * should carry explicit reason/correlation/window attrs so the server no
17910
18206
  * longer infers lifecycle semantics from generic activity strings.
17911
18207
  */
17912
- broadcastActivity(agentId, activityKind, detail, extraTrajectory = [], launchIdOverride, detailKind = "other", activityDisplay = activityKind) {
18208
+ broadcastActivity(agentId, activityKind, detail, extraTrajectory = [], launchIdOverride, detailKind = "other", activityDisplay = activityKind, subagentLineage) {
17913
18209
  const ap = this.agents.get(agentId);
17914
18210
  const entries = [...extraTrajectory];
17915
18211
  const hasToolStart = entries.some((e) => e.kind === "tool_start");
17916
18212
  if (!hasToolStart) {
17917
- entries.push({ kind: "status", activity: activityKind, activityKind, detail, detailKind });
18213
+ entries.push({
18214
+ kind: "status",
18215
+ activity: activityKind,
18216
+ activityKind,
18217
+ detail,
18218
+ detailKind,
18219
+ ...subagentLineage ? { subagent: subagentLineage } : {}
18220
+ });
17918
18221
  }
17919
18222
  const launchId = launchIdOverride || ap?.launchId || void 0;
17920
18223
  const clientSeq = this.nextActivityClientSeq(agentId);
@@ -18050,13 +18353,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18050
18353
  ap.pendingTrajectory = null;
18051
18354
  const text = pending.text.length > MAX_TRAJECTORY_TEXT ? pending.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : pending.text;
18052
18355
  if (!text) return;
18053
- if (pending.kind === "thinking") {
18054
- this.broadcastActivity(agentId, "thinking", "", [{ kind: "thinking", text }], void 0, "none");
18055
- } else {
18056
- this.broadcastActivity(agentId, "thinking", "", [{ kind: "text", text }], void 0, "none");
18057
- }
18356
+ const entry = pending.kind === "thinking" ? { kind: "thinking", text, ...pending.subagent ? { subagent: pending.subagent } : {} } : { kind: "text", text, ...pending.subagent ? { subagent: pending.subagent } : {} };
18357
+ this.broadcastActivity(agentId, "thinking", "", [entry], void 0, "none");
18058
18358
  }
18059
- queueTrajectoryText(agentId, kind, text) {
18359
+ queueTrajectoryText(agentId, kind, text, subagent) {
18060
18360
  const ap = this.agents.get(agentId);
18061
18361
  if (!ap) {
18062
18362
  this.recordDaemonTrace("daemon.agent.activity.skipped", {
@@ -18072,7 +18372,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18072
18372
  return;
18073
18373
  }
18074
18374
  const pending = ap.pendingTrajectory;
18075
- if (pending && pending.kind === kind) {
18375
+ const sameLineage = JSON.stringify(pending?.subagent ?? null) === JSON.stringify(subagent ?? null);
18376
+ if (pending && pending.kind === kind && sameLineage) {
18076
18377
  pending.text += text;
18077
18378
  clearTimeout(pending.timer);
18078
18379
  pending.timer = setTimeout(() => this.flushPendingTrajectory(agentId), TRAJECTORY_COALESCE_MS);
@@ -18085,6 +18386,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18085
18386
  ap.pendingTrajectory = {
18086
18387
  kind,
18087
18388
  text,
18389
+ ...subagent ? { subagent } : {},
18088
18390
  timer: setTimeout(() => this.flushPendingTrajectory(agentId), TRAJECTORY_COALESCE_MS)
18089
18391
  };
18090
18392
  }
@@ -18795,6 +19097,23 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18795
19097
  if (ap.runtimeProgress.isStale) return true;
18796
19098
  const staleForMs = ap.runtimeProgress.ageMs();
18797
19099
  if (staleForMs < RUNTIME_PROGRESS_STALE_MS) return false;
19100
+ const subprocessPidAlive = this.probeRuntimeProcessLiveness(ap);
19101
+ if (subprocessPidAlive === true) {
19102
+ this.recordDaemonTrace("daemon.runtime.stall.suppressed_alive", {
19103
+ ...this.processLifecycleIdentityAttrs(agentId, ap),
19104
+ last_event_kind: ap.lastActivityKind || void 0,
19105
+ last_event_age_ms_bucket: bucketMs(staleForMs),
19106
+ subprocess_pid_alive: true,
19107
+ subprocess_socket_alive: !ap.runtime.closed,
19108
+ daemon_connected_to_server: this.serverConnected()
19109
+ });
19110
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.silent_alive", {
19111
+ staleForMs: bucketMs(staleForMs),
19112
+ lastActivity: ap.lastActivityKind || void 0,
19113
+ lastActivityDetailKind: ap.lastActivityDetailKind
19114
+ });
19115
+ return false;
19116
+ }
18798
19117
  ap.runtimeProgress.markStale();
18799
19118
  const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
18800
19119
  const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
@@ -18814,15 +19133,6 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18814
19133
  ...runtimeTraceCounterAttrs(ap),
18815
19134
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
18816
19135
  });
18817
- let subprocessPidAlive;
18818
- if (typeof ap.runtime.pid === "number") {
18819
- try {
18820
- process.kill(ap.runtime.pid, 0);
18821
- subprocessPidAlive = true;
18822
- } catch {
18823
- subprocessPidAlive = false;
18824
- }
18825
- }
18826
19136
  this.recordDaemonTrace("daemon.runtime.stall.detected", {
18827
19137
  ...this.processLifecycleIdentityAttrs(agentId, ap),
18828
19138
  last_event_kind: ap.lastActivityKind || void 0,
@@ -18834,6 +19144,17 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18834
19144
  this.broadcastActivity(agentId, "error", diagnostic.detail, [], void 0, "runtime_stalled");
18835
19145
  return true;
18836
19146
  }
19147
+ probeRuntimeProcessLiveness(ap) {
19148
+ const pid = ap.runtime.pid;
19149
+ if (typeof pid !== "number") return void 0;
19150
+ try {
19151
+ process.kill(pid, 0);
19152
+ return true;
19153
+ } catch (err) {
19154
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
19155
+ return code === "EPERM" ? true : false;
19156
+ }
19157
+ }
18837
19158
  recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap) {
18838
19159
  const staleForMs = ap.runtimeProgress.ageMs();
18839
19160
  const reduction = reduceApmStalledRecoveryTermination(ap.gatedSteering, {
@@ -18944,6 +19265,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18944
19265
  source: event.source,
18945
19266
  itemType: event.itemType,
18946
19267
  payloadBytes: event.payloadBytes
19268
+ } : event.kind === "subagent_progress" ? {
19269
+ kind: event.kind,
19270
+ source: event.source,
19271
+ phase: event.phase,
19272
+ parent_tool_use_id_present: Boolean(event.parentToolUseId),
19273
+ subagent_type_present: Boolean(event.subagentType),
19274
+ task_id_present: Boolean(event.taskId),
19275
+ last_tool_name_present: Boolean(event.lastToolName),
19276
+ payloadBytes: event.payloadBytes
18947
19277
  } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : event.kind === "runtime_recovery" ? runtimeRecoveryTraceAttrs(event) : { kind: event.kind };
18948
19278
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
18949
19279
  const recordProgressObservedAfterStall = () => {
@@ -18965,6 +19295,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18965
19295
  itemType: event.itemType,
18966
19296
  payloadBytes: event.payloadBytes
18967
19297
  });
19298
+ this.maybeBroadcastRuntimeProgressActivity(agentId, ap);
19299
+ return;
19300
+ }
19301
+ if (event.kind === "subagent_progress") {
19302
+ this.noteRuntimeProgress(ap, event.kind);
19303
+ recordProgressObservedAfterStall();
19304
+ this.invalidateRecoveryErrorView(ap);
19305
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19306
+ this.recordSubagentProgressActivity(agentId, ap, event);
18968
19307
  return;
18969
19308
  }
18970
19309
  if (event.kind === "runtime_diagnostic") {
@@ -19001,7 +19340,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19001
19340
  break;
19002
19341
  case "thinking": {
19003
19342
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
19004
- this.queueTrajectoryText(agentId, "thinking", event.text);
19343
+ this.queueTrajectoryText(agentId, "thinking", event.text, event.subagent);
19005
19344
  if (ap) {
19006
19345
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19007
19346
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
@@ -19012,7 +19351,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19012
19351
  }
19013
19352
  case "text": {
19014
19353
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
19015
- this.queueTrajectoryText(agentId, "text", event.text);
19354
+ this.queueTrajectoryText(agentId, "text", event.text, event.subagent);
19016
19355
  if (ap) {
19017
19356
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19018
19357
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
@@ -19039,8 +19378,9 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19039
19378
  this.broadcastActivity(agentId, "working", detail, [{
19040
19379
  kind: "tool_start",
19041
19380
  toolName: invocation.toolName,
19042
- toolInput: inputSummary
19043
- }], void 0, this.toolActivityDetailKind(invocation.toolName));
19381
+ toolInput: inputSummary,
19382
+ ...event.subagent ? { subagent: event.subagent } : {}
19383
+ }], void 0, event.subagent ? "subagent_activity" : this.toolActivityDetailKind(invocation.toolName));
19044
19384
  break;
19045
19385
  }
19046
19386
  case "tool_output": {
@@ -19779,7 +20119,7 @@ ${RESPONSE_TARGET_HINT}`);
19779
20119
  return true;
19780
20120
  }
19781
20121
  /** List ONE level of a directory — directories returned without children (lazy-loaded on demand) */
19782
- async listDirectoryChildren(dir, rootDir) {
20122
+ async listDirectoryChildren(dir, rootDir, includeHidden = false) {
19783
20123
  let entries;
19784
20124
  try {
19785
20125
  entries = await readdir2(dir, { withFileTypes: true });
@@ -19793,7 +20133,9 @@ ${RESPONSE_TARGET_HINT}`);
19793
20133
  });
19794
20134
  const nodes = [];
19795
20135
  for (const entry of entries) {
19796
- if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
20136
+ const isHidden = entry.name.startsWith(".");
20137
+ if (entry.name === "node_modules") continue;
20138
+ if (isHidden && (!includeHidden || isWorkspaceNeverVisibleHiddenEntry(entry.name))) continue;
19797
20139
  const fullPath = path14.join(dir, entry.name);
19798
20140
  const relativePath = path14.relative(rootDir, fullPath);
19799
20141
  let info;
@@ -19803,9 +20145,9 @@ ${RESPONSE_TARGET_HINT}`);
19803
20145
  continue;
19804
20146
  }
19805
20147
  if (entry.isDirectory()) {
19806
- nodes.push({ name: entry.name, path: relativePath, isDirectory: true, size: 0, modifiedAt: info.mtime.toISOString() });
20148
+ nodes.push({ name: entry.name, path: relativePath, isDirectory: true, size: 0, modifiedAt: info.mtime.toISOString(), isHidden });
19807
20149
  } else {
19808
- nodes.push({ name: entry.name, path: relativePath, isDirectory: false, size: info.size, modifiedAt: info.mtime.toISOString() });
20150
+ nodes.push({ name: entry.name, path: relativePath, isDirectory: false, size: info.size, modifiedAt: info.mtime.toISOString(), isHidden });
19809
20151
  }
19810
20152
  }
19811
20153
  return nodes;
@@ -19843,6 +20185,7 @@ var DaemonConnection = class {
19843
20185
  lastDroppedSendLogAt = 0;
19844
20186
  lastInboundAt = null;
19845
20187
  lastInboundMessageKind = null;
20188
+ inboundProbeInFlight = false;
19846
20189
  pendingActivityByAgent = /* @__PURE__ */ new Map();
19847
20190
  latestObservedLaunchIdByAgent = /* @__PURE__ */ new Map();
19848
20191
  constructor(options) {
@@ -19996,11 +20339,44 @@ var DaemonConnection = class {
19996
20339
  }
19997
20340
  resetWatchdog() {
19998
20341
  this.clearWatchdog();
20342
+ this.inboundProbeInFlight = false;
20343
+ this.scheduleWatchdogDeadline();
20344
+ }
20345
+ scheduleWatchdogDeadline() {
19999
20346
  const ms = this.options.inboundWatchdogMs ?? INBOUND_WATCHDOG_MS;
20000
20347
  this.watchdogTimer = this.clock.setTimeout(() => {
20001
- logger.warn(`[Daemon] No inbound traffic for ${ms / 1e3}s \u2014 forcing reconnect`);
20348
+ if (!this.inboundProbeInFlight && this.ws?.readyState === WebSocket.OPEN) {
20349
+ this.inboundProbeInFlight = true;
20350
+ logger.info(`[Daemon] No inbound traffic for ${ms / 1e3}s \u2014 sending liveness probe`);
20351
+ this.trace("daemon.connection.inbound_probe_sent", {
20352
+ inbound_watchdog_ms: ms,
20353
+ last_inbound_message_kind: this.lastInboundMessageKind,
20354
+ last_inbound_age_ms_bucket: this.lastInboundAgeBucket(),
20355
+ ws_ready_state: this.ws.readyState,
20356
+ reconnecting: this.shouldConnect
20357
+ });
20358
+ try {
20359
+ this.ws.send(JSON.stringify({ type: "ping" }));
20360
+ } catch (err) {
20361
+ this.trace("daemon.connection.inbound_probe_send_failed", {
20362
+ inbound_watchdog_ms: ms,
20363
+ error_class: err instanceof Error ? err.name : typeof err,
20364
+ ws_ready_state: this.ws?.readyState ?? null,
20365
+ reconnecting: this.shouldConnect
20366
+ }, "error");
20367
+ try {
20368
+ this.ws?.terminate();
20369
+ } catch {
20370
+ }
20371
+ return;
20372
+ }
20373
+ this.scheduleWatchdogDeadline();
20374
+ return;
20375
+ }
20376
+ logger.warn(`[Daemon] No inbound traffic after liveness probe \u2014 forcing reconnect`);
20002
20377
  this.trace("daemon.connection.watchdog_timeout", {
20003
20378
  inbound_watchdog_ms: ms,
20379
+ probe_in_flight: this.inboundProbeInFlight,
20004
20380
  last_inbound_message_kind: this.lastInboundMessageKind,
20005
20381
  last_inbound_age_ms_bucket: this.lastInboundAgeBucket(),
20006
20382
  ws_ready_state: this.ws?.readyState ?? null,
@@ -21032,7 +21408,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
21032
21408
  }
21033
21409
  async function runBundledSlockCli(argv) {
21034
21410
  process.argv = [process.execPath, "slock", ...argv];
21035
- await import("./dist-TGKVYIA7.js");
21411
+ await import("./dist-Y3VRAU5E.js");
21036
21412
  }
21037
21413
  function detectRuntimes(tracer = noopTracer) {
21038
21414
  const ids = [];
@@ -21144,7 +21520,7 @@ function summarizeIncomingMessage(msg) {
21144
21520
  case "agent:runtime_profile:daemon_release_notice":
21145
21521
  return `(agent=${msg.agentId}, notice=${msg.noticeKey})`;
21146
21522
  case "agent:workspace:list":
21147
- return `(agent=${msg.agentId}, dir=${msg.dirPath || "."})`;
21523
+ return `(agent=${msg.agentId}, dir=${msg.dirPath || "."}, hidden=${msg.includeHidden ? "yes" : "no"})`;
21148
21524
  case "agent:workspace:read":
21149
21525
  return `(agent=${msg.agentId}, path=${msg.path})`;
21150
21526
  case "agent:skills:list":
@@ -21669,8 +22045,8 @@ var DaemonCore = class {
21669
22045
  break;
21670
22046
  }
21671
22047
  case "agent:workspace:list":
21672
- this.agentManager.getFileTree(msg.agentId, msg.dirPath).then((files) => {
21673
- this.connection.send({ type: "agent:workspace:file_tree", agentId: msg.agentId, files, dirPath: msg.dirPath });
22048
+ this.agentManager.getFileTree(msg.agentId, msg.dirPath, Boolean(msg.includeHidden)).then((files) => {
22049
+ this.connection.send({ type: "agent:workspace:file_tree", agentId: msg.agentId, files, dirPath: msg.dirPath, includeHidden: Boolean(msg.includeHidden) });
21674
22050
  });
21675
22051
  break;
21676
22052
  case "agent:workspace:read":
package/dist/cli/index.js CHANGED
@@ -44868,6 +44868,14 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
44868
44868
  "synthetic_repair",
44869
44869
  "slock_action",
44870
44870
  "system_message",
44871
+ // Generic structured runtime-progress heartbeat (Claude --include-partial-messages
44872
+ // stream events / system status) — a "working" liveness signal with no rendered
44873
+ // content. NOT a subagent. (APM 1.6 6a)
44874
+ "runtime_progress",
44875
+ // A subagent (Claude `Agent` tool) lifecycle/activity row, marked from explicit
44876
+ // parent_tool_use_id / task-lifecycle lineage — never inferred from display text.
44877
+ // (APM 1.6 6b)
44878
+ "subagent_activity",
44871
44879
  "other"
44872
44880
  ];
44873
44881
  var isAgentActivityDetailKind = makeIsMember(AGENT_ACTIVITY_DETAIL_KINDS);
package/dist/core.js CHANGED
@@ -12,7 +12,7 @@ import {
12
12
  runBundledSlockCli,
13
13
  scanWorkspaceDirectories,
14
14
  subscribeDaemonLogs
15
- } from "./chunk-H3WRFI54.js";
15
+ } from "./chunk-RXK2KSYW.js";
16
16
  export {
17
17
  DAEMON_CLI_USAGE,
18
18
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
@@ -44397,6 +44397,14 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
44397
44397
  "synthetic_repair",
44398
44398
  "slock_action",
44399
44399
  "system_message",
44400
+ // Generic structured runtime-progress heartbeat (Claude --include-partial-messages
44401
+ // stream events / system status) — a "working" liveness signal with no rendered
44402
+ // content. NOT a subagent. (APM 1.6 6a)
44403
+ "runtime_progress",
44404
+ // A subagent (Claude `Agent` tool) lifecycle/activity row, marked from explicit
44405
+ // parent_tool_use_id / task-lifecycle lineage — never inferred from display text.
44406
+ // (APM 1.6 6b)
44407
+ "subagent_activity",
44400
44408
  "other"
44401
44409
  ];
44402
44410
  var isAgentActivityDetailKind = makeIsMember(AGENT_ACTIVITY_DETAIL_KINDS);
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
5
  parseDaemonCliArgs
6
- } from "./chunk-H3WRFI54.js";
6
+ } from "./chunk-RXK2KSYW.js";
7
7
 
8
8
  // src/index.ts
9
9
  var parsedArgs = parseDaemonCliArgs(process.argv.slice(2));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-daemon",
3
- "version": "0.67.0",
3
+ "version": "0.68.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "raft-daemon": "dist/raft-daemon.js",