@botiverse/raft-daemon 0.67.0 → 0.69.0-play.20260704183824

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.
@@ -3288,6 +3288,7 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
3288
3288
  "compaction_stale",
3289
3289
  "reviewing_changes",
3290
3290
  "review_finished",
3291
+ "review_stale",
3291
3292
  "runtime_reconnecting",
3292
3293
  "runtime_error",
3293
3294
  "runtime_crashed",
@@ -3303,6 +3304,14 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
3303
3304
  "synthetic_repair",
3304
3305
  "slock_action",
3305
3306
  "system_message",
3307
+ // Generic structured runtime-progress heartbeat (Claude --include-partial-messages
3308
+ // stream events / system status) — a "working" liveness signal with no rendered
3309
+ // content. NOT a subagent. (APM 1.6 6a)
3310
+ "runtime_progress",
3311
+ // A subagent (Claude `Agent` tool) lifecycle/activity row, marked from explicit
3312
+ // parent_tool_use_id / task-lifecycle lineage — never inferred from display text.
3313
+ // (APM 1.6 6b)
3314
+ "subagent_activity",
3306
3315
  "other"
3307
3316
  ];
3308
3317
  var isAgentActivityDetailKind = makeIsMember(AGENT_ACTIVITY_DETAIL_KINDS);
@@ -4700,6 +4709,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
4700
4709
  return candidates.filter((candidate) => existsSync(candidate.path));
4701
4710
  }
4702
4711
 
4712
+ // src/authEnv.ts
4713
+ var DAEMON_API_KEY_ENV = "SLOCK_MACHINE_API_KEY";
4714
+ var SLOCK_AGENT_TOKEN_ENV = "SLOCK_AGENT_TOKEN";
4715
+ function scrubDaemonAuthEnv(env) {
4716
+ delete env[DAEMON_API_KEY_ENV];
4717
+ return env;
4718
+ }
4719
+ function scrubDaemonChildEnv(env) {
4720
+ delete env[DAEMON_API_KEY_ENV];
4721
+ delete env[SLOCK_AGENT_TOKEN_ENV];
4722
+ return env;
4723
+ }
4724
+
4703
4725
  // src/agentCredentialProxy.ts
4704
4726
  import { randomBytes } from "crypto";
4705
4727
  import http from "http";
@@ -5875,27 +5897,44 @@ function writeProxyFailureResponse(res, failure) {
5875
5897
  res.destroy();
5876
5898
  return;
5877
5899
  }
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",
5900
+ const body = {
5901
+ error: failure.responseError ?? "failed to proxy local agent request",
5902
+ code: failure.responseCode ?? "agent_proxy_failed",
5882
5903
  detail: failure.errorMessage
5883
- }));
5904
+ };
5905
+ if (failure.lifecycleInvalidAgentId) body.agent_id = failure.lifecycleInvalidAgentId;
5906
+ if (failure.lifecycleInvalidContext) body.invariant_context = failure.lifecycleInvalidContext;
5907
+ res.writeHead(failure.responseStatusCode ?? 502, { "content-type": "application/json" });
5908
+ res.end(JSON.stringify(body));
5884
5909
  }
5885
5910
  function proxyFailureForError(method, target, err) {
5886
5911
  const queryKeys = target ? [.../* @__PURE__ */ new Set([...target.searchParams.keys()])].sort() : [];
5887
5912
  const cause = err instanceof Error ? err.cause : void 0;
5913
+ const errorMessage = truncateProxyErrorMessage(err instanceof Error ? err.message : String(err));
5914
+ const lifecycleInvalid = parseAgentNoProcessResidencyInvariant(errorMessage);
5888
5915
  const failure = {
5889
5916
  method,
5890
5917
  pathname: target?.pathname ?? "unknown",
5891
5918
  queryKeys,
5892
5919
  errorName: err instanceof Error ? err.name : typeof err,
5893
- errorMessage: truncateProxyErrorMessage(err instanceof Error ? err.message : String(err))
5920
+ errorMessage
5894
5921
  };
5922
+ if (lifecycleInvalid) {
5923
+ failure.responseStatusCode = 409;
5924
+ failure.responseCode = "agent_lifecycle_state_invalid";
5925
+ failure.responseError = "local daemon agent lifecycle state invalid";
5926
+ failure.lifecycleInvalidAgentId = lifecycleInvalid.agentId;
5927
+ failure.lifecycleInvalidContext = lifecycleInvalid.context;
5928
+ }
5895
5929
  const errorCause = describeProxyErrorCause(cause);
5896
5930
  if (errorCause) failure.errorCause = errorCause;
5897
5931
  return failure;
5898
5932
  }
5933
+ function parseAgentNoProcessResidencyInvariant(message) {
5934
+ const match = /^Agent no-process residency invariant violation after ([^:]+): .+ for ([0-9a-f-]{36})\b/i.exec(message);
5935
+ if (!match) return void 0;
5936
+ return { context: match[1], agentId: match[2] };
5937
+ }
5899
5938
  function describeProxyErrorCause(cause) {
5900
5939
  if (!cause) return void 0;
5901
5940
  if (cause instanceof Error) {
@@ -5932,18 +5971,21 @@ function transportNormalizedErrorForHttpStatus(target, status, launchId) {
5932
5971
  };
5933
5972
  }
5934
5973
  function transportNormalizedErrorForError(target, err, launchId) {
5974
+ const localDaemonStateInvalid = parseAgentNoProcessResidencyInvariant(
5975
+ sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err))
5976
+ );
5935
5977
  return {
5936
- normalizedCode: "transport_failure",
5978
+ normalizedCode: localDaemonStateInvalid ? "local_daemon_state_invalid" : "transport_failure",
5937
5979
  routeFamily: routeFamilyForPath(target?.pathname ?? "unknown"),
5938
5980
  responseStarted: false,
5939
- upstreamLayer: target ? upstreamLayerForProxyError(err) : "unknown",
5981
+ upstreamLayer: localDaemonStateInvalid ? "unknown" : target ? upstreamLayerForProxyError(err) : "unknown",
5940
5982
  originalMessage: sanitizeTransportOriginalMessage(err instanceof Error ? err.message : String(err)),
5941
5983
  launchId,
5942
- targetHostClass: target ? daemonUpstreamTargetHostClass(target) : "custom_server",
5984
+ targetHostClass: localDaemonStateInvalid ? "local_daemon" : target ? daemonUpstreamTargetHostClass(target) : "custom_server",
5943
5985
  // Today the local credential proxy is only used by CLI wrappers. If runtime
5944
5986
  // or daemon-internal callers use it later, plumb the caller identity here.
5945
5987
  downstreamCaller: "cli",
5946
- upstream: "server"
5988
+ upstream: localDaemonStateInvalid ? "local_daemon" : "server"
5947
5989
  };
5948
5990
  }
5949
5991
  function routeFamilyForPath(pathname) {
@@ -6254,7 +6296,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
6254
6296
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
6255
6297
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
6256
6298
  var RAW_CREDENTIAL_ENV_DENYLIST = [
6257
- "SLOCK_AGENT_CREDENTIAL_KEY"
6299
+ "SLOCK_AGENT_TOKEN",
6300
+ "SLOCK_AGENT_CREDENTIAL_KEY",
6301
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
6258
6302
  ];
6259
6303
  var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
6260
6304
  "agent-token",
@@ -6609,7 +6653,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6609
6653
  SLOCK_SERVER_URL: ctx.config.serverUrl,
6610
6654
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
6611
6655
  };
6612
- delete spawnEnv.SLOCK_AGENT_TOKEN;
6656
+ scrubDaemonChildEnv(spawnEnv);
6613
6657
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
6614
6658
  delete spawnEnv[key];
6615
6659
  }
@@ -6628,6 +6672,28 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6628
6672
  }
6629
6673
 
6630
6674
  // src/drivers/claudeEventNormalizer.ts
6675
+ function extractSubagentLineage(event) {
6676
+ const parentToolUseId = finiteString(event.parent_tool_use_id);
6677
+ const subagentType = finiteString(event.subagent_type);
6678
+ if (!parentToolUseId && !subagentType) return void 0;
6679
+ return {
6680
+ ...parentToolUseId ? { parentToolUseId } : {},
6681
+ ...subagentType ? { subagentType } : {},
6682
+ phase: "active"
6683
+ };
6684
+ }
6685
+ function taskLifecyclePhase(subtype) {
6686
+ switch (subtype) {
6687
+ case "task_started":
6688
+ return "started";
6689
+ case "task_progress":
6690
+ return "progress";
6691
+ case "task_notification":
6692
+ return "notification";
6693
+ default:
6694
+ return null;
6695
+ }
6696
+ }
6631
6697
  function collectResultErrorDetail(message, fallback) {
6632
6698
  const parts = [];
6633
6699
  if (Array.isArray(message.errors)) {
@@ -6801,6 +6867,25 @@ var ClaudeEventNormalizer = class {
6801
6867
  if (event.subtype === "compact_boundary") {
6802
6868
  events.push({ kind: "compaction_finished" });
6803
6869
  }
6870
+ {
6871
+ const phase = typeof event.subtype === "string" ? taskLifecyclePhase(event.subtype) : null;
6872
+ if (phase) {
6873
+ events.push({
6874
+ kind: "subagent_progress",
6875
+ source: "claude_task_lifecycle",
6876
+ phase,
6877
+ ...finiteString(event.task_id) ? { taskId: finiteString(event.task_id) } : {},
6878
+ // `system` task-lifecycle envelopes carry the outer Agent tool id
6879
+ // as `tool_use_id`; belt-and-suspenders fall back to
6880
+ // `parent_tool_use_id` in case a Claude version names it that way
6881
+ // on the envelope (confirmed field name = tool_use_id, @Huarong).
6882
+ ...finiteString(event.tool_use_id) ?? finiteString(event.parent_tool_use_id) ? { parentToolUseId: finiteString(event.tool_use_id) ?? finiteString(event.parent_tool_use_id) } : {},
6883
+ ...finiteString(event.subagent_type) ? { subagentType: finiteString(event.subagent_type) } : {},
6884
+ ...finiteString(event.last_tool_name) ? { lastToolName: finiteString(event.last_tool_name) } : {},
6885
+ payloadBytes: Buffer.byteLength(line, "utf8")
6886
+ });
6887
+ }
6888
+ }
6804
6889
  break;
6805
6890
  case "stream_event":
6806
6891
  events.push({
@@ -6812,19 +6897,21 @@ var ClaudeEventNormalizer = class {
6812
6897
  break;
6813
6898
  case "assistant": {
6814
6899
  const content = event.message?.content;
6900
+ const subagent = extractSubagentLineage(event);
6901
+ const withLineage = subagent ? { subagent } : {};
6815
6902
  if (Array.isArray(content)) {
6816
6903
  const hasToolUse = content.some((block) => block?.type === "tool_use");
6817
6904
  for (const block of content) {
6818
6905
  if (block.type === "thinking" && block.thinking) {
6819
- events.push({ kind: "thinking", text: block.thinking });
6906
+ events.push({ kind: "thinking", text: block.thinking, ...withLineage });
6820
6907
  } else if (block.type === "text" && block.text) {
6821
6908
  if (isProviderApiFailureText(block.text, hasToolUse)) {
6822
6909
  events.push({ kind: "error", message: block.text });
6823
6910
  } else {
6824
- events.push({ kind: "text", text: block.text });
6911
+ events.push({ kind: "text", text: block.text, ...withLineage });
6825
6912
  }
6826
6913
  } else if (block.type === "tool_use") {
6827
- events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input });
6914
+ events.push({ kind: "tool_call", name: block.name || "unknown_tool", input: block.input, ...withLineage });
6828
6915
  }
6829
6916
  }
6830
6917
  }
@@ -6832,10 +6919,12 @@ var ClaudeEventNormalizer = class {
6832
6919
  }
6833
6920
  case "user": {
6834
6921
  const content = event.message?.content;
6922
+ const subagent = extractSubagentLineage(event);
6923
+ const withLineage = subagent ? { subagent } : {};
6835
6924
  if (Array.isArray(content)) {
6836
6925
  for (const block of content) {
6837
6926
  if (block.type === "tool_result") {
6838
- events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result" });
6927
+ events.push({ kind: "tool_output", name: block.name || block.tool_use_id || "tool_result", ...withLineage });
6839
6928
  }
6840
6929
  }
6841
6930
  }
@@ -7132,7 +7221,7 @@ function requiresWindowsShell(command, platform = process.platform) {
7132
7221
  }
7133
7222
  function resolveCommandOnPath(command, deps = {}) {
7134
7223
  const platform = deps.platform ?? process.platform;
7135
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7224
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7136
7225
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7137
7226
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
7138
7227
  if (platform === "win32") {
@@ -7158,7 +7247,7 @@ function firstExistingPath(candidates, deps = {}) {
7158
7247
  return null;
7159
7248
  }
7160
7249
  function readCommandVersion(command, args = [], deps = {}) {
7161
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7250
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7162
7251
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7163
7252
  try {
7164
7253
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -9242,11 +9331,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
9242
9331
  return parseCursorModelsOutput(String(result.stdout || ""));
9243
9332
  }
9244
9333
  function buildCursorModelProbeEnv(deps = {}) {
9245
- return withWindowsUserEnvironment({
9334
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
9246
9335
  ...deps.env ?? process.env,
9247
9336
  FORCE_COLOR: "0",
9248
9337
  NO_COLOR: "1"
9249
- }, deps);
9338
+ }, deps));
9250
9339
  }
9251
9340
  function runCursorModelsCommand() {
9252
9341
  return spawnSync("cursor-agent", ["models"], {
@@ -9302,7 +9391,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
9302
9391
  }
9303
9392
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
9304
9393
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
9305
- const env = deps.env ?? process.env;
9394
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
9306
9395
  const winPath = path8.win32;
9307
9396
  let geminiEntry = null;
9308
9397
  try {
@@ -9439,12 +9528,15 @@ var GeminiDriver = class {
9439
9528
  // src/drivers/kimi.ts
9440
9529
  import { randomUUID as randomUUID2 } from "crypto";
9441
9530
  import { spawn as spawn7 } from "child_process";
9442
- import { existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9531
+ import { chmodSync, existsSync as existsSync6, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
9443
9532
  import os4 from "os";
9444
9533
  import path9 from "path";
9445
9534
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
9446
9535
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
9447
9536
  var KIMI_AGENT_FILE = ".slock-kimi-agent.yaml";
9537
+ var KIMI_GENERATED_CONFIG_FILE = ".slock-kimi-config.toml";
9538
+ var SLOCK_KIMI_CONFIG_CONTENT_ENV = "SLOCK_KIMI_CONFIG_CONTENT";
9539
+ var SLOCK_KIMI_CONFIG_FILE_ENV = "SLOCK_KIMI_CONFIG_FILE";
9448
9540
  function parseToolArguments(raw) {
9449
9541
  if (typeof raw !== "string") return raw;
9450
9542
  try {
@@ -9453,6 +9545,73 @@ function parseToolArguments(raw) {
9453
9545
  return raw;
9454
9546
  }
9455
9547
  }
9548
+ function readKimiConfigSource(home = os4.homedir(), env = process.env) {
9549
+ const inlineConfig = env[SLOCK_KIMI_CONFIG_CONTENT_ENV];
9550
+ if (inlineConfig && inlineConfig.trim()) {
9551
+ return {
9552
+ raw: inlineConfig,
9553
+ explicitPath: null,
9554
+ sourcePath: SLOCK_KIMI_CONFIG_CONTENT_ENV
9555
+ };
9556
+ }
9557
+ const explicitPath = env[SLOCK_KIMI_CONFIG_FILE_ENV];
9558
+ const configPath = explicitPath && explicitPath.trim() ? explicitPath : path9.join(home, ".kimi", "config.toml");
9559
+ try {
9560
+ return {
9561
+ raw: readFileSync3(configPath, "utf8"),
9562
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
9563
+ sourcePath: configPath
9564
+ };
9565
+ } catch {
9566
+ return {
9567
+ raw: null,
9568
+ explicitPath: explicitPath && explicitPath.trim() ? explicitPath : null,
9569
+ sourcePath: configPath
9570
+ };
9571
+ }
9572
+ }
9573
+ function buildKimiSpawnEnv(env = process.env) {
9574
+ const spawnEnv = { ...env, FORCE_COLOR: "0", NO_COLOR: "1" };
9575
+ delete spawnEnv[SLOCK_KIMI_CONFIG_CONTENT_ENV];
9576
+ delete spawnEnv[SLOCK_KIMI_CONFIG_FILE_ENV];
9577
+ return scrubDaemonChildEnv(spawnEnv);
9578
+ }
9579
+ function buildKimiEffectiveEnv(ctx, overrideEnv) {
9580
+ return {
9581
+ ...process.env,
9582
+ ...ctx.config.envVars || {},
9583
+ ...overrideEnv || {}
9584
+ };
9585
+ }
9586
+ function buildKimiLaunchOptions(ctx, opts = {}) {
9587
+ const env = buildKimiEffectiveEnv(ctx, opts.env);
9588
+ const source = readKimiConfigSource(opts.home ?? os4.homedir(), env);
9589
+ const args = [];
9590
+ let configFilePath = null;
9591
+ let configContent = null;
9592
+ if (source.explicitPath) {
9593
+ configFilePath = source.explicitPath;
9594
+ } else if (source.raw !== null && source.sourcePath === SLOCK_KIMI_CONFIG_CONTENT_ENV) {
9595
+ configFilePath = path9.join(ctx.workingDirectory, KIMI_GENERATED_CONFIG_FILE);
9596
+ configContent = source.raw;
9597
+ if (opts.writeGeneratedConfig !== false) {
9598
+ writeFileSync3(configFilePath, source.raw, { encoding: "utf8", mode: 384 });
9599
+ chmodSync(configFilePath, 384);
9600
+ }
9601
+ }
9602
+ if (configFilePath) {
9603
+ args.push("--config-file", configFilePath);
9604
+ }
9605
+ if (ctx.config.model && ctx.config.model !== "default") {
9606
+ args.push("--model", ctx.config.model);
9607
+ }
9608
+ return {
9609
+ args,
9610
+ env: buildKimiSpawnEnv(env),
9611
+ configFilePath,
9612
+ configContent
9613
+ };
9614
+ }
9456
9615
  function resolveKimiSpawn(commandArgs, deps = {}) {
9457
9616
  return {
9458
9617
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -9476,7 +9635,25 @@ var KimiDriver = class {
9476
9635
  };
9477
9636
  model = {
9478
9637
  detectedModelsVerifiedAs: "launchable",
9479
- toLaunchSpec: (modelId) => ({ args: ["--model", modelId] })
9638
+ toLaunchSpec: (modelId, ctx, opts) => {
9639
+ if (!ctx) return { args: ["--model", modelId] };
9640
+ const launchCtx = {
9641
+ ...ctx,
9642
+ config: {
9643
+ ...ctx.config,
9644
+ model: modelId
9645
+ }
9646
+ };
9647
+ const launch = buildKimiLaunchOptions(launchCtx, {
9648
+ home: opts?.home,
9649
+ writeGeneratedConfig: false
9650
+ });
9651
+ return {
9652
+ args: launch.args,
9653
+ env: launch.env,
9654
+ configFiles: launch.configFilePath ? [launch.configFilePath] : void 0
9655
+ };
9656
+ }
9480
9657
  };
9481
9658
  supportsStdinNotification = true;
9482
9659
  busyDeliveryMode = "direct";
@@ -9500,21 +9677,23 @@ var KimiDriver = class {
9500
9677
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
9501
9678
  ""
9502
9679
  ].join("\n"), "utf8");
9680
+ const launch = buildKimiLaunchOptions(ctx);
9503
9681
  const args = [
9504
9682
  "--wire",
9505
9683
  "--yolo",
9506
9684
  "--agent-file",
9507
9685
  agentFilePath,
9508
9686
  "--session",
9509
- this.sessionId
9687
+ this.sessionId,
9688
+ ...launch.args
9510
9689
  ];
9511
9690
  const launchRuntimeFields = runtimeConfigToLaunchFields(hydrateRuntimeConfig(ctx.config));
9512
9691
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
9513
9692
  args.push("--model", launchRuntimeFields.model);
9514
9693
  }
9515
9694
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
9516
- const launch = resolveKimiSpawn(args);
9517
- const proc = spawn7(launch.command, launch.args, {
9695
+ const spawnTarget = resolveKimiSpawn(args);
9696
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
9518
9697
  cwd: ctx.workingDirectory,
9519
9698
  stdio: ["pipe", "pipe", "pipe"],
9520
9699
  env: spawnEnv,
@@ -9522,7 +9701,7 @@ var KimiDriver = class {
9522
9701
  // and has an 8191-character command-line limit. Kimi's official
9523
9702
  // installer/uv entrypoint is an executable, so launch it directly and
9524
9703
  // keep prompts on stdin / files instead of routing through cmd.exe.
9525
- shell: launch.shell
9704
+ shell: spawnTarget.shell
9526
9705
  });
9527
9706
  proc.stdin?.write(JSON.stringify({
9528
9707
  jsonrpc: "2.0",
@@ -9635,14 +9814,9 @@ var KimiDriver = class {
9635
9814
  return detectKimiModels();
9636
9815
  }
9637
9816
  };
9638
- function detectKimiModels(home = os4.homedir()) {
9639
- const configPath = path9.join(home, ".kimi", "config.toml");
9640
- let raw;
9641
- try {
9642
- raw = readFileSync3(configPath, "utf8");
9643
- } catch {
9644
- return null;
9645
- }
9817
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
9818
+ const raw = readKimiConfigSource(home, opts.env).raw;
9819
+ if (raw === null) return null;
9646
9820
  const models = [];
9647
9821
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
9648
9822
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -10308,7 +10482,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
10308
10482
  const platform = deps.platform ?? process.platform;
10309
10483
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
10310
10484
  const result = spawnSyncFn("opencode", ["models"], {
10311
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
10485
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
10312
10486
  encoding: "utf8",
10313
10487
  timeout: 5e3,
10314
10488
  shell: platform === "win32"
@@ -10465,6 +10639,7 @@ var OpenCodeDriver = class {
10465
10639
  };
10466
10640
  supportsStdinNotification = false;
10467
10641
  busyDeliveryMode = "none";
10642
+ supportsNativeStandingPrompt = true;
10468
10643
  terminateProcessOnTurnEnd = true;
10469
10644
  deferSpawnUntilMessage = true;
10470
10645
  shouldDeferWakeMessage(message) {
@@ -12107,6 +12282,18 @@ var AgentLifecycleRecords = class {
12107
12282
  pendingSpawnCauses = /* @__PURE__ */ new Map();
12108
12283
  runtimeErrorFingerprintFences = /* @__PURE__ */ new Map();
12109
12284
  activityClientSeqs = /* @__PURE__ */ new Map();
12285
+ stopEpochs = /* @__PURE__ */ new Map();
12286
+ recordStop(agentId) {
12287
+ const next = (this.stopEpochs.get(agentId) ?? 0) + 1;
12288
+ this.stopEpochs.set(agentId, next);
12289
+ return next;
12290
+ }
12291
+ stopEpoch(agentId) {
12292
+ return this.stopEpochs.get(agentId) ?? 0;
12293
+ }
12294
+ stopEpochChanged(agentId, epoch) {
12295
+ return this.stopEpoch(agentId) !== epoch;
12296
+ }
12110
12297
  nextActivityClientSeq(agentId) {
12111
12298
  const next = (this.activityClientSeqs.get(agentId) ?? 0) + 1;
12112
12299
  this.activityClientSeqs.set(agentId, next);
@@ -13008,6 +13195,36 @@ var WORKSPACE_IMAGE_MIME_BY_EXTENSION = {
13008
13195
  ".png": "image/png",
13009
13196
  ".webp": "image/webp"
13010
13197
  };
13198
+ var WORKSPACE_NEVER_VISIBLE_HIDDEN_NAMES = /* @__PURE__ */ new Set([
13199
+ ".aws",
13200
+ ".gnupg",
13201
+ ".ssh",
13202
+ ".slock",
13203
+ ".slock-runtime"
13204
+ ]);
13205
+ var WORKSPACE_SECRET_FILE_PATTERNS = [
13206
+ /^\.env(?:\.|$)/i,
13207
+ /(?:^|[._-])secret(?:s)?(?:[._-]|$)/i,
13208
+ /(?:^|[._-])credential(?:s)?(?:[._-]|$)/i,
13209
+ /(?:^|[._-])token(?:s)?(?:[._-]|$)/i
13210
+ ];
13211
+ function isWorkspaceNeverVisibleHiddenEntry(name) {
13212
+ return WORKSPACE_NEVER_VISIBLE_HIDDEN_NAMES.has(name) || name.startsWith(".slock-");
13213
+ }
13214
+ function workspacePathParts(relativePath) {
13215
+ return relativePath.split(path14.sep).filter(Boolean);
13216
+ }
13217
+ function isWorkspaceHiddenPath(relativePath) {
13218
+ return workspacePathParts(relativePath).some((part) => part.startsWith("."));
13219
+ }
13220
+ function isWorkspaceNeverVisibleHiddenPath(relativePath) {
13221
+ return workspacePathParts(relativePath).some(isWorkspaceNeverVisibleHiddenEntry);
13222
+ }
13223
+ function isWorkspaceSecretFilePath(filePath) {
13224
+ return workspacePathParts(filePath).some(
13225
+ (part) => WORKSPACE_SECRET_FILE_PATTERNS.some((pattern) => pattern.test(part))
13226
+ );
13227
+ }
13011
13228
  function readPositiveIntegerEnv(name, fallback) {
13012
13229
  const raw = process.env[name];
13013
13230
  if (!raw) return fallback;
@@ -13102,9 +13319,6 @@ function toLocalTime(iso) {
13102
13319
  const pad = (n) => String(n).padStart(2, "0");
13103
13320
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`;
13104
13321
  }
13105
- function formatChannelLabel(message) {
13106
- return message.channel_type === "dm" ? `DM:@${message.channel_name}` : `#${message.channel_name}`;
13107
- }
13108
13322
  function formatMessageTarget(message) {
13109
13323
  return formatAgentMessageVisibleTarget(message);
13110
13324
  }
@@ -13437,15 +13651,6 @@ function formatRuntimeProfileControlStartupInput(control, driver) {
13437
13651
  control.message
13438
13652
  ].join("\n");
13439
13653
  }
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
13654
  var MAX_TRAJECTORY_TEXT = 2e3;
13450
13655
  var TRAJECTORY_COALESCE_MS = 350;
13451
13656
  var ACTIVITY_HEARTBEAT_MS = 6e4;
@@ -13456,11 +13661,12 @@ var RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS = 5 * 6e4;
13456
13661
  var RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD = 3;
13457
13662
  var SPAWN_FAIL_BACKOFF_BASE_MS = 1e3;
13458
13663
  var SPAWN_FAIL_BACKOFF_MAX_MS = 3e4;
13459
- var SPAWN_FAIL_BACKOFF_THRESHOLD = 3;
13664
+ var SPAWN_FAIL_BACKOFF_THRESHOLD = 0;
13460
13665
  var RUNNER_CREDENTIAL_MINT_BACKOFF_BASE_MS = 6e4;
13461
13666
  var RUNNER_CREDENTIAL_MINT_BACKOFF_MAX_MS = 10 * 6e4;
13462
13667
  var RUNNER_CREDENTIAL_MINT_BACKOFF_THRESHOLD = 0;
13463
13668
  var COMPACTION_STALE_MS = 5 * 6e4;
13669
+ var REVIEW_STALE_MS = 10 * 6e4;
13464
13670
  var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
13465
13671
  var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
13466
13672
  var DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS = 1e4;
@@ -14096,6 +14302,39 @@ function runtimeRecoveryTraceAttrs(event) {
14096
14302
  details_present: Boolean(event.details)
14097
14303
  };
14098
14304
  }
14305
+ function subagentProgressDetail(event) {
14306
+ const role = event.subagentType ? ` (${event.subagentType})` : "";
14307
+ switch (event.phase) {
14308
+ case "started":
14309
+ return `Subagent started${role}`;
14310
+ case "progress":
14311
+ return `Subagent working${role}`;
14312
+ case "notification":
14313
+ return `Subagent update${role}`;
14314
+ default:
14315
+ return `Subagent working${role}`;
14316
+ }
14317
+ }
14318
+ function subagentLineageFromEvent(event) {
14319
+ return {
14320
+ ...event.parentToolUseId ? { parentToolUseId: event.parentToolUseId } : {},
14321
+ ...event.subagentType ? { subagentType: event.subagentType } : {},
14322
+ ...event.taskId ? { taskId: event.taskId } : {},
14323
+ phase: event.phase
14324
+ };
14325
+ }
14326
+ function subagentProgressTraceAttrs(event) {
14327
+ return {
14328
+ kind: event.kind,
14329
+ source: event.source,
14330
+ phase: event.phase,
14331
+ parent_tool_use_id_present: Boolean(event.parentToolUseId),
14332
+ subagent_type_present: Boolean(event.subagentType),
14333
+ task_id_present: Boolean(event.taskId),
14334
+ last_tool_name_present: Boolean(event.lastToolName),
14335
+ payloadBytes: event.payloadBytes
14336
+ };
14337
+ }
14099
14338
  function currentErrorCandidates(ap) {
14100
14339
  return ap.decisionErrorWindow.currentErrorCandidates();
14101
14340
  }
@@ -14465,6 +14704,7 @@ var AgentProcessManager = class _AgentProcessManager {
14465
14704
  this.cliTransportTraceDir = traceDir;
14466
14705
  }
14467
14706
  assertStartPendingDeliveryInvariants(context) {
14707
+ this.repairNonresidentRuntimeErrorFingerprintFences(context);
14468
14708
  const residencySnapshot = this.noProcessResidencySnapshot();
14469
14709
  AgentNoProcessResidency.assertInvariants(context, residencySnapshot);
14470
14710
  this.lifecycleRecords.assertInvariants(context, this.agentLifecycleRecordSnapshot());
@@ -14515,10 +14755,10 @@ var AgentProcessManager = class _AgentProcessManager {
14515
14755
  }
14516
14756
  // ----- SPAWN-FAIL BACKOFF (per-agent) ----------------------------------
14517
14757
  // Anchored at auto_restart_from_idle (apm:3596) + runtime_profile_auto_restart (apm:4137).
14518
- // Generic spawn errors keep a threshold > 1 so single-transient hiccups behave as today.
14519
- // Runner credential mint failures are different: one outer spawn failure already means the
14520
- // inner credential-mint loop exhausted RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS. Those enter a
14521
- // longer cooldown immediately so repeated wakes don't keep burning three network fetches.
14758
+ // One outer spawn failure enters cooldown immediately so repeated wakes cannot burn one
14759
+ // full spawn attempt per delivery. Runner credential mint failures keep a longer cooldown
14760
+ // because one outer failure already means the inner credential-mint loop exhausted
14761
+ // RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS.
14522
14762
  // Successful spawn resets state. State lives outside AgentProcess because spawn fails BEFORE
14523
14763
  // ap exists and the rate-window must persist across stop/respawn (else stop/start churn
14524
14764
  // bypasses the cap — CC1 lifecycle pattern). Never advances any consume cursor or model-seen
@@ -14602,6 +14842,17 @@ var AgentProcessManager = class _AgentProcessManager {
14602
14842
  reset_source: resetSource
14603
14843
  });
14604
14844
  }
14845
+ resetRuntimeErrorFingerprintFenceIfNonresident(agentId, resetSource, ap) {
14846
+ if (this.agents.has(agentId)) return;
14847
+ if (this.lifecycleRecords.getRestartSnapshot(agentId)) return;
14848
+ if (this.lifecycleRecords.getTerminalFailure(agentId)) return;
14849
+ this.resetRuntimeErrorFingerprintFence(agentId, resetSource, ap);
14850
+ }
14851
+ repairNonresidentRuntimeErrorFingerprintFences(context) {
14852
+ for (const agentId of [...this.lifecycleRecords.runtimeErrorFingerprintFenceAgentIds()]) {
14853
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, `invariant_repair:${context}`);
14854
+ }
14855
+ }
14605
14856
  formatRuntimeErrorFingerprintFenceDetail(state) {
14606
14857
  return [
14607
14858
  `Runtime stopped after ${state.attempts} repeated runtime errors with the same fingerprint (${state.fingerprint}).`,
@@ -14660,14 +14911,22 @@ var AgentProcessManager = class _AgentProcessManager {
14660
14911
  this.sendStdinNotification(agentId);
14661
14912
  }, delayMs);
14662
14913
  }
14663
- flushAsyncRejectedIdleDelivery(agentId) {
14914
+ scheduleIdleInboxDeliveryRetry(agentId, ap, notificationCount, source, traceName) {
14915
+ if (notificationCount <= 0 || !ap.driver.supportsStdinNotification || !ap.sessionId) return false;
14916
+ ap.notifications.add(notificationCount);
14917
+ ap.notifications.clearTimer();
14918
+ return ap.notifications.schedule(() => {
14919
+ this.flushIdleInboxDeliveryRetry(agentId, source, traceName);
14920
+ }, this.stdinNotificationRetryMs);
14921
+ }
14922
+ flushIdleInboxDeliveryRetry(agentId, source, traceName) {
14664
14923
  const ap = this.agents.get(agentId);
14665
14924
  if (!ap) return false;
14666
14925
  const count = ap.notifications.takePendingAndClearTimer();
14667
14926
  if (count === 0) return false;
14668
14927
  if (!ap.driver.supportsStdinNotification || !ap.sessionId || ap.inbox.length === 0) {
14669
14928
  ap.notifications.add(count);
14670
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14929
+ this.recordDaemonTrace(traceName, {
14671
14930
  agentId,
14672
14931
  runtime: ap.config.runtime,
14673
14932
  model: ap.config.model,
@@ -14688,7 +14947,7 @@ var AgentProcessManager = class _AgentProcessManager {
14688
14947
  ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
14689
14948
  const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
14690
14949
  if (messages.length === 0) {
14691
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14950
+ this.recordDaemonTrace(traceName, {
14692
14951
  agentId,
14693
14952
  runtime: ap.config.runtime,
14694
14953
  model: ap.config.model,
@@ -14702,16 +14961,16 @@ var AgentProcessManager = class _AgentProcessManager {
14702
14961
  return false;
14703
14962
  }
14704
14963
  this.commitApmIdleState(agentId, ap, false);
14705
- this.startRuntimeTrace(agentId, ap, "async-rejected-idle-delivery-retry", messages);
14964
+ this.startRuntimeTrace(agentId, ap, source.replaceAll("_", "-"), messages);
14706
14965
  this.broadcastMessageReceivedActivity(agentId);
14707
14966
  const accepted = this.deliverInboxUpdateViaStdin(
14708
14967
  agentId,
14709
14968
  ap,
14710
14969
  messages,
14711
14970
  "idle",
14712
- "async_rejected_idle_delivery_retry"
14971
+ source
14713
14972
  );
14714
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14973
+ this.recordDaemonTrace(traceName, {
14715
14974
  agentId,
14716
14975
  runtime: ap.config.runtime,
14717
14976
  model: ap.config.model,
@@ -14724,6 +14983,13 @@ var AgentProcessManager = class _AgentProcessManager {
14724
14983
  });
14725
14984
  return accepted;
14726
14985
  }
14986
+ flushAsyncRejectedIdleDelivery(agentId) {
14987
+ return this.flushIdleInboxDeliveryRetry(
14988
+ agentId,
14989
+ "async_rejected_idle_delivery_retry",
14990
+ "daemon.agent.stdin_delivery.async_rejected.retry"
14991
+ );
14992
+ }
14727
14993
  isApmIdle(ap) {
14728
14994
  return ap.gatedSteering.isIdle;
14729
14995
  }
@@ -14769,7 +15035,7 @@ var AgentProcessManager = class _AgentProcessManager {
14769
15035
  this.broadcastActivity(agentId, "working", "Message received", [], void 0, "message_received");
14770
15036
  }
14771
15037
  toolActivityDetailKind(toolName) {
14772
- switch (toolName) {
15038
+ switch (resolveToolSemantic(toolName) ?? toolName) {
14773
15039
  case "bash":
14774
15040
  return "running_command";
14775
15041
  case "check_messages":
@@ -14787,6 +15053,7 @@ var AgentProcessManager = class _AgentProcessManager {
14787
15053
  if (options.includeCompactionWatchdog ?? true) {
14788
15054
  this.clearCompactionWatchdog(ap);
14789
15055
  }
15056
+ this.clearReviewWatchdog(ap);
14790
15057
  this.clearStalledRecoverySigtermWatchdog(ap);
14791
15058
  }
14792
15059
  clearRuntimeErrorDeliveryBackoffWithTrace(agentId, ap, resetSource) {
@@ -15119,7 +15386,11 @@ var AgentProcessManager = class _AgentProcessManager {
15119
15386
  path: input.pathname,
15120
15387
  query_keys: input.queryKeys,
15121
15388
  error_name: input.errorName,
15122
- error_message: input.errorMessage
15389
+ error_message: input.errorMessage,
15390
+ response_status_code: input.responseStatusCode,
15391
+ response_code: input.responseCode,
15392
+ lifecycle_invalid_agent_id: input.lifecycleInvalidAgentId,
15393
+ lifecycle_invalid_context: input.lifecycleInvalidContext
15123
15394
  }, "error");
15124
15395
  }
15125
15396
  recordAgentProxyTransportNormalizedError(agentId, input) {
@@ -15158,7 +15429,8 @@ var AgentProcessManager = class _AgentProcessManager {
15158
15429
  detailKind: activity.statusEntry.detailKind ?? "daemon_activity",
15159
15430
  entries: activity.entries,
15160
15431
  launchId: ap?.launchId || void 0,
15161
- clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
15432
+ clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0,
15433
+ isHeartbeat: false
15162
15434
  });
15163
15435
  }
15164
15436
  recordRuntimeDiagnosticActivity(agentId, ap, event) {
@@ -15171,7 +15443,8 @@ var AgentProcessManager = class _AgentProcessManager {
15171
15443
  detailKind: ap.lastActivityDetailKind || "none",
15172
15444
  entries: [runtimeDiagnosticTrajectoryEntry(event)],
15173
15445
  launchId: ap.launchId || void 0,
15174
- clientSeq: this.nextActivityClientSeq(agentId)
15446
+ clientSeq: this.nextActivityClientSeq(agentId),
15447
+ isHeartbeat: false
15175
15448
  });
15176
15449
  this.recordDaemonTrace("daemon.runtime.diagnostic", {
15177
15450
  agentId,
@@ -15180,6 +15453,38 @@ var AgentProcessManager = class _AgentProcessManager {
15180
15453
  ...runtimeDiagnosticTraceAttrs(event)
15181
15454
  });
15182
15455
  }
15456
+ /**
15457
+ * APM 1.6 6a — surface a generic structured "working" signal from an
15458
+ * internal_progress liveness tick during a long turn. Only transitions INTO
15459
+ * the runtime-progress state once (idempotent while already in it) so the
15460
+ * high-frequency Claude partial-message stream does not spam a rendered status
15461
+ * line every tick; the activity heartbeat then keeps the signal live. Carries
15462
+ * no raw content — a bounded "Working" detail only. Never marks a subagent.
15463
+ */
15464
+ maybeBroadcastRuntimeProgressActivity(agentId, ap) {
15465
+ const alreadyLive = ap.lastActivityKind === "working" || ap.lastActivityKind === "thinking";
15466
+ if (alreadyLive) return;
15467
+ this.broadcastActivity(agentId, "working", "Working", [], void 0, "runtime_progress");
15468
+ }
15469
+ recordSubagentProgressActivity(agentId, ap, event) {
15470
+ this.flushPendingTrajectory(agentId);
15471
+ this.broadcastActivity(
15472
+ agentId,
15473
+ "working",
15474
+ subagentProgressDetail(event),
15475
+ [],
15476
+ void 0,
15477
+ "subagent_activity",
15478
+ "working",
15479
+ subagentLineageFromEvent(event)
15480
+ );
15481
+ this.recordDaemonTrace("daemon.runtime.subagent.progress", {
15482
+ agentId,
15483
+ launchId: ap.launchId || void 0,
15484
+ runtime: ap.config.runtime,
15485
+ ...subagentProgressTraceAttrs(event)
15486
+ });
15487
+ }
15183
15488
  recordRuntimeRecoveryActivity(agentId, ap, event) {
15184
15489
  this.sendToServer({
15185
15490
  type: "agent:activity",
@@ -15190,7 +15495,8 @@ var AgentProcessManager = class _AgentProcessManager {
15190
15495
  detailKind: ap.lastActivityDetailKind || "none",
15191
15496
  entries: [runtimeRecoveryTrajectoryEntry(event)],
15192
15497
  launchId: ap.launchId || void 0,
15193
- clientSeq: this.nextActivityClientSeq(agentId)
15498
+ clientSeq: this.nextActivityClientSeq(agentId),
15499
+ isHeartbeat: false
15194
15500
  });
15195
15501
  this.recordDaemonTrace("daemon.runtime.recovery.visible", {
15196
15502
  agentId,
@@ -15416,7 +15722,10 @@ var AgentProcessManager = class _AgentProcessManager {
15416
15722
  rebindRunningStart(agentId, start, reason) {
15417
15723
  const ap = this.agents.get(agentId);
15418
15724
  if (!ap) {
15419
- this.lifecycleRecords.setPendingStartRebind(agentId, start);
15725
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15726
+ ...start,
15727
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15728
+ });
15420
15729
  return false;
15421
15730
  }
15422
15731
  const previousLaunchId = ap.launchId;
@@ -15466,7 +15775,16 @@ var AgentProcessManager = class _AgentProcessManager {
15466
15775
  ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages),
15467
15776
  reason: "already_starting"
15468
15777
  });
15469
- this.lifecycleRecords.setPendingStartRebind(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages });
15778
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15779
+ config,
15780
+ wakeMessage,
15781
+ unreadSummary,
15782
+ resumePrompt,
15783
+ launchId,
15784
+ wakeMessageTransient,
15785
+ resumeMessages,
15786
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15787
+ });
15470
15788
  logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
15471
15789
  return;
15472
15790
  }
@@ -15544,7 +15862,10 @@ var AgentProcessManager = class _AgentProcessManager {
15544
15862
  if (this.agents.has(item.agentId)) {
15545
15863
  this.rebindRunningStart(item.agentId, item, "already_running_or_starting");
15546
15864
  } else {
15547
- this.lifecycleRecords.setPendingStartRebind(item.agentId, item);
15865
+ this.lifecycleRecords.setPendingStartRebind(item.agentId, {
15866
+ ...item,
15867
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(item.agentId)
15868
+ });
15548
15869
  }
15549
15870
  logger.info(`[Agent ${item.agentId}] Queued start skipped (already running or starting)`);
15550
15871
  item.resolve();
@@ -15642,10 +15963,20 @@ var AgentProcessManager = class _AgentProcessManager {
15642
15963
  ...this.startQueueTraceAttrs(agentId, config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages),
15643
15964
  reason: "already_starting"
15644
15965
  });
15645
- this.lifecycleRecords.setPendingStartRebind(agentId, { config, wakeMessage, unreadSummary, resumePrompt, launchId, wakeMessageTransient, resumeMessages });
15966
+ this.lifecycleRecords.setPendingStartRebind(agentId, {
15967
+ config,
15968
+ wakeMessage,
15969
+ unreadSummary,
15970
+ resumePrompt,
15971
+ launchId,
15972
+ wakeMessageTransient,
15973
+ resumeMessages,
15974
+ stopEpochAtRebind: this.lifecycleRecords.stopEpoch(agentId)
15975
+ });
15646
15976
  logger.info(`[Agent ${agentId}] Start rebind deferred (startup in progress)`);
15647
15977
  return;
15648
15978
  }
15979
+ let startStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
15649
15980
  this.agentStarts.markStarting(agentId);
15650
15981
  let agentProcess = null;
15651
15982
  let pendingStartRebind;
@@ -15691,6 +16022,7 @@ var AgentProcessManager = class _AgentProcessManager {
15691
16022
  resumePrompt = pendingStartRebind.resumePrompt;
15692
16023
  launchId = pendingStartRebind.launchId || launchId;
15693
16024
  resumeMessages = pendingStartRebind.resumeMessages;
16025
+ startStopEpoch = pendingStartRebind.stopEpochAtRebind ?? startStopEpoch;
15694
16026
  if (pendingStartRebind.wakeMessage) {
15695
16027
  wakeMessage = pendingStartRebind.wakeMessage;
15696
16028
  wakeMessageTransient = pendingStartRebind.wakeMessageTransient === true;
@@ -15834,6 +16166,11 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
15834
16166
  this.agentStarts.clearStarting(agentId);
15835
16167
  this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "defer_until_concrete_message" });
15836
16168
  this.assertStartPendingDeliveryInvariants("defer-empty-start-drain");
16169
+ if (this.lifecycleRecords.stopEpochChanged(agentId, startStopEpoch)) {
16170
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16171
+ logger.info(`[Agent ${agentId}] Deferred ${driver.id} spawn suppressed by stop request`);
16172
+ return;
16173
+ }
15837
16174
  this.lifecycleRecords.setRestartSnapshot(agentId, {
15838
16175
  config: this.buildRestartSafeConfig(runtimeConfig, runtimeConfig.sessionId || null),
15839
16176
  sessionId: runtimeConfig.sessionId || null,
@@ -15896,6 +16233,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
15896
16233
  readinessTransition: null,
15897
16234
  activation: { kind: "idle" },
15898
16235
  compaction: { kind: "none" },
16236
+ review: { kind: "none" },
15899
16237
  runtimeProgress: new RuntimeProgressState(Date.now()),
15900
16238
  runtimeTraceSpan: null,
15901
16239
  runtimeTraceCounters: createRuntimeTraceCounters(),
@@ -15919,6 +16257,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
15919
16257
  };
15920
16258
  this.startingInboxes.drainOnSpawn(agentId);
15921
16259
  this.agents.set(agentId, agentProcess);
16260
+ if (this.lifecycleRecords.stopEpochChanged(agentId, startStopEpoch)) {
16261
+ await this.cleanupStoppedRuntimeStart(agentId, agentProcess);
16262
+ return;
16263
+ }
15922
16264
  this.lifecycleRecords.setRestartSnapshot(agentId, {
15923
16265
  config: this.buildRestartSafeConfig(runtimeConfig, restartSessionId),
15924
16266
  sessionId: restartSessionId,
@@ -16108,16 +16450,16 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16108
16450
  return;
16109
16451
  }
16110
16452
  if (processEndedCleanly) {
16453
+ const pendingRestartMessages = ap.inbox.splice(0).filter((message) => !this.isTransientDelivery(message));
16111
16454
  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;
16455
+ const bufferedRestartMessages = [];
16456
+ for (const message of pendingRestartMessages) {
16457
+ if (!queuedWakeMessage && !this.shouldDeferWakeMessage(agentId, ap.driver, message)) {
16458
+ queuedWakeMessage = message;
16459
+ } else {
16460
+ bufferedRestartMessages.push(message);
16118
16461
  }
16119
16462
  }
16120
- const unreadSummary2 = queuedWakeMessage ? buildUnreadSummary(ap.inbox, formatChannelLabel(queuedWakeMessage)) : void 0;
16121
16463
  if (queuedWakeMessage) {
16122
16464
  logger.info(`[Agent ${agentId}] Turn completed; restarting immediately for queued message`);
16123
16465
  const nextConfig = this.buildRestartSafeConfig(ap.config, ap.sessionId);
@@ -16131,7 +16473,12 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16131
16473
  if (expectedTerminationReason === "stalled_recovery") {
16132
16474
  this.lifecycleRecords.setPendingSpawnCause(agentId, "restart_stall");
16133
16475
  }
16134
- this.startAgent(agentId, nextConfig, queuedWakeMessage, unreadSummary2, void 0, ap.launchId || void 0).catch((err) => {
16476
+ const startPromise = this.startAgent(agentId, nextConfig, queuedWakeMessage, void 0, void 0, ap.launchId || void 0);
16477
+ if (bufferedRestartMessages.length > 0) {
16478
+ this.startingInboxes.bufferMessagesDuringStart(agentId, bufferedRestartMessages);
16479
+ this.assertStartPendingDeliveryInvariants("clean-exit-pending-inbox-transfer");
16480
+ }
16481
+ startPromise.catch((err) => {
16135
16482
  logger.error(`[Agent ${agentId}] Failed to continue with queued message`, err);
16136
16483
  if (this.reportRunnerCredentialMintFailure(agentId, err, ap.launchId, "queued_continuation")) {
16137
16484
  this.lifecycleRecords.setRestartSnapshot(agentId, {
@@ -16187,9 +16534,11 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16187
16534
  logger.warn(`[Agent ${agentId}] Startup timeout cleanup completed (${reason})`);
16188
16535
  } else if (startupRequestErrorTermination) {
16189
16536
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
16537
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "startup_request_error_cleanup", ap);
16190
16538
  logger.warn(`[Agent ${agentId}] Startup request failure cleanup completed (${reason})`);
16191
16539
  } else {
16192
16540
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
16541
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "nonrecoverable_process_close", ap);
16193
16542
  logger.error(`[Agent ${agentId}] Process crashed (${reason}) \u2014 marking inactive`);
16194
16543
  this.sendAgentStatus(agentId, "inactive", ap.launchId);
16195
16544
  }
@@ -16278,6 +16627,47 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16278
16627
  throw err;
16279
16628
  }
16280
16629
  }
16630
+ async cleanupStoppedRuntimeStart(agentId, ap) {
16631
+ if (this.agents.get(agentId) !== ap) return;
16632
+ this.agentStarts.clearStarting(agentId);
16633
+ this.lifecycleRecords.deletePendingStartRebind(agentId);
16634
+ this.lifecycleRecords.deletePendingSpawnCause(agentId);
16635
+ this.lifecycleRecords.deleteRestartSnapshot(agentId);
16636
+ this.startingInboxes.cancelStart(agentId);
16637
+ ap.notifications.clearTimer();
16638
+ this.disposeAgentProcessTimers(ap);
16639
+ this.closeRuntimeReadinessTransition(ap, "terminal");
16640
+ this.closeActivationTransition(ap, "terminal");
16641
+ cleanupAgentCredentialProxy(agentId, ap.launchId);
16642
+ this.revokeManagedRunnerCredential(agentId, ap.config, ap.launchId);
16643
+ this.agents.delete(agentId);
16644
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16645
+ this.assertStartPendingDeliveryInvariants("runtime-start-stop-epoch-fence");
16646
+ this.runtimeExitTraceAttrs.set(ap.runtime, {
16647
+ stop_source: "explicit_request",
16648
+ stop_wait_requested: false,
16649
+ stop_silent: false,
16650
+ stop_epoch_fence: true
16651
+ });
16652
+ try {
16653
+ await ap.runtime.stop({ signal: "SIGTERM", reason: "explicit_request" });
16654
+ } catch (err) {
16655
+ const reason = err instanceof Error ? err.message : String(err);
16656
+ logger.warn(`[Agent ${agentId}] Failed to stop runtime suppressed by stop epoch: ${reason}`);
16657
+ }
16658
+ logger.info(`[Agent ${agentId}] Runtime start discarded after stop request`);
16659
+ }
16660
+ suppressFailedRestartAfterStop(agentId, stopEpochAtRestart, source) {
16661
+ if (!this.lifecycleRecords.stopEpochChanged(agentId, stopEpochAtRestart)) return false;
16662
+ this.lifecycleRecords.deleteRestartSnapshot(agentId);
16663
+ this.lifecycleRecords.deletePendingStartRebind(agentId);
16664
+ this.lifecycleRecords.deletePendingSpawnCause(agentId);
16665
+ this.startingInboxes.cancelStart(agentId);
16666
+ this.closeNoProcessResidency(agentId, "suppressed", { negativeEvidenceBucket: "explicit_stop" });
16667
+ this.assertStartPendingDeliveryInvariants(`${source}-stop-epoch-fence`);
16668
+ logger.info(`[Agent ${agentId}] Failed ${source} restart suppressed after stop request`);
16669
+ return true;
16670
+ }
16281
16671
  cleanupFailedRuntimeStart(agentId, ap, err) {
16282
16672
  if (!ap) return;
16283
16673
  if (this.agents.get(agentId) !== ap) return;
@@ -16298,6 +16688,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16298
16688
  if (this.lifecycleRecords.deleteTerminalFailure(agentId)) {
16299
16689
  this.closeNoProcessResidency(agentId, "terminal", { negativeEvidenceBucket: "runtime_start_failed" });
16300
16690
  }
16691
+ this.resetRuntimeErrorFingerprintFenceIfNonresident(agentId, "runtime_start_failed_cleanup", ap);
16301
16692
  }
16302
16693
  cleanupTerminalRuntimeFailure(agentId, ap, detail) {
16303
16694
  if (this.agents.get(agentId) !== ap) return;
@@ -16583,6 +16974,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16583
16974
  return messages.some((message) => !runtimeProfileNotificationFromMessage(message));
16584
16975
  }
16585
16976
  async stopAgent(agentId, { wait = false, silent = false } = {}) {
16977
+ this.lifecycleRecords.recordStop(agentId);
16586
16978
  this.cancelQueuedAgentStart(agentId, "stop requested");
16587
16979
  this.lifecycleRecords.deletePendingStartRebind(agentId);
16588
16980
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
@@ -16759,6 +17151,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16759
17151
  return true;
16760
17152
  }
16761
17153
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
17154
+ const restartStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
16762
17155
  this.recordDaemonTrace("daemon.agent.delivery.routed", this.deliveryTraceAttrs(agentId, message, {
16763
17156
  outcome: "auto_restart_from_idle",
16764
17157
  accepted: true,
@@ -16774,6 +17167,9 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16774
17167
  return true;
16775
17168
  }, (err) => {
16776
17169
  logger.error(`[Agent ${agentId}] Failed to auto-restart`, err);
17170
+ if (this.suppressFailedRestartAfterStop(agentId, restartStopEpoch, "idle-auto-restart")) {
17171
+ return false;
17172
+ }
16777
17173
  if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "idle_auto_restart")) {
16778
17174
  this.lifecycleRecords.setRestartSnapshot(agentId, cached);
16779
17175
  const report2 = this.recordSpawnFailure(agentId, "runner_credential_mint");
@@ -17428,12 +17824,23 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17428
17824
  return true;
17429
17825
  }
17430
17826
  this.lifecycleRecords.deleteRestartSnapshot(agentId);
17827
+ const restartStopEpoch = this.lifecycleRecords.stopEpoch(agentId);
17431
17828
  return this.startAgent(agentId, cached.config, message, void 0, void 0, cached.launchId || void 0).then(() => {
17432
17829
  this.resetSpawnFailBackoff(agentId);
17433
17830
  this.assertStartPendingDeliveryInvariants("runtime-profile-auto-restart-success");
17434
17831
  return true;
17435
17832
  }, (err) => {
17436
17833
  logger.error(`[Agent ${agentId}] Failed to auto-restart for runtime profile notification`, err);
17834
+ if (this.suppressFailedRestartAfterStop(agentId, restartStopEpoch, "runtime-profile-auto-restart")) {
17835
+ span.end("ok", {
17836
+ attrs: {
17837
+ outcome: "suppressed_after_stop",
17838
+ runtime: cached.config.runtime,
17839
+ launchId: cached.launchId || void 0
17840
+ }
17841
+ });
17842
+ return false;
17843
+ }
17437
17844
  if (this.reportRunnerCredentialMintFailure(agentId, err, cached.launchId, "runtime_profile_auto_restart")) {
17438
17845
  this.lifecycleRecords.setRestartSnapshot(agentId, cached);
17439
17846
  const report2 = this.recordSpawnFailure(agentId, "runner_credential_mint");
@@ -17574,7 +17981,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17574
17981
  return deleteWorkspaceDirectory(this.dataDir, directoryName);
17575
17982
  }
17576
17983
  // Workspace file browsing
17577
- async getFileTree(agentId, dirPath) {
17984
+ async getFileTree(agentId, dirPath, includeHidden = false) {
17578
17985
  const agentDir = path14.join(this.dataDir, agentId);
17579
17986
  try {
17580
17987
  await stat2(agentDir);
@@ -17587,9 +17994,16 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17587
17994
  if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
17588
17995
  return [];
17589
17996
  }
17997
+ const relativePath = path14.relative(agentDir, resolved);
17998
+ if (isWorkspaceNeverVisibleHiddenPath(relativePath)) {
17999
+ return [];
18000
+ }
18001
+ if (!includeHidden && isWorkspaceHiddenPath(relativePath)) {
18002
+ return [];
18003
+ }
17590
18004
  targetDir = resolved;
17591
18005
  }
17592
- return this.listDirectoryChildren(targetDir, agentDir);
18006
+ return this.listDirectoryChildren(targetDir, agentDir, includeHidden);
17593
18007
  }
17594
18008
  async readFile(agentId, filePath) {
17595
18009
  const agentDir = path14.join(this.dataDir, agentId);
@@ -17597,6 +18011,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17597
18011
  if (!resolved.startsWith(agentDir + path14.sep) && resolved !== agentDir) {
17598
18012
  throw new Error("Access denied");
17599
18013
  }
18014
+ const relativePath = path14.relative(agentDir, resolved);
18015
+ if (isWorkspaceNeverVisibleHiddenPath(relativePath) || isWorkspaceSecretFilePath(relativePath)) {
18016
+ throw new Error("Preview is disabled for sensitive workspace files");
18017
+ }
17600
18018
  const info = await stat2(resolved);
17601
18019
  if (info.isDirectory()) throw new Error("Cannot read a directory");
17602
18020
  const ext = path14.extname(resolved).toLowerCase();
@@ -17909,12 +18327,19 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17909
18327
  * should carry explicit reason/correlation/window attrs so the server no
17910
18328
  * longer infers lifecycle semantics from generic activity strings.
17911
18329
  */
17912
- broadcastActivity(agentId, activityKind, detail, extraTrajectory = [], launchIdOverride, detailKind = "other", activityDisplay = activityKind) {
18330
+ broadcastActivity(agentId, activityKind, detail, extraTrajectory = [], launchIdOverride, detailKind = "other", activityDisplay = activityKind, subagentLineage) {
17913
18331
  const ap = this.agents.get(agentId);
17914
18332
  const entries = [...extraTrajectory];
17915
18333
  const hasToolStart = entries.some((e) => e.kind === "tool_start");
17916
18334
  if (!hasToolStart) {
17917
- entries.push({ kind: "status", activity: activityKind, activityKind, detail, detailKind });
18335
+ entries.push({
18336
+ kind: "status",
18337
+ activity: activityKind,
18338
+ activityKind,
18339
+ detail,
18340
+ detailKind,
18341
+ ...subagentLineage ? { subagent: subagentLineage } : {}
18342
+ });
17918
18343
  }
17919
18344
  const launchId = launchIdOverride || ap?.launchId || void 0;
17920
18345
  const clientSeq = this.nextActivityClientSeq(agentId);
@@ -17929,7 +18354,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17929
18354
  entries,
17930
18355
  launchId,
17931
18356
  clientSeq,
17932
- producerFactId
18357
+ producerFactId,
18358
+ isHeartbeat: false
17933
18359
  });
17934
18360
  this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId);
17935
18361
  if (ap) {
@@ -17957,7 +18383,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
17957
18383
  detailKind: ap.lastActivityDetailKind,
17958
18384
  launchId: heartbeatLaunchId,
17959
18385
  clientSeq: heartbeatClientSeq,
17960
- producerFactId: heartbeatProducerFactId
18386
+ producerFactId: heartbeatProducerFactId,
18387
+ // The one knowing site: this timer re-broadcasts stale
18388
+ // lastActivity, so it declares its replay provenance.
18389
+ isHeartbeat: true
17961
18390
  });
17962
18391
  this.recordActivityProducedTrace(agentId, ap.lastActivityKind, ap.lastActivityDetail, ap.lastActivityDetailKind, [], ap, heartbeatLaunchId, heartbeatClientSeq, heartbeatProducerFactId);
17963
18392
  }, ACTIVITY_HEARTBEAT_MS);
@@ -18038,7 +18467,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18038
18467
  launchId,
18039
18468
  probeId,
18040
18469
  clientSeq,
18041
- producerFactId
18470
+ producerFactId,
18471
+ isHeartbeat: false
18042
18472
  });
18043
18473
  this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, [], ap, launchId, clientSeq, producerFactId);
18044
18474
  }
@@ -18050,13 +18480,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18050
18480
  ap.pendingTrajectory = null;
18051
18481
  const text = pending.text.length > MAX_TRAJECTORY_TEXT ? pending.text.slice(0, MAX_TRAJECTORY_TEXT) + "\u2026" : pending.text;
18052
18482
  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
- }
18483
+ const entry = pending.kind === "thinking" ? { kind: "thinking", text, ...pending.subagent ? { subagent: pending.subagent } : {} } : { kind: "text", text, ...pending.subagent ? { subagent: pending.subagent } : {} };
18484
+ this.broadcastActivity(agentId, "thinking", "", [entry], void 0, "none");
18058
18485
  }
18059
- queueTrajectoryText(agentId, kind, text) {
18486
+ queueTrajectoryText(agentId, kind, text, subagent) {
18060
18487
  const ap = this.agents.get(agentId);
18061
18488
  if (!ap) {
18062
18489
  this.recordDaemonTrace("daemon.agent.activity.skipped", {
@@ -18072,7 +18499,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18072
18499
  return;
18073
18500
  }
18074
18501
  const pending = ap.pendingTrajectory;
18075
- if (pending && pending.kind === kind) {
18502
+ const sameLineage = JSON.stringify(pending?.subagent ?? null) === JSON.stringify(subagent ?? null);
18503
+ if (pending && pending.kind === kind && sameLineage) {
18076
18504
  pending.text += text;
18077
18505
  clearTimeout(pending.timer);
18078
18506
  pending.timer = setTimeout(() => this.flushPendingTrajectory(agentId), TRAJECTORY_COALESCE_MS);
@@ -18085,6 +18513,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18085
18513
  ap.pendingTrajectory = {
18086
18514
  kind,
18087
18515
  text,
18516
+ ...subagent ? { subagent } : {},
18088
18517
  timer: setTimeout(() => this.flushPendingTrajectory(agentId), TRAJECTORY_COALESCE_MS)
18089
18518
  };
18090
18519
  }
@@ -18187,6 +18616,29 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18187
18616
  ap.compaction = { ...ap.compaction, watchdog: null };
18188
18617
  this.broadcastActivity(agentId, "working", "Context compaction still running; no finish event observed", [], void 0, "compaction_stale");
18189
18618
  }
18619
+ startReviewWatchdog(agentId, ap) {
18620
+ this.clearReviewWatchdog(ap);
18621
+ const startedAt = Date.now();
18622
+ const watchdog = setTimeout(() => {
18623
+ this.markReviewStale(agentId, startedAt);
18624
+ }, REVIEW_STALE_MS);
18625
+ ap.review = { kind: "active", startedAt, watchdog };
18626
+ }
18627
+ clearReviewWatchdog(ap) {
18628
+ if (ap.review.kind === "active" && ap.review.watchdog) {
18629
+ clearTimeout(ap.review.watchdog);
18630
+ }
18631
+ ap.review = { kind: "none" };
18632
+ }
18633
+ markReviewStale(agentId, startedAt) {
18634
+ const ap = this.agents.get(agentId);
18635
+ if (!ap || ap.review.kind !== "active" || ap.review.startedAt !== startedAt) return;
18636
+ ap.review = { ...ap.review, watchdog: null };
18637
+ this.broadcastActivity(agentId, "working", "Review mode still active; no finish event observed", [], void 0, "review_stale");
18638
+ const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_finished" });
18639
+ this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "review_finished", inferred: true });
18640
+ this.flushReviewBoundaryMessages(agentId, ap);
18641
+ }
18190
18642
  completeCompactionIfActive(agentId, detail = "Context compaction finished", options = {}) {
18191
18643
  const ap = this.agents.get(agentId);
18192
18644
  if (!ap || ap.compaction.kind !== "active") return;
@@ -18795,6 +19247,23 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18795
19247
  if (ap.runtimeProgress.isStale) return true;
18796
19248
  const staleForMs = ap.runtimeProgress.ageMs();
18797
19249
  if (staleForMs < RUNTIME_PROGRESS_STALE_MS) return false;
19250
+ const subprocessPidAlive = this.probeRuntimeProcessLiveness(ap);
19251
+ if (subprocessPidAlive === true) {
19252
+ this.recordDaemonTrace("daemon.runtime.stall.suppressed_alive", {
19253
+ ...this.processLifecycleIdentityAttrs(agentId, ap),
19254
+ last_event_kind: ap.lastActivityKind || void 0,
19255
+ last_event_age_ms_bucket: bucketMs(staleForMs),
19256
+ subprocess_pid_alive: true,
19257
+ subprocess_socket_alive: !ap.runtime.closed,
19258
+ daemon_connected_to_server: this.serverConnected()
19259
+ });
19260
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.progress.silent_alive", {
19261
+ staleForMs: bucketMs(staleForMs),
19262
+ lastActivity: ap.lastActivityKind || void 0,
19263
+ lastActivityDetailKind: ap.lastActivityDetailKind
19264
+ });
19265
+ return false;
19266
+ }
18798
19267
  ap.runtimeProgress.markStale();
18799
19268
  const staleForMinutes = Math.max(1, Math.floor(staleForMs / 6e4));
18800
19269
  const diagnostic = buildRuntimeStallDiagnostic(ap, staleForMs, staleForMinutes);
@@ -18814,15 +19283,6 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18814
19283
  ...runtimeTraceCounterAttrs(ap),
18815
19284
  ...this.finalizeRuntimeProfileTurnControl(agentId, ap, "runtime_stalled")
18816
19285
  });
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
19286
  this.recordDaemonTrace("daemon.runtime.stall.detected", {
18827
19287
  ...this.processLifecycleIdentityAttrs(agentId, ap),
18828
19288
  last_event_kind: ap.lastActivityKind || void 0,
@@ -18834,6 +19294,17 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18834
19294
  this.broadcastActivity(agentId, "error", diagnostic.detail, [], void 0, "runtime_stalled");
18835
19295
  return true;
18836
19296
  }
19297
+ probeRuntimeProcessLiveness(ap) {
19298
+ const pid = ap.runtime.pid;
19299
+ if (typeof pid !== "number") return void 0;
19300
+ try {
19301
+ process.kill(pid, 0);
19302
+ return true;
19303
+ } catch (err) {
19304
+ const code = typeof err === "object" && err !== null && "code" in err ? err.code : void 0;
19305
+ return code === "EPERM" ? true : false;
19306
+ }
19307
+ }
18837
19308
  recoverStaleProcessForQueuedMessageIfNeeded(agentId, ap) {
18838
19309
  const staleForMs = ap.runtimeProgress.ageMs();
18839
19310
  const reduction = reduceApmStalledRecoveryTermination(ap.gatedSteering, {
@@ -18944,6 +19415,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18944
19415
  source: event.source,
18945
19416
  itemType: event.itemType,
18946
19417
  payloadBytes: event.payloadBytes
19418
+ } : event.kind === "subagent_progress" ? {
19419
+ kind: event.kind,
19420
+ source: event.source,
19421
+ phase: event.phase,
19422
+ parent_tool_use_id_present: Boolean(event.parentToolUseId),
19423
+ subagent_type_present: Boolean(event.subagentType),
19424
+ task_id_present: Boolean(event.taskId),
19425
+ last_tool_name_present: Boolean(event.lastToolName),
19426
+ payloadBytes: event.payloadBytes
18947
19427
  } : event.kind === "runtime_diagnostic" ? runtimeDiagnosticTraceAttrs(event) : event.kind === "runtime_recovery" ? runtimeRecoveryTraceAttrs(event) : { kind: event.kind };
18948
19428
  this.recordRuntimeTraceEvent(agentId, ap, "runtime.event.received", eventAttrs);
18949
19429
  const recordProgressObservedAfterStall = () => {
@@ -18965,6 +19445,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18965
19445
  itemType: event.itemType,
18966
19446
  payloadBytes: event.payloadBytes
18967
19447
  });
19448
+ this.maybeBroadcastRuntimeProgressActivity(agentId, ap);
19449
+ return;
19450
+ }
19451
+ if (event.kind === "subagent_progress") {
19452
+ this.noteRuntimeProgress(ap, event.kind);
19453
+ recordProgressObservedAfterStall();
19454
+ this.invalidateRecoveryErrorView(ap);
19455
+ this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19456
+ this.recordSubagentProgressActivity(agentId, ap, event);
18968
19457
  return;
18969
19458
  }
18970
19459
  if (event.kind === "runtime_diagnostic") {
@@ -19001,7 +19490,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19001
19490
  break;
19002
19491
  case "thinking": {
19003
19492
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
19004
- this.queueTrajectoryText(agentId, "thinking", event.text);
19493
+ this.queueTrajectoryText(agentId, "thinking", event.text, event.subagent);
19005
19494
  if (ap) {
19006
19495
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19007
19496
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
@@ -19012,7 +19501,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19012
19501
  }
19013
19502
  case "text": {
19014
19503
  this.completeCompactionIfActive(agentId, "Context compaction finished (inferred from resumed output)");
19015
- this.queueTrajectoryText(agentId, "text", event.text);
19504
+ this.queueTrajectoryText(agentId, "text", event.text, event.subagent);
19016
19505
  if (ap) {
19017
19506
  this.clearRuntimeErrorDeliveryBackoffAfterProgress(agentId, ap, event.kind);
19018
19507
  const reduction = reduceApmGatedAssistantContinuation(ap.gatedSteering);
@@ -19039,8 +19528,9 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19039
19528
  this.broadcastActivity(agentId, "working", detail, [{
19040
19529
  kind: "tool_start",
19041
19530
  toolName: invocation.toolName,
19042
- toolInput: inputSummary
19043
- }], void 0, this.toolActivityDetailKind(invocation.toolName));
19531
+ toolInput: inputSummary,
19532
+ ...event.subagent ? { subagent: event.subagent } : {}
19533
+ }], void 0, event.subagent ? "subagent_activity" : this.toolActivityDetailKind(invocation.toolName));
19044
19534
  break;
19045
19535
  }
19046
19536
  case "tool_output": {
@@ -19088,11 +19578,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19088
19578
  if (ap) {
19089
19579
  const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_started" });
19090
19580
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "review_started" });
19581
+ this.startReviewWatchdog(agentId, ap);
19091
19582
  }
19092
19583
  break;
19093
19584
  case "review_finished":
19094
19585
  this.flushPendingTrajectory(agentId);
19095
- if (ap) this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_mode.finished");
19586
+ if (ap) {
19587
+ this.clearReviewWatchdog(ap);
19588
+ this.recordRuntimeTraceEvent(agentId, ap, "runtime.review_mode.finished");
19589
+ }
19096
19590
  this.broadcastActivity(agentId, "working", "Review finished", [], void 0, "review_finished");
19097
19591
  if (ap) {
19098
19592
  const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_finished" });
@@ -19613,6 +20107,15 @@ ${RESPONSE_TARGET_HINT}`;
19613
20107
  `${mode} inbox update`
19614
20108
  );
19615
20109
  if (!sendResult.ok) {
20110
+ const retryNotificationCount = mode === "idle" && ap.driver.supportsStdinNotification && ap.sessionId ? messages.length : 0;
20111
+ const retrySource = source.endsWith("_retry") ? source : `${source}_retry`;
20112
+ const retryScheduled = mode === "idle" ? this.scheduleIdleInboxDeliveryRetry(
20113
+ agentId,
20114
+ ap,
20115
+ retryNotificationCount,
20116
+ retrySource,
20117
+ "daemon.agent.stdin_delivery.idle_retry"
20118
+ ) : false;
19616
20119
  if (mode === "idle") {
19617
20120
  this.commitApmIdleState(agentId, ap, true);
19618
20121
  }
@@ -19637,7 +20140,9 @@ ${RESPONSE_TARGET_HINT}`;
19637
20140
  outcome: runtimeSendFailureOutcome(sendResult),
19638
20141
  failure_reason: sendResult.reason,
19639
20142
  failure_error: sendResult.error,
19640
- requeued_messages_count: 0,
20143
+ requeued_messages_count: retryNotificationCount,
20144
+ retry_scheduled: retryScheduled,
20145
+ notification_timer_present: ap.notifications.hasTimer,
19641
20146
  cursors_advanced: "none"
19642
20147
  }, "error");
19643
20148
  return false;
@@ -19779,7 +20284,7 @@ ${RESPONSE_TARGET_HINT}`);
19779
20284
  return true;
19780
20285
  }
19781
20286
  /** List ONE level of a directory — directories returned without children (lazy-loaded on demand) */
19782
- async listDirectoryChildren(dir, rootDir) {
20287
+ async listDirectoryChildren(dir, rootDir, includeHidden = false) {
19783
20288
  let entries;
19784
20289
  try {
19785
20290
  entries = await readdir2(dir, { withFileTypes: true });
@@ -19793,7 +20298,9 @@ ${RESPONSE_TARGET_HINT}`);
19793
20298
  });
19794
20299
  const nodes = [];
19795
20300
  for (const entry of entries) {
19796
- if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
20301
+ const isHidden = entry.name.startsWith(".");
20302
+ if (entry.name === "node_modules") continue;
20303
+ if (isHidden && (!includeHidden || isWorkspaceNeverVisibleHiddenEntry(entry.name))) continue;
19797
20304
  const fullPath = path14.join(dir, entry.name);
19798
20305
  const relativePath = path14.relative(rootDir, fullPath);
19799
20306
  let info;
@@ -19803,9 +20310,9 @@ ${RESPONSE_TARGET_HINT}`);
19803
20310
  continue;
19804
20311
  }
19805
20312
  if (entry.isDirectory()) {
19806
- nodes.push({ name: entry.name, path: relativePath, isDirectory: true, size: 0, modifiedAt: info.mtime.toISOString() });
20313
+ nodes.push({ name: entry.name, path: relativePath, isDirectory: true, size: 0, modifiedAt: info.mtime.toISOString(), isHidden });
19807
20314
  } else {
19808
- nodes.push({ name: entry.name, path: relativePath, isDirectory: false, size: info.size, modifiedAt: info.mtime.toISOString() });
20315
+ nodes.push({ name: entry.name, path: relativePath, isDirectory: false, size: info.size, modifiedAt: info.mtime.toISOString(), isHidden });
19809
20316
  }
19810
20317
  }
19811
20318
  return nodes;
@@ -19820,6 +20327,14 @@ var systemClock = {
19820
20327
  clearTimeout: (timer) => clearTimeout(timer)
19821
20328
  };
19822
20329
  var INBOUND_WATCHDOG_MS = 7e4;
20330
+ function normalizeHandshakeReason(value) {
20331
+ const raw = Array.isArray(value) ? value[0] : value;
20332
+ if (typeof raw !== "string") return null;
20333
+ const trimmed = raw.trim();
20334
+ if (!trimmed) return null;
20335
+ if (!/^[a-z0-9_:-]{1,80}$/i.test(trimmed)) return "invalid_header";
20336
+ return trimmed;
20337
+ }
19823
20338
  function durationMsBucket(ms) {
19824
20339
  if (ms == null || !Number.isFinite(ms) || ms < 0) return "unknown";
19825
20340
  if (ms === 0) return "0";
@@ -19843,6 +20358,7 @@ var DaemonConnection = class {
19843
20358
  lastDroppedSendLogAt = 0;
19844
20359
  lastInboundAt = null;
19845
20360
  lastInboundMessageKind = null;
20361
+ inboundProbeInFlight = false;
19846
20362
  pendingActivityByAgent = /* @__PURE__ */ new Map();
19847
20363
  latestObservedLaunchIdByAgent = /* @__PURE__ */ new Map();
19848
20364
  constructor(options) {
@@ -19911,6 +20427,7 @@ var DaemonConnection = class {
19911
20427
  });
19912
20428
  const ws = this.options.wsFactory ? this.options.wsFactory(wsUrl, wsOptions) : new WebSocket(wsUrl, wsOptions);
19913
20429
  this.ws = ws;
20430
+ let handshakeRejected = false;
19914
20431
  ws.on("open", () => {
19915
20432
  if (this.ws !== ws) return;
19916
20433
  if (!this.shouldConnect) return;
@@ -19971,8 +20488,27 @@ var DaemonConnection = class {
19971
20488
  this.options.onDisconnect();
19972
20489
  this.scheduleReconnect();
19973
20490
  });
20491
+ ws.on("unexpected-response", (_request, response) => {
20492
+ if (this.ws !== ws) return;
20493
+ handshakeRejected = true;
20494
+ const reason = normalizeHandshakeReason(response.headers["slock-reason"]);
20495
+ const statusCode = response.statusCode ?? 0;
20496
+ const reasonText = reason ? `, slock_reason=${reason}` : "";
20497
+ logger.error(`[Daemon] WebSocket handshake rejected (status=${statusCode}${reasonText})`);
20498
+ this.trace("daemon.connection.handshake_rejected", {
20499
+ status_code: statusCode,
20500
+ slock_reason_present: Boolean(reason),
20501
+ slock_reason: reason
20502
+ }, "error");
20503
+ response.resume();
20504
+ try {
20505
+ ws.terminate();
20506
+ } catch {
20507
+ }
20508
+ });
19974
20509
  ws.on("error", (err) => {
19975
20510
  if (this.ws !== ws) return;
20511
+ if (handshakeRejected) return;
19976
20512
  logger.error(`[Daemon] WebSocket error: ${err.message}`);
19977
20513
  this.trace("daemon.connection.error", {
19978
20514
  error_class: err.name || "Error"
@@ -19996,11 +20532,44 @@ var DaemonConnection = class {
19996
20532
  }
19997
20533
  resetWatchdog() {
19998
20534
  this.clearWatchdog();
20535
+ this.inboundProbeInFlight = false;
20536
+ this.scheduleWatchdogDeadline();
20537
+ }
20538
+ scheduleWatchdogDeadline() {
19999
20539
  const ms = this.options.inboundWatchdogMs ?? INBOUND_WATCHDOG_MS;
20000
20540
  this.watchdogTimer = this.clock.setTimeout(() => {
20001
- logger.warn(`[Daemon] No inbound traffic for ${ms / 1e3}s \u2014 forcing reconnect`);
20541
+ if (!this.inboundProbeInFlight && this.ws?.readyState === WebSocket.OPEN) {
20542
+ this.inboundProbeInFlight = true;
20543
+ logger.info(`[Daemon] No inbound traffic for ${ms / 1e3}s \u2014 sending liveness probe`);
20544
+ this.trace("daemon.connection.inbound_probe_sent", {
20545
+ inbound_watchdog_ms: ms,
20546
+ last_inbound_message_kind: this.lastInboundMessageKind,
20547
+ last_inbound_age_ms_bucket: this.lastInboundAgeBucket(),
20548
+ ws_ready_state: this.ws.readyState,
20549
+ reconnecting: this.shouldConnect
20550
+ });
20551
+ try {
20552
+ this.ws.send(JSON.stringify({ type: "ping" }));
20553
+ } catch (err) {
20554
+ this.trace("daemon.connection.inbound_probe_send_failed", {
20555
+ inbound_watchdog_ms: ms,
20556
+ error_class: err instanceof Error ? err.name : typeof err,
20557
+ ws_ready_state: this.ws?.readyState ?? null,
20558
+ reconnecting: this.shouldConnect
20559
+ }, "error");
20560
+ try {
20561
+ this.ws?.terminate();
20562
+ } catch {
20563
+ }
20564
+ return;
20565
+ }
20566
+ this.scheduleWatchdogDeadline();
20567
+ return;
20568
+ }
20569
+ logger.warn(`[Daemon] No inbound traffic after liveness probe \u2014 forcing reconnect`);
20002
20570
  this.trace("daemon.connection.watchdog_timeout", {
20003
20571
  inbound_watchdog_ms: ms,
20572
+ probe_in_flight: this.inboundProbeInFlight,
20004
20573
  last_inbound_message_kind: this.lastInboundMessageKind,
20005
20574
  last_inbound_age_ms_bucket: this.lastInboundAgeBucket(),
20006
20575
  ws_ready_state: this.ws?.readyState ?? null,
@@ -20957,7 +21526,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
20957
21526
  spanAttrs: ["agentId", "event_kind", "runtime"]
20958
21527
  }
20959
21528
  };
20960
- var DAEMON_CLI_USAGE = "Usage: slock-daemon --server-url <url> --api-key <key>";
21529
+ var DAEMON_CLI_USAGE = `Usage: slock-daemon --server-url <url> (--api-key <key> or ${DAEMON_API_KEY_ENV}=<key>)`;
20961
21530
  var RunnerCredentialMintError2 = class extends Error {
20962
21531
  code;
20963
21532
  retryable;
@@ -20993,9 +21562,9 @@ function runnerCredentialErrorDetail2(error) {
20993
21562
  async function waitForRunnerCredentialRetry2() {
20994
21563
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
20995
21564
  }
20996
- function parseDaemonCliArgs(args) {
21565
+ function parseDaemonCliArgs(args, env = {}) {
20997
21566
  let serverUrl = "";
20998
- let apiKey = "";
21567
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
20999
21568
  for (let i = 0; i < args.length; i++) {
21000
21569
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
21001
21570
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -21032,7 +21601,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
21032
21601
  }
21033
21602
  async function runBundledSlockCli(argv) {
21034
21603
  process.argv = [process.execPath, "slock", ...argv];
21035
- await import("./dist-TGKVYIA7.js");
21604
+ await import("./dist-IHHGSWH2.js");
21036
21605
  }
21037
21606
  function detectRuntimes(tracer = noopTracer) {
21038
21607
  const ids = [];
@@ -21144,7 +21713,7 @@ function summarizeIncomingMessage(msg) {
21144
21713
  case "agent:runtime_profile:daemon_release_notice":
21145
21714
  return `(agent=${msg.agentId}, notice=${msg.noticeKey})`;
21146
21715
  case "agent:workspace:list":
21147
- return `(agent=${msg.agentId}, dir=${msg.dirPath || "."})`;
21716
+ return `(agent=${msg.agentId}, dir=${msg.dirPath || "."}, hidden=${msg.includeHidden ? "yes" : "no"})`;
21148
21717
  case "agent:workspace:read":
21149
21718
  return `(agent=${msg.agentId}, path=${msg.path})`;
21150
21719
  case "agent:skills:list":
@@ -21669,8 +22238,8 @@ var DaemonCore = class {
21669
22238
  break;
21670
22239
  }
21671
22240
  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 });
22241
+ this.agentManager.getFileTree(msg.agentId, msg.dirPath, Boolean(msg.includeHidden)).then((files) => {
22242
+ this.connection.send({ type: "agent:workspace:file_tree", agentId: msg.agentId, files, dirPath: msg.dirPath, includeHidden: Boolean(msg.includeHidden) });
21674
22243
  });
21675
22244
  break;
21676
22245
  case "agent:workspace:read":
@@ -21974,6 +22543,8 @@ var DaemonCore = class {
21974
22543
 
21975
22544
  export {
21976
22545
  subscribeDaemonLogs,
22546
+ DAEMON_API_KEY_ENV,
22547
+ scrubDaemonAuthEnv,
21977
22548
  resolveWorkspaceDirectoryPath,
21978
22549
  scanWorkspaceDirectories,
21979
22550
  deleteWorkspaceDirectory,