@botiverse/raft-daemon 0.68.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",
@@ -4708,6 +4709,19 @@ function listLegacySlockStatePaths(slockHome = resolveSlockHome(), homeDir = os.
4708
4709
  return candidates.filter((candidate) => existsSync(candidate.path));
4709
4710
  }
4710
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
+
4711
4725
  // src/agentCredentialProxy.ts
4712
4726
  import { randomBytes } from "crypto";
4713
4727
  import http from "http";
@@ -6282,7 +6296,9 @@ var LOOPBACK_NO_PROXY = "127.0.0.1,localhost";
6282
6296
  var CLI_TRANSPORT_TRACE_DIR_ENV = "SLOCK_CLI_TRANSPORT_TRACE_DIR";
6283
6297
  var safePathPart = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_");
6284
6298
  var RAW_CREDENTIAL_ENV_DENYLIST = [
6285
- "SLOCK_AGENT_CREDENTIAL_KEY"
6299
+ "SLOCK_AGENT_TOKEN",
6300
+ "SLOCK_AGENT_CREDENTIAL_KEY",
6301
+ "SLOCK_AGENT_CREDENTIAL_KEY_FILE"
6286
6302
  ];
6287
6303
  var WORKSPACE_CLI_TRANSPORT_FILENAMES = [
6288
6304
  "agent-token",
@@ -6637,7 +6653,7 @@ set "SLOCK_AGENT_ACTIVE_CAPABILITIES=${DEFAULT_ACTIVE_CAPABILITIES}"\r
6637
6653
  SLOCK_SERVER_URL: ctx.config.serverUrl,
6638
6654
  PATH: `${slockDir}${path2.delimiter}${process.env.PATH ?? ""}`
6639
6655
  };
6640
- delete spawnEnv.SLOCK_AGENT_TOKEN;
6656
+ scrubDaemonChildEnv(spawnEnv);
6641
6657
  for (const key of RAW_CREDENTIAL_ENV_DENYLIST) {
6642
6658
  delete spawnEnv[key];
6643
6659
  }
@@ -7205,7 +7221,7 @@ function requiresWindowsShell(command, platform = process.platform) {
7205
7221
  }
7206
7222
  function resolveCommandOnPath(command, deps = {}) {
7207
7223
  const platform = deps.platform ?? process.platform;
7208
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7224
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7209
7225
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7210
7226
  const existsSyncFn = deps.existsSyncFn ?? existsSync3;
7211
7227
  if (platform === "win32") {
@@ -7231,7 +7247,7 @@ function firstExistingPath(candidates, deps = {}) {
7231
7247
  return null;
7232
7248
  }
7233
7249
  function readCommandVersion(command, args = [], deps = {}) {
7234
- const env = withWindowsUserEnvironment(deps.env ?? process.env, deps);
7250
+ const env = scrubDaemonChildEnv({ ...withWindowsUserEnvironment(deps.env ?? process.env, deps) });
7235
7251
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync;
7236
7252
  try {
7237
7253
  const output = normalizeExecOutput(execFileSyncFn(command, [...args, "--version"], {
@@ -9315,11 +9331,11 @@ function detectCursorModels(runCommand = runCursorModelsCommand) {
9315
9331
  return parseCursorModelsOutput(String(result.stdout || ""));
9316
9332
  }
9317
9333
  function buildCursorModelProbeEnv(deps = {}) {
9318
- return withWindowsUserEnvironment({
9334
+ return scrubDaemonChildEnv(withWindowsUserEnvironment({
9319
9335
  ...deps.env ?? process.env,
9320
9336
  FORCE_COLOR: "0",
9321
9337
  NO_COLOR: "1"
9322
- }, deps);
9338
+ }, deps));
9323
9339
  }
9324
9340
  function runCursorModelsCommand() {
9325
9341
  return spawnSync("cursor-agent", ["models"], {
@@ -9375,7 +9391,7 @@ function resolveGeminiSpawn(commandArgs, deps = {}) {
9375
9391
  }
9376
9392
  const execFileSyncFn = deps.execFileSyncFn ?? execFileSync3;
9377
9393
  const existsSyncFn = deps.existsSyncFn ?? existsSync5;
9378
- const env = deps.env ?? process.env;
9394
+ const env = scrubDaemonChildEnv({ ...deps.env ?? process.env });
9379
9395
  const winPath = path8.win32;
9380
9396
  let geminiEntry = null;
9381
9397
  try {
@@ -9512,12 +9528,15 @@ var GeminiDriver = class {
9512
9528
  // src/drivers/kimi.ts
9513
9529
  import { randomUUID as randomUUID2 } from "crypto";
9514
9530
  import { spawn as spawn7 } from "child_process";
9515
- 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";
9516
9532
  import os4 from "os";
9517
9533
  import path9 from "path";
9518
9534
  var KIMI_WIRE_PROTOCOL_VERSION = "1.3";
9519
9535
  var KIMI_SYSTEM_PROMPT_FILE = ".slock-kimi-system.md";
9520
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";
9521
9540
  function parseToolArguments(raw) {
9522
9541
  if (typeof raw !== "string") return raw;
9523
9542
  try {
@@ -9526,6 +9545,73 @@ function parseToolArguments(raw) {
9526
9545
  return raw;
9527
9546
  }
9528
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
+ }
9529
9615
  function resolveKimiSpawn(commandArgs, deps = {}) {
9530
9616
  return {
9531
9617
  command: resolveCommandOnPath("kimi", deps) ?? "kimi",
@@ -9549,7 +9635,25 @@ var KimiDriver = class {
9549
9635
  };
9550
9636
  model = {
9551
9637
  detectedModelsVerifiedAs: "launchable",
9552
- 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
+ }
9553
9657
  };
9554
9658
  supportsStdinNotification = true;
9555
9659
  busyDeliveryMode = "direct";
@@ -9573,21 +9677,23 @@ var KimiDriver = class {
9573
9677
  ` system_prompt_path: ./${KIMI_SYSTEM_PROMPT_FILE}`,
9574
9678
  ""
9575
9679
  ].join("\n"), "utf8");
9680
+ const launch = buildKimiLaunchOptions(ctx);
9576
9681
  const args = [
9577
9682
  "--wire",
9578
9683
  "--yolo",
9579
9684
  "--agent-file",
9580
9685
  agentFilePath,
9581
9686
  "--session",
9582
- this.sessionId
9687
+ this.sessionId,
9688
+ ...launch.args
9583
9689
  ];
9584
9690
  const launchRuntimeFields = runtimeConfigToLaunchFields(hydrateRuntimeConfig(ctx.config));
9585
9691
  if (launchRuntimeFields.model && launchRuntimeFields.model !== "default") {
9586
9692
  args.push("--model", launchRuntimeFields.model);
9587
9693
  }
9588
9694
  const spawnEnv = (await prepareCliTransport(ctx, { NO_COLOR: "1" })).spawnEnv;
9589
- const launch = resolveKimiSpawn(args);
9590
- const proc = spawn7(launch.command, launch.args, {
9695
+ const spawnTarget = resolveKimiSpawn(args);
9696
+ const proc = spawn7(spawnTarget.command, spawnTarget.args, {
9591
9697
  cwd: ctx.workingDirectory,
9592
9698
  stdio: ["pipe", "pipe", "pipe"],
9593
9699
  env: spawnEnv,
@@ -9595,7 +9701,7 @@ var KimiDriver = class {
9595
9701
  // and has an 8191-character command-line limit. Kimi's official
9596
9702
  // installer/uv entrypoint is an executable, so launch it directly and
9597
9703
  // keep prompts on stdin / files instead of routing through cmd.exe.
9598
- shell: launch.shell
9704
+ shell: spawnTarget.shell
9599
9705
  });
9600
9706
  proc.stdin?.write(JSON.stringify({
9601
9707
  jsonrpc: "2.0",
@@ -9708,14 +9814,9 @@ var KimiDriver = class {
9708
9814
  return detectKimiModels();
9709
9815
  }
9710
9816
  };
9711
- function detectKimiModels(home = os4.homedir()) {
9712
- const configPath = path9.join(home, ".kimi", "config.toml");
9713
- let raw;
9714
- try {
9715
- raw = readFileSync3(configPath, "utf8");
9716
- } catch {
9717
- return null;
9718
- }
9817
+ function detectKimiModels(home = os4.homedir(), opts = {}) {
9818
+ const raw = readKimiConfigSource(home, opts.env).raw;
9819
+ if (raw === null) return null;
9719
9820
  const models = [];
9720
9821
  const sectionRe = /^\s*\[models(?:\.([^\]]+)|"\.[^"]+"|\."[^"]+")\s*\]\s*$/gm;
9721
9822
  const lineRe = /^\s*\[models\.(.+?)\s*\]\s*$/gm;
@@ -10381,7 +10482,7 @@ function runOpenCodeModelsCommand(home, deps = {}) {
10381
10482
  const platform = deps.platform ?? process.platform;
10382
10483
  const spawnSyncFn = deps.spawnSyncFn ?? spawnSync2;
10383
10484
  const result = spawnSyncFn("opencode", ["models"], {
10384
- env: { ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" },
10485
+ env: scrubDaemonChildEnv({ ...process.env, HOME: home, FORCE_COLOR: "0", NO_COLOR: "1" }),
10385
10486
  encoding: "utf8",
10386
10487
  timeout: 5e3,
10387
10488
  shell: platform === "win32"
@@ -13560,11 +13661,12 @@ var RUNTIME_ERROR_DELIVERY_BACKOFF_MAX_MS = 5 * 6e4;
13560
13661
  var RUNTIME_ERROR_FINGERPRINT_FENCE_THRESHOLD = 3;
13561
13662
  var SPAWN_FAIL_BACKOFF_BASE_MS = 1e3;
13562
13663
  var SPAWN_FAIL_BACKOFF_MAX_MS = 3e4;
13563
- var SPAWN_FAIL_BACKOFF_THRESHOLD = 3;
13664
+ var SPAWN_FAIL_BACKOFF_THRESHOLD = 0;
13564
13665
  var RUNNER_CREDENTIAL_MINT_BACKOFF_BASE_MS = 6e4;
13565
13666
  var RUNNER_CREDENTIAL_MINT_BACKOFF_MAX_MS = 10 * 6e4;
13566
13667
  var RUNNER_CREDENTIAL_MINT_BACKOFF_THRESHOLD = 0;
13567
13668
  var COMPACTION_STALE_MS = 5 * 6e4;
13669
+ var REVIEW_STALE_MS = 10 * 6e4;
13568
13670
  var RUNTIME_PROGRESS_STALE_MS = 15 * 6e4;
13569
13671
  var DEFAULT_RUNTIME_START_TIMEOUT_MS = 2 * 6e4;
13570
13672
  var DEFAULT_STALLED_RECOVERY_SIGTERM_TIMEOUT_MS = 1e4;
@@ -14653,10 +14755,10 @@ var AgentProcessManager = class _AgentProcessManager {
14653
14755
  }
14654
14756
  // ----- SPAWN-FAIL BACKOFF (per-agent) ----------------------------------
14655
14757
  // Anchored at auto_restart_from_idle (apm:3596) + runtime_profile_auto_restart (apm:4137).
14656
- // Generic spawn errors keep a threshold > 1 so single-transient hiccups behave as today.
14657
- // Runner credential mint failures are different: one outer spawn failure already means the
14658
- // inner credential-mint loop exhausted RUNNER_CREDENTIAL_MINT_MAX_ATTEMPTS. Those enter a
14659
- // 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.
14660
14762
  // Successful spawn resets state. State lives outside AgentProcess because spawn fails BEFORE
14661
14763
  // ap exists and the rate-window must persist across stop/respawn (else stop/start churn
14662
14764
  // bypasses the cap — CC1 lifecycle pattern). Never advances any consume cursor or model-seen
@@ -14809,14 +14911,22 @@ var AgentProcessManager = class _AgentProcessManager {
14809
14911
  this.sendStdinNotification(agentId);
14810
14912
  }, delayMs);
14811
14913
  }
14812
- 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) {
14813
14923
  const ap = this.agents.get(agentId);
14814
14924
  if (!ap) return false;
14815
14925
  const count = ap.notifications.takePendingAndClearTimer();
14816
14926
  if (count === 0) return false;
14817
14927
  if (!ap.driver.supportsStdinNotification || !ap.sessionId || ap.inbox.length === 0) {
14818
14928
  ap.notifications.add(count);
14819
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14929
+ this.recordDaemonTrace(traceName, {
14820
14930
  agentId,
14821
14931
  runtime: ap.config.runtime,
14822
14932
  model: ap.config.model,
@@ -14837,7 +14947,7 @@ var AgentProcessManager = class _AgentProcessManager {
14837
14947
  ap.notifications.pruneContributedToPending(ap.inbox, ap.sessionId);
14838
14948
  const messages = ap.notifications.filterUncontributedMessages(ap.inbox, ap.sessionId);
14839
14949
  if (messages.length === 0) {
14840
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14950
+ this.recordDaemonTrace(traceName, {
14841
14951
  agentId,
14842
14952
  runtime: ap.config.runtime,
14843
14953
  model: ap.config.model,
@@ -14851,16 +14961,16 @@ var AgentProcessManager = class _AgentProcessManager {
14851
14961
  return false;
14852
14962
  }
14853
14963
  this.commitApmIdleState(agentId, ap, false);
14854
- this.startRuntimeTrace(agentId, ap, "async-rejected-idle-delivery-retry", messages);
14964
+ this.startRuntimeTrace(agentId, ap, source.replaceAll("_", "-"), messages);
14855
14965
  this.broadcastMessageReceivedActivity(agentId);
14856
14966
  const accepted = this.deliverInboxUpdateViaStdin(
14857
14967
  agentId,
14858
14968
  ap,
14859
14969
  messages,
14860
14970
  "idle",
14861
- "async_rejected_idle_delivery_retry"
14971
+ source
14862
14972
  );
14863
- this.recordDaemonTrace("daemon.agent.stdin_delivery.async_rejected.retry", {
14973
+ this.recordDaemonTrace(traceName, {
14864
14974
  agentId,
14865
14975
  runtime: ap.config.runtime,
14866
14976
  model: ap.config.model,
@@ -14873,6 +14983,13 @@ var AgentProcessManager = class _AgentProcessManager {
14873
14983
  });
14874
14984
  return accepted;
14875
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
+ }
14876
14993
  isApmIdle(ap) {
14877
14994
  return ap.gatedSteering.isIdle;
14878
14995
  }
@@ -14936,6 +15053,7 @@ var AgentProcessManager = class _AgentProcessManager {
14936
15053
  if (options.includeCompactionWatchdog ?? true) {
14937
15054
  this.clearCompactionWatchdog(ap);
14938
15055
  }
15056
+ this.clearReviewWatchdog(ap);
14939
15057
  this.clearStalledRecoverySigtermWatchdog(ap);
14940
15058
  }
14941
15059
  clearRuntimeErrorDeliveryBackoffWithTrace(agentId, ap, resetSource) {
@@ -15311,7 +15429,8 @@ var AgentProcessManager = class _AgentProcessManager {
15311
15429
  detailKind: activity.statusEntry.detailKind ?? "daemon_activity",
15312
15430
  entries: activity.entries,
15313
15431
  launchId: ap?.launchId || void 0,
15314
- clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0
15432
+ clientSeq: ap ? this.nextActivityClientSeq(agentId) : void 0,
15433
+ isHeartbeat: false
15315
15434
  });
15316
15435
  }
15317
15436
  recordRuntimeDiagnosticActivity(agentId, ap, event) {
@@ -15324,7 +15443,8 @@ var AgentProcessManager = class _AgentProcessManager {
15324
15443
  detailKind: ap.lastActivityDetailKind || "none",
15325
15444
  entries: [runtimeDiagnosticTrajectoryEntry(event)],
15326
15445
  launchId: ap.launchId || void 0,
15327
- clientSeq: this.nextActivityClientSeq(agentId)
15446
+ clientSeq: this.nextActivityClientSeq(agentId),
15447
+ isHeartbeat: false
15328
15448
  });
15329
15449
  this.recordDaemonTrace("daemon.runtime.diagnostic", {
15330
15450
  agentId,
@@ -15375,7 +15495,8 @@ var AgentProcessManager = class _AgentProcessManager {
15375
15495
  detailKind: ap.lastActivityDetailKind || "none",
15376
15496
  entries: [runtimeRecoveryTrajectoryEntry(event)],
15377
15497
  launchId: ap.launchId || void 0,
15378
- clientSeq: this.nextActivityClientSeq(agentId)
15498
+ clientSeq: this.nextActivityClientSeq(agentId),
15499
+ isHeartbeat: false
15379
15500
  });
15380
15501
  this.recordDaemonTrace("daemon.runtime.recovery.visible", {
15381
15502
  agentId,
@@ -16112,6 +16233,7 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
16112
16233
  readinessTransition: null,
16113
16234
  activation: { kind: "idle" },
16114
16235
  compaction: { kind: "none" },
16236
+ review: { kind: "none" },
16115
16237
  runtimeProgress: new RuntimeProgressState(Date.now()),
16116
16238
  runtimeTraceSpan: null,
16117
16239
  runtimeTraceCounters: createRuntimeTraceCounters(),
@@ -18232,7 +18354,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18232
18354
  entries,
18233
18355
  launchId,
18234
18356
  clientSeq,
18235
- producerFactId
18357
+ producerFactId,
18358
+ isHeartbeat: false
18236
18359
  });
18237
18360
  this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, entries, ap, launchId, clientSeq, producerFactId);
18238
18361
  if (ap) {
@@ -18260,7 +18383,10 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18260
18383
  detailKind: ap.lastActivityDetailKind,
18261
18384
  launchId: heartbeatLaunchId,
18262
18385
  clientSeq: heartbeatClientSeq,
18263
- 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
18264
18390
  });
18265
18391
  this.recordActivityProducedTrace(agentId, ap.lastActivityKind, ap.lastActivityDetail, ap.lastActivityDetailKind, [], ap, heartbeatLaunchId, heartbeatClientSeq, heartbeatProducerFactId);
18266
18392
  }, ACTIVITY_HEARTBEAT_MS);
@@ -18341,7 +18467,8 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18341
18467
  launchId,
18342
18468
  probeId,
18343
18469
  clientSeq,
18344
- producerFactId
18470
+ producerFactId,
18471
+ isHeartbeat: false
18345
18472
  });
18346
18473
  this.recordActivityProducedTrace(agentId, activityKind, detail, detailKind, [], ap, launchId, clientSeq, producerFactId);
18347
18474
  }
@@ -18489,6 +18616,29 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
18489
18616
  ap.compaction = { ...ap.compaction, watchdog: null };
18490
18617
  this.broadcastActivity(agentId, "working", "Context compaction still running; no finish event observed", [], void 0, "compaction_stale");
18491
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
+ }
18492
18642
  completeCompactionIfActive(agentId, detail = "Context compaction finished", options = {}) {
18493
18643
  const ap = this.agents.get(agentId);
18494
18644
  if (!ap || ap.compaction.kind !== "active") return;
@@ -19428,11 +19578,15 @@ Use \`raft message read\` to catch up on the channels listed above, then stop. R
19428
19578
  if (ap) {
19429
19579
  const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_started" });
19430
19580
  this.commitGatedSteeringDecisionState(agentId, ap, reduction.nextState, { event: "review_started" });
19581
+ this.startReviewWatchdog(agentId, ap);
19431
19582
  }
19432
19583
  break;
19433
19584
  case "review_finished":
19434
19585
  this.flushPendingTrajectory(agentId);
19435
- 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
+ }
19436
19590
  this.broadcastActivity(agentId, "working", "Review finished", [], void 0, "review_finished");
19437
19591
  if (ap) {
19438
19592
  const reduction = reduceApmGatedReview(ap.gatedSteering, { kind: "review_finished" });
@@ -19953,6 +20107,15 @@ ${RESPONSE_TARGET_HINT}`;
19953
20107
  `${mode} inbox update`
19954
20108
  );
19955
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;
19956
20119
  if (mode === "idle") {
19957
20120
  this.commitApmIdleState(agentId, ap, true);
19958
20121
  }
@@ -19977,7 +20140,9 @@ ${RESPONSE_TARGET_HINT}`;
19977
20140
  outcome: runtimeSendFailureOutcome(sendResult),
19978
20141
  failure_reason: sendResult.reason,
19979
20142
  failure_error: sendResult.error,
19980
- requeued_messages_count: 0,
20143
+ requeued_messages_count: retryNotificationCount,
20144
+ retry_scheduled: retryScheduled,
20145
+ notification_timer_present: ap.notifications.hasTimer,
19981
20146
  cursors_advanced: "none"
19982
20147
  }, "error");
19983
20148
  return false;
@@ -20162,6 +20327,14 @@ var systemClock = {
20162
20327
  clearTimeout: (timer) => clearTimeout(timer)
20163
20328
  };
20164
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
+ }
20165
20338
  function durationMsBucket(ms) {
20166
20339
  if (ms == null || !Number.isFinite(ms) || ms < 0) return "unknown";
20167
20340
  if (ms === 0) return "0";
@@ -20254,6 +20427,7 @@ var DaemonConnection = class {
20254
20427
  });
20255
20428
  const ws = this.options.wsFactory ? this.options.wsFactory(wsUrl, wsOptions) : new WebSocket(wsUrl, wsOptions);
20256
20429
  this.ws = ws;
20430
+ let handshakeRejected = false;
20257
20431
  ws.on("open", () => {
20258
20432
  if (this.ws !== ws) return;
20259
20433
  if (!this.shouldConnect) return;
@@ -20314,8 +20488,27 @@ var DaemonConnection = class {
20314
20488
  this.options.onDisconnect();
20315
20489
  this.scheduleReconnect();
20316
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
+ });
20317
20509
  ws.on("error", (err) => {
20318
20510
  if (this.ws !== ws) return;
20511
+ if (handshakeRejected) return;
20319
20512
  logger.error(`[Daemon] WebSocket error: ${err.message}`);
20320
20513
  this.trace("daemon.connection.error", {
20321
20514
  error_class: err.name || "Error"
@@ -21333,7 +21526,7 @@ var DAEMON_CORE_TRACE_ATTR_CONTRACTS = {
21333
21526
  spanAttrs: ["agentId", "event_kind", "runtime"]
21334
21527
  }
21335
21528
  };
21336
- 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>)`;
21337
21530
  var RunnerCredentialMintError2 = class extends Error {
21338
21531
  code;
21339
21532
  retryable;
@@ -21369,9 +21562,9 @@ function runnerCredentialErrorDetail2(error) {
21369
21562
  async function waitForRunnerCredentialRetry2() {
21370
21563
  await new Promise((resolve) => setTimeout(resolve, RUNNER_CREDENTIAL_MINT_RETRY_DELAY_MS2));
21371
21564
  }
21372
- function parseDaemonCliArgs(args) {
21565
+ function parseDaemonCliArgs(args, env = {}) {
21373
21566
  let serverUrl = "";
21374
- let apiKey = "";
21567
+ let apiKey = env[DAEMON_API_KEY_ENV] ?? "";
21375
21568
  for (let i = 0; i < args.length; i++) {
21376
21569
  if (args[i] === "--server-url" && args[i + 1]) serverUrl = args[++i];
21377
21570
  if (args[i] === "--api-key" && args[i + 1]) apiKey = args[++i];
@@ -21408,7 +21601,7 @@ function resolveSlockCliPathOrEmpty(moduleUrl = import.meta.url) {
21408
21601
  }
21409
21602
  async function runBundledSlockCli(argv) {
21410
21603
  process.argv = [process.execPath, "slock", ...argv];
21411
- await import("./dist-Y3VRAU5E.js");
21604
+ await import("./dist-IHHGSWH2.js");
21412
21605
  }
21413
21606
  function detectRuntimes(tracer = noopTracer) {
21414
21607
  const ids = [];
@@ -22350,6 +22543,8 @@ var DaemonCore = class {
22350
22543
 
22351
22544
  export {
22352
22545
  subscribeDaemonLogs,
22546
+ DAEMON_API_KEY_ENV,
22547
+ scrubDaemonAuthEnv,
22353
22548
  resolveWorkspaceDirectoryPath,
22354
22549
  scanWorkspaceDirectories,
22355
22550
  deleteWorkspaceDirectory,
package/dist/cli/index.js CHANGED
@@ -44746,6 +44746,9 @@ init_esm_shims();
44746
44746
  // ../shared/src/agentScopes.ts
44747
44747
  init_esm_shims();
44748
44748
 
44749
+ // ../shared/src/docs/crossPageInvariants.ts
44750
+ init_esm_shims();
44751
+
44749
44752
  // ../shared/src/testing/failpoints.ts
44750
44753
  init_esm_shims();
44751
44754
  var NoopFailpointRegistry = class {
@@ -44853,6 +44856,7 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
44853
44856
  "compaction_stale",
44854
44857
  "reviewing_changes",
44855
44858
  "review_finished",
44859
+ "review_stale",
44856
44860
  "runtime_reconnecting",
44857
44861
  "runtime_error",
44858
44862
  "runtime_crashed",
@@ -51041,6 +51045,9 @@ function formatClaimResults(channel, data) {
51041
51045
  const msgShort = r.messageId ? r.messageId.slice(0, 8) : "";
51042
51046
  return `${label} (msg:${msgShort}): claimed`;
51043
51047
  }
51048
+ if (r.reason === "already claimed by you") {
51049
+ return `${label}: already claimed by you.`;
51050
+ }
51044
51051
  return `${label}: FAILED \u2014 ${r.reason || "already claimed"}. Do not work on this task unless an owner/admin explicitly redirects it to you.`;
51045
51052
  });
51046
51053
  const succeeded = data.results.filter((r) => r.success).length;
package/dist/core.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import {
2
+ DAEMON_API_KEY_ENV,
2
3
  DAEMON_CLI_USAGE,
3
4
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
4
5
  DaemonCore,
@@ -11,9 +12,11 @@ import {
11
12
  resolveWorkspaceDirectoryPath,
12
13
  runBundledSlockCli,
13
14
  scanWorkspaceDirectories,
15
+ scrubDaemonAuthEnv,
14
16
  subscribeDaemonLogs
15
- } from "./chunk-RXK2KSYW.js";
17
+ } from "./chunk-FRBMWZ3P.js";
16
18
  export {
19
+ DAEMON_API_KEY_ENV,
17
20
  DAEMON_CLI_USAGE,
18
21
  DAEMON_CORE_TRACE_ATTR_CONTRACTS,
19
22
  DaemonCore,
@@ -26,5 +29,6 @@ export {
26
29
  resolveWorkspaceDirectoryPath,
27
30
  runBundledSlockCli,
28
31
  scanWorkspaceDirectories,
32
+ scrubDaemonAuthEnv,
29
33
  subscribeDaemonLogs
30
34
  };
@@ -44281,6 +44281,7 @@ init_esm_shims();
44281
44281
  init_esm_shims();
44282
44282
  init_esm_shims();
44283
44283
  init_esm_shims();
44284
+ init_esm_shims();
44284
44285
  var NoopFailpointRegistry = class {
44285
44286
  get enabled() {
44286
44287
  return false;
@@ -44382,6 +44383,7 @@ var AGENT_ACTIVITY_DETAIL_KINDS = [
44382
44383
  "compaction_stale",
44383
44384
  "reviewing_changes",
44384
44385
  "review_finished",
44386
+ "review_stale",
44385
44387
  "runtime_reconnecting",
44386
44388
  "runtime_error",
44387
44389
  "runtime_crashed",
@@ -50421,6 +50423,9 @@ function formatClaimResults(channel, data) {
50421
50423
  const msgShort = r.messageId ? r.messageId.slice(0, 8) : "";
50422
50424
  return `${label} (msg:${msgShort}): claimed`;
50423
50425
  }
50426
+ if (r.reason === "already claimed by you") {
50427
+ return `${label}: already claimed by you.`;
50428
+ }
50424
50429
  return `${label}: FAILED \u2014 ${r.reason || "already claimed"}. Do not work on this task unless an owner/admin explicitly redirects it to you.`;
50425
50430
  });
50426
50431
  const succeeded = data.results.filter((r) => r.success).length;
package/dist/index.js CHANGED
@@ -2,11 +2,13 @@
2
2
  import {
3
3
  DAEMON_CLI_USAGE,
4
4
  DaemonCore,
5
- parseDaemonCliArgs
6
- } from "./chunk-RXK2KSYW.js";
5
+ parseDaemonCliArgs,
6
+ scrubDaemonAuthEnv
7
+ } from "./chunk-FRBMWZ3P.js";
7
8
 
8
9
  // src/index.ts
9
- var parsedArgs = parseDaemonCliArgs(process.argv.slice(2));
10
+ var parsedArgs = parseDaemonCliArgs(process.argv.slice(2), process.env);
11
+ scrubDaemonAuthEnv(process.env);
10
12
  if (!parsedArgs) {
11
13
  console.error(DAEMON_CLI_USAGE);
12
14
  process.exit(1);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@botiverse/raft-daemon",
3
- "version": "0.68.0",
3
+ "version": "0.69.0-play.20260704183824",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "raft-daemon": "dist/raft-daemon.js",