@integrity-labs/agt-cli 0.28.404 → 0.28.406

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.
@@ -30279,10 +30279,14 @@ import {
30279
30279
  mkdirSync as mkdirSync4,
30280
30280
  readFileSync as readFileSync7,
30281
30281
  renameSync as renameSync4,
30282
+ statSync,
30282
30283
  unlinkSync as unlinkSync2,
30284
+ utimesSync,
30283
30285
  writeFileSync as writeFileSync5
30284
30286
  } from "fs";
30285
30287
  import { join as join7 } from "path";
30288
+ var STALE_LOCK_MS = 9e4;
30289
+ var HEARTBEAT_INTERVAL_MS = 3e4;
30286
30290
  function defaultIsPidAlive(pid) {
30287
30291
  if (!Number.isFinite(pid) || pid <= 0) return false;
30288
30292
  try {
@@ -30300,6 +30304,9 @@ function acquireMcpSpawnLock(args) {
30300
30304
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
30301
30305
  const selfPid = options.selfPid ?? process.pid;
30302
30306
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
30307
+ const nowMs = options.nowMs ?? (() => Date.now());
30308
+ const lockMtimeMs = options.lockMtimeMs ?? defaultLockMtimeMs;
30309
+ const staleMs = options.staleMs ?? STALE_LOCK_MS;
30303
30310
  const path = join7(agentDir, basename);
30304
30311
  const existing = readLockHolder(path);
30305
30312
  if (existing) {
@@ -30307,7 +30314,11 @@ function acquireMcpSpawnLock(args) {
30307
30314
  return { kind: "acquired", path };
30308
30315
  }
30309
30316
  if (isPidAlive(existing.pid)) {
30310
- return { kind: "blocked", path, holder: existing };
30317
+ const mtime = lockMtimeMs(path);
30318
+ const fresh = mtime !== null && nowMs() - mtime <= staleMs;
30319
+ if (fresh) {
30320
+ return { kind: "blocked", path, holder: existing };
30321
+ }
30311
30322
  }
30312
30323
  }
30313
30324
  mkdirSync4(agentDir, { recursive: true, mode: 448 });
@@ -30328,6 +30339,38 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
30328
30339
  } catch {
30329
30340
  }
30330
30341
  }
30342
+ function refreshMcpSpawnLock(lockPath, opts = {}) {
30343
+ if (!lockPath) return false;
30344
+ const selfPid = opts.selfPid ?? process.pid;
30345
+ const existing = readLockHolder(lockPath);
30346
+ if (!existing || existing.pid !== selfPid) return false;
30347
+ try {
30348
+ const t = (opts.nowMs ?? (() => Date.now()))() / 1e3;
30349
+ utimesSync(lockPath, t, t);
30350
+ return true;
30351
+ } catch {
30352
+ return false;
30353
+ }
30354
+ }
30355
+ function startMcpSpawnLockHeartbeat(lockPath, opts = {}) {
30356
+ if (!lockPath) return () => {
30357
+ };
30358
+ const intervalMs = opts.intervalMs ?? HEARTBEAT_INTERVAL_MS;
30359
+ const handle = setInterval(() => {
30360
+ if (!refreshMcpSpawnLock(lockPath, { selfPid: opts.selfPid })) {
30361
+ clearInterval(handle);
30362
+ }
30363
+ }, intervalMs);
30364
+ handle.unref?.();
30365
+ return () => clearInterval(handle);
30366
+ }
30367
+ function defaultLockMtimeMs(path) {
30368
+ try {
30369
+ return statSync(path).mtimeMs;
30370
+ } catch {
30371
+ return null;
30372
+ }
30373
+ }
30331
30374
  function readLockHolder(path) {
30332
30375
  if (!existsSync5(path)) return null;
30333
30376
  try {
@@ -30355,7 +30398,7 @@ import {
30355
30398
  existsSync as existsSync6,
30356
30399
  renameSync as renameSync5,
30357
30400
  unlinkSync as unlinkSync3,
30358
- statSync,
30401
+ statSync as statSync2,
30359
30402
  createWriteStream
30360
30403
  } from "fs";
30361
30404
 
@@ -35186,6 +35229,17 @@ var FLAG_REGISTRY = [
35186
35229
  // call the flag evaluator) can gate the kanban_move `waiting` option. Also the
35187
35230
  // highest-precedence operator override, per ADR-0022.
35188
35231
  envVar: "AGT_KANBAN_WAITING_ENABLED"
35232
+ },
35233
+ {
35234
+ key: "scheduled-task-nudge-durable",
35235
+ description: 'Deliver the scheduled-task "work this card" nudge over the durable direct-chat notice rail instead of unverified tmux send-keys (ENG-8068, re-landing ENG-7971). When ON, routeScheduledTaskViaKanban enqueues the nudge via POST /host/scheduled-task/notify (kind:notice, push-and-consumed by the in-session MCP with a per-card consume-time revalidation) and closes the card + run on an enqueue failure, retiring the ENG-6351 orphaned-card gap where an unsubmitted keystroke left a scheduled task silently unrun. When OFF (default) the manager keeps the current tmux send-keys delivery, byte-for-byte as today. This is the kill switch: ENG-7971 was reverted once because the durable rail 500d on a session_id bug (fixed in ENG-8061); flip OFF to fall back to send-keys instantly without a code rollback if the durable path misbehaves. Resolved host-side by the manager via hostFlagStore().getBoolean (the manager evaluates the decision itself, so no spawn-env materialization is needed). Ships dark.',
35236
+ flagType: "boolean",
35237
+ // Declared safe value is `false`: the current, reverted-to send-keys delivery. Turning
35238
+ // it ON activates the durable rail per org/host once operators are confident. `false`
35239
+ // is also the fail-safe direction - a flag-DB read error must never silently switch a
35240
+ // fleet onto the rail that broke prod twice. Net-new gate, so registry-only (ADR-0022);
35241
+ // consumed by the manager's own logic, not an in-container MCP, so no env materialization.
35242
+ defaultValue: false
35189
35243
  }
35190
35244
  ];
35191
35245
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -35819,7 +35873,7 @@ function scheduleDirectChatBusyAck(sessionId, messageId, arrivedWhileBusy) {
35819
35873
  let paneLogFreshAgeMs = null;
35820
35874
  if (DIRECT_CHAT_CODE_NAME_AGENT_DIR) {
35821
35875
  try {
35822
- const paneMtimeMs = statSync(join10(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "pane.log")).mtimeMs;
35876
+ const paneMtimeMs = statSync2(join10(DIRECT_CHAT_CODE_NAME_AGENT_DIR, "pane.log")).mtimeMs;
35823
35877
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
35824
35878
  } catch {
35825
35879
  }
@@ -36045,6 +36099,7 @@ async function pollForMessages(sinceMs) {
36045
36099
  }
36046
36100
  }
36047
36101
  var acquiredLockPath = null;
36102
+ var stopLockHeartbeat = null;
36048
36103
  {
36049
36104
  const lockResult = acquireMcpSpawnLock({
36050
36105
  agentDir: DIRECT_CHAT_AGENT_DIR,
@@ -36058,6 +36113,7 @@ var acquiredLockPath = null;
36058
36113
  process.exit(0);
36059
36114
  } else if (lockResult.kind === "acquired") {
36060
36115
  acquiredLockPath = lockResult.path;
36116
+ stopLockHeartbeat = startMcpSpawnLockHeartbeat(acquiredLockPath);
36061
36117
  }
36062
36118
  }
36063
36119
  var DOORBELL_ENABLED = resolveHostBooleanFlag({
@@ -36277,6 +36333,10 @@ function shutdown(reason) {
36277
36333
  }
36278
36334
  if (safetyNetTimer) clearInterval(safetyNetTimer);
36279
36335
  if (progressTickTimer) clearInterval(progressTickTimer);
36336
+ try {
36337
+ stopLockHeartbeat?.();
36338
+ } catch {
36339
+ }
36280
36340
  try {
36281
36341
  releaseMcpSpawnLock(acquiredLockPath);
36282
36342
  } catch {
@@ -40845,6 +40845,17 @@ var FLAG_REGISTRY = [
40845
40845
  // call the flag evaluator) can gate the kanban_move `waiting` option. Also the
40846
40846
  // highest-precedence operator override, per ADR-0022.
40847
40847
  envVar: "AGT_KANBAN_WAITING_ENABLED"
40848
+ },
40849
+ {
40850
+ key: "scheduled-task-nudge-durable",
40851
+ description: 'Deliver the scheduled-task "work this card" nudge over the durable direct-chat notice rail instead of unverified tmux send-keys (ENG-8068, re-landing ENG-7971). When ON, routeScheduledTaskViaKanban enqueues the nudge via POST /host/scheduled-task/notify (kind:notice, push-and-consumed by the in-session MCP with a per-card consume-time revalidation) and closes the card + run on an enqueue failure, retiring the ENG-6351 orphaned-card gap where an unsubmitted keystroke left a scheduled task silently unrun. When OFF (default) the manager keeps the current tmux send-keys delivery, byte-for-byte as today. This is the kill switch: ENG-7971 was reverted once because the durable rail 500d on a session_id bug (fixed in ENG-8061); flip OFF to fall back to send-keys instantly without a code rollback if the durable path misbehaves. Resolved host-side by the manager via hostFlagStore().getBoolean (the manager evaluates the decision itself, so no spawn-env materialization is needed). Ships dark.',
40852
+ flagType: "boolean",
40853
+ // Declared safe value is `false`: the current, reverted-to send-keys delivery. Turning
40854
+ // it ON activates the durable rail per org/host once operators are confident. `false`
40855
+ // is also the fail-safe direction - a flag-DB read error must never silently switch a
40856
+ // fleet onto the rail that broke prod twice. Net-new gate, so registry-only (ADR-0022);
40857
+ // consumed by the manager's own logic, not an in-container MCP, so no env materialization.
40858
+ defaultValue: false
40848
40859
  }
40849
40860
  ];
40850
40861
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -34764,6 +34764,17 @@ var FLAG_REGISTRY = [
34764
34764
  // call the flag evaluator) can gate the kanban_move `waiting` option. Also the
34765
34765
  // highest-precedence operator override, per ADR-0022.
34766
34766
  envVar: "AGT_KANBAN_WAITING_ENABLED"
34767
+ },
34768
+ {
34769
+ key: "scheduled-task-nudge-durable",
34770
+ description: 'Deliver the scheduled-task "work this card" nudge over the durable direct-chat notice rail instead of unverified tmux send-keys (ENG-8068, re-landing ENG-7971). When ON, routeScheduledTaskViaKanban enqueues the nudge via POST /host/scheduled-task/notify (kind:notice, push-and-consumed by the in-session MCP with a per-card consume-time revalidation) and closes the card + run on an enqueue failure, retiring the ENG-6351 orphaned-card gap where an unsubmitted keystroke left a scheduled task silently unrun. When OFF (default) the manager keeps the current tmux send-keys delivery, byte-for-byte as today. This is the kill switch: ENG-7971 was reverted once because the durable rail 500d on a session_id bug (fixed in ENG-8061); flip OFF to fall back to send-keys instantly without a code rollback if the durable path misbehaves. Resolved host-side by the manager via hostFlagStore().getBoolean (the manager evaluates the decision itself, so no spawn-env materialization is needed). Ships dark.',
34771
+ flagType: "boolean",
34772
+ // Declared safe value is `false`: the current, reverted-to send-keys delivery. Turning
34773
+ // it ON activates the durable rail per org/host once operators are confident. `false`
34774
+ // is also the fail-safe direction - a flag-DB read error must never silently switch a
34775
+ // fleet onto the rail that broke prod twice. Net-new gate, so registry-only (ADR-0022);
34776
+ // consumed by the manager's own logic, not an in-container MCP, so no env materialization.
34777
+ defaultValue: false
34767
34778
  }
34768
34779
  ];
34769
34780
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -36683,7 +36694,7 @@ import {
36683
36694
  readFileSync as readFileSync17,
36684
36695
  readdirSync as readdirSync5,
36685
36696
  renameSync as renameSync5,
36686
- statSync as statSync2,
36697
+ statSync as statSync3,
36687
36698
  unlinkSync as unlinkSync7,
36688
36699
  watch,
36689
36700
  writeFileSync as writeFileSync13,
@@ -38494,10 +38505,14 @@ import {
38494
38505
  mkdirSync as mkdirSync8,
38495
38506
  readFileSync as readFileSync16,
38496
38507
  renameSync as renameSync4,
38508
+ statSync as statSync2,
38497
38509
  unlinkSync as unlinkSync6,
38510
+ utimesSync,
38498
38511
  writeFileSync as writeFileSync12
38499
38512
  } from "fs";
38500
38513
  import { join as join16 } from "path";
38514
+ var STALE_LOCK_MS = 9e4;
38515
+ var HEARTBEAT_INTERVAL_MS = 3e4;
38501
38516
  function defaultIsPidAlive(pid) {
38502
38517
  if (!Number.isFinite(pid) || pid <= 0) return false;
38503
38518
  try {
@@ -38515,6 +38530,9 @@ function acquireMcpSpawnLock(args) {
38515
38530
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
38516
38531
  const selfPid = options.selfPid ?? process.pid;
38517
38532
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
38533
+ const nowMs = options.nowMs ?? (() => Date.now());
38534
+ const lockMtimeMs = options.lockMtimeMs ?? defaultLockMtimeMs;
38535
+ const staleMs = options.staleMs ?? STALE_LOCK_MS;
38518
38536
  const path = join16(agentDir, basename2);
38519
38537
  const existing = readLockHolder(path);
38520
38538
  if (existing) {
@@ -38522,7 +38540,11 @@ function acquireMcpSpawnLock(args) {
38522
38540
  return { kind: "acquired", path };
38523
38541
  }
38524
38542
  if (isPidAlive(existing.pid)) {
38525
- return { kind: "blocked", path, holder: existing };
38543
+ const mtime = lockMtimeMs(path);
38544
+ const fresh = mtime !== null && nowMs() - mtime <= staleMs;
38545
+ if (fresh) {
38546
+ return { kind: "blocked", path, holder: existing };
38547
+ }
38526
38548
  }
38527
38549
  }
38528
38550
  mkdirSync8(agentDir, { recursive: true, mode: 448 });
@@ -38543,6 +38565,38 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
38543
38565
  } catch {
38544
38566
  }
38545
38567
  }
38568
+ function refreshMcpSpawnLock(lockPath, opts = {}) {
38569
+ if (!lockPath) return false;
38570
+ const selfPid = opts.selfPid ?? process.pid;
38571
+ const existing = readLockHolder(lockPath);
38572
+ if (!existing || existing.pid !== selfPid) return false;
38573
+ try {
38574
+ const t = (opts.nowMs ?? (() => Date.now()))() / 1e3;
38575
+ utimesSync(lockPath, t, t);
38576
+ return true;
38577
+ } catch {
38578
+ return false;
38579
+ }
38580
+ }
38581
+ function startMcpSpawnLockHeartbeat(lockPath, opts = {}) {
38582
+ if (!lockPath) return () => {
38583
+ };
38584
+ const intervalMs = opts.intervalMs ?? HEARTBEAT_INTERVAL_MS;
38585
+ const handle = setInterval(() => {
38586
+ if (!refreshMcpSpawnLock(lockPath, { selfPid: opts.selfPid })) {
38587
+ clearInterval(handle);
38588
+ }
38589
+ }, intervalMs);
38590
+ handle.unref?.();
38591
+ return () => clearInterval(handle);
38592
+ }
38593
+ function defaultLockMtimeMs(path) {
38594
+ try {
38595
+ return statSync2(path).mtimeMs;
38596
+ } catch {
38597
+ return null;
38598
+ }
38599
+ }
38546
38600
  function readLockHolder(path) {
38547
38601
  if (!existsSync9(path)) return null;
38548
38602
  try {
@@ -38858,7 +38912,7 @@ var liveAllowedUsersCache = null;
38858
38912
  function readLiveAllowedUsers() {
38859
38913
  if (!SLACK_MCP_CONFIG_PATH) return null;
38860
38914
  try {
38861
- const mtimeMs = statSync2(SLACK_MCP_CONFIG_PATH).mtimeMs;
38915
+ const mtimeMs = statSync3(SLACK_MCP_CONFIG_PATH).mtimeMs;
38862
38916
  if (liveAllowedUsersCache && liveAllowedUsersCache.mtimeMs === mtimeMs) {
38863
38917
  return liveAllowedUsersCache.value;
38864
38918
  }
@@ -38879,7 +38933,7 @@ var livePingAllowedUsersCache = null;
38879
38933
  function readLivePingAllowedUsers() {
38880
38934
  if (!SLACK_MCP_CONFIG_PATH) return null;
38881
38935
  try {
38882
- const mtimeMs = statSync2(SLACK_MCP_CONFIG_PATH).mtimeMs;
38936
+ const mtimeMs = statSync3(SLACK_MCP_CONFIG_PATH).mtimeMs;
38883
38937
  if (livePingAllowedUsersCache && livePingAllowedUsersCache.mtimeMs === mtimeMs) {
38884
38938
  return livePingAllowedUsersCache.value;
38885
38939
  }
@@ -39109,7 +39163,7 @@ function scheduleBusyAck(channel, threadTs, messageTs, isThreadReply, arrivedWhi
39109
39163
  let paneLogFreshAgeMs = null;
39110
39164
  if (SLACK_AGENT_DIR) {
39111
39165
  try {
39112
- const paneMtimeMs = statSync2(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
39166
+ const paneMtimeMs = statSync3(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
39113
39167
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
39114
39168
  } catch {
39115
39169
  }
@@ -39376,7 +39430,7 @@ function scanSlackRecoveryRetries() {
39376
39430
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
39377
39431
  let mtimeMs;
39378
39432
  try {
39379
- mtimeMs = statSync2(join17(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
39433
+ mtimeMs = statSync3(join17(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
39380
39434
  } catch {
39381
39435
  continue;
39382
39436
  }
@@ -39433,7 +39487,7 @@ async function processSlackNoticeOutboxFile(filename) {
39433
39487
  try {
39434
39488
  let mtimeMs;
39435
39489
  try {
39436
- mtimeMs = statSync2(fullPath).mtimeMs;
39490
+ mtimeMs = statSync3(fullPath).mtimeMs;
39437
39491
  } catch {
39438
39492
  return;
39439
39493
  }
@@ -41882,7 +41936,7 @@ ${result.formatted}` : "No messages in range, or the bot is not a member of this
41882
41936
  let bytes;
41883
41937
  let size;
41884
41938
  try {
41885
- const stat2 = statSync2(resolvedPath);
41939
+ const stat2 = statSync3(resolvedPath);
41886
41940
  if (!stat2.isFile()) {
41887
41941
  return {
41888
41942
  content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }],
@@ -42673,7 +42727,7 @@ async function replayPendingSlackMarkers() {
42673
42727
  let paneFreshAgeMs = null;
42674
42728
  if (SLACK_AGENT_DIR) {
42675
42729
  try {
42676
- paneFreshAgeMs = Math.max(0, now - statSync2(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs);
42730
+ paneFreshAgeMs = Math.max(0, now - statSync3(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs);
42677
42731
  } catch {
42678
42732
  }
42679
42733
  }
@@ -42785,6 +42839,7 @@ async function resolveUserEmail(userId) {
42785
42839
  var currentWs = null;
42786
42840
  var isShuttingDown = false;
42787
42841
  var acquiredLockPath = null;
42842
+ var stopLockHeartbeat = null;
42788
42843
  async function seedMutedChannelsFromApi() {
42789
42844
  try {
42790
42845
  const cfg = statusRuntimeConfig();
@@ -43263,7 +43318,7 @@ async function connectSocketMode() {
43263
43318
  let paneLogFreshAgeMs = null;
43264
43319
  if (SLACK_AGENT_DIR) {
43265
43320
  try {
43266
- const paneMtimeMs = statSync2(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
43321
+ const paneMtimeMs = statSync3(join17(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
43267
43322
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
43268
43323
  } catch {
43269
43324
  }
@@ -43498,6 +43553,10 @@ function shutdown(reason, exitCode = 0) {
43498
43553
  currentWs?.close();
43499
43554
  } catch {
43500
43555
  }
43556
+ try {
43557
+ stopLockHeartbeat?.();
43558
+ } catch {
43559
+ }
43501
43560
  try {
43502
43561
  releaseMcpSpawnLock(acquiredLockPath);
43503
43562
  } catch {
@@ -43527,6 +43586,7 @@ process.on("unhandledRejection", (reason) => {
43527
43586
  process.exit(0);
43528
43587
  } else if (lockResult.kind === "acquired") {
43529
43588
  acquiredLockPath = lockResult.path;
43589
+ stopLockHeartbeat = startMcpSpawnLockHeartbeat(acquiredLockPath);
43530
43590
  }
43531
43591
  }
43532
43592
  if (THREAD_STORE_PATH) {
@@ -30097,7 +30097,7 @@ import {
30097
30097
  readdirSync as readdirSync4,
30098
30098
  realpathSync,
30099
30099
  renameSync as renameSync7,
30100
- statSync as statSync2,
30100
+ statSync as statSync3,
30101
30101
  unlinkSync as unlinkSync7,
30102
30102
  watch,
30103
30103
  writeFileSync as writeFileSync11,
@@ -35114,6 +35114,17 @@ var FLAG_REGISTRY = [
35114
35114
  // call the flag evaluator) can gate the kanban_move `waiting` option. Also the
35115
35115
  // highest-precedence operator override, per ADR-0022.
35116
35116
  envVar: "AGT_KANBAN_WAITING_ENABLED"
35117
+ },
35118
+ {
35119
+ key: "scheduled-task-nudge-durable",
35120
+ description: 'Deliver the scheduled-task "work this card" nudge over the durable direct-chat notice rail instead of unverified tmux send-keys (ENG-8068, re-landing ENG-7971). When ON, routeScheduledTaskViaKanban enqueues the nudge via POST /host/scheduled-task/notify (kind:notice, push-and-consumed by the in-session MCP with a per-card consume-time revalidation) and closes the card + run on an enqueue failure, retiring the ENG-6351 orphaned-card gap where an unsubmitted keystroke left a scheduled task silently unrun. When OFF (default) the manager keeps the current tmux send-keys delivery, byte-for-byte as today. This is the kill switch: ENG-7971 was reverted once because the durable rail 500d on a session_id bug (fixed in ENG-8061); flip OFF to fall back to send-keys instantly without a code rollback if the durable path misbehaves. Resolved host-side by the manager via hostFlagStore().getBoolean (the manager evaluates the decision itself, so no spawn-env materialization is needed). Ships dark.',
35121
+ flagType: "boolean",
35122
+ // Declared safe value is `false`: the current, reverted-to send-keys delivery. Turning
35123
+ // it ON activates the durable rail per org/host once operators are confident. `false`
35124
+ // is also the fail-safe direction - a flag-DB read error must never silently switch a
35125
+ // fleet onto the rail that broke prod twice. Net-new gate, so registry-only (ADR-0022);
35126
+ // consumed by the manager's own logic, not an in-container MCP, so no env materialization.
35127
+ defaultValue: false
35117
35128
  }
35118
35129
  ];
35119
35130
  var REGISTRY_BY_KEY = new Map(FLAG_REGISTRY.map((definition) => [definition.key, definition]));
@@ -37295,10 +37306,14 @@ import {
37295
37306
  mkdirSync as mkdirSync8,
37296
37307
  readFileSync as readFileSync11,
37297
37308
  renameSync as renameSync6,
37309
+ statSync as statSync2,
37298
37310
  unlinkSync as unlinkSync6,
37311
+ utimesSync,
37299
37312
  writeFileSync as writeFileSync9
37300
37313
  } from "fs";
37301
37314
  import { join as join11 } from "path";
37315
+ var STALE_LOCK_MS = 9e4;
37316
+ var HEARTBEAT_INTERVAL_MS = 3e4;
37302
37317
  function defaultIsPidAlive(pid) {
37303
37318
  if (!Number.isFinite(pid) || pid <= 0) return false;
37304
37319
  try {
@@ -37316,6 +37331,9 @@ function acquireMcpSpawnLock(args) {
37316
37331
  const isPidAlive = options.isPidAlive ?? defaultIsPidAlive;
37317
37332
  const selfPid = options.selfPid ?? process.pid;
37318
37333
  const now = options.now ?? (() => (/* @__PURE__ */ new Date()).toISOString());
37334
+ const nowMs = options.nowMs ?? (() => Date.now());
37335
+ const lockMtimeMs = options.lockMtimeMs ?? defaultLockMtimeMs;
37336
+ const staleMs = options.staleMs ?? STALE_LOCK_MS;
37319
37337
  const path = join11(agentDir, basename2);
37320
37338
  const existing = readLockHolder(path);
37321
37339
  if (existing) {
@@ -37323,7 +37341,11 @@ function acquireMcpSpawnLock(args) {
37323
37341
  return { kind: "acquired", path };
37324
37342
  }
37325
37343
  if (isPidAlive(existing.pid)) {
37326
- return { kind: "blocked", path, holder: existing };
37344
+ const mtime = lockMtimeMs(path);
37345
+ const fresh = mtime !== null && nowMs() - mtime <= staleMs;
37346
+ if (fresh) {
37347
+ return { kind: "blocked", path, holder: existing };
37348
+ }
37327
37349
  }
37328
37350
  }
37329
37351
  mkdirSync8(agentDir, { recursive: true, mode: 448 });
@@ -37344,6 +37366,38 @@ function releaseMcpSpawnLock(lockPath, opts = {}) {
37344
37366
  } catch {
37345
37367
  }
37346
37368
  }
37369
+ function refreshMcpSpawnLock(lockPath, opts = {}) {
37370
+ if (!lockPath) return false;
37371
+ const selfPid = opts.selfPid ?? process.pid;
37372
+ const existing = readLockHolder(lockPath);
37373
+ if (!existing || existing.pid !== selfPid) return false;
37374
+ try {
37375
+ const t = (opts.nowMs ?? (() => Date.now()))() / 1e3;
37376
+ utimesSync(lockPath, t, t);
37377
+ return true;
37378
+ } catch {
37379
+ return false;
37380
+ }
37381
+ }
37382
+ function startMcpSpawnLockHeartbeat(lockPath, opts = {}) {
37383
+ if (!lockPath) return () => {
37384
+ };
37385
+ const intervalMs = opts.intervalMs ?? HEARTBEAT_INTERVAL_MS;
37386
+ const handle = setInterval(() => {
37387
+ if (!refreshMcpSpawnLock(lockPath, { selfPid: opts.selfPid })) {
37388
+ clearInterval(handle);
37389
+ }
37390
+ }, intervalMs);
37391
+ handle.unref?.();
37392
+ return () => clearInterval(handle);
37393
+ }
37394
+ function defaultLockMtimeMs(path) {
37395
+ try {
37396
+ return statSync2(path).mtimeMs;
37397
+ } catch {
37398
+ return null;
37399
+ }
37400
+ }
37347
37401
  function readLockHolder(path) {
37348
37402
  if (!existsSync8(path)) return null;
37349
37403
  try {
@@ -38057,7 +38111,7 @@ function scheduleBusyAck(chatId, messageId, arrivedWhileBusy) {
38057
38111
  let paneLogFreshAgeMs = null;
38058
38112
  if (AGENT_DIR) {
38059
38113
  try {
38060
- const paneMtimeMs = statSync2(join13(AGENT_DIR, "pane.log")).mtimeMs;
38114
+ const paneMtimeMs = statSync3(join13(AGENT_DIR, "pane.log")).mtimeMs;
38061
38115
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
38062
38116
  } catch {
38063
38117
  }
@@ -39193,7 +39247,7 @@ function scanRecoveryRetries() {
39193
39247
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
39194
39248
  let mtimeMs;
39195
39249
  try {
39196
- mtimeMs = statSync2(join13(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
39250
+ mtimeMs = statSync3(join13(RECOVERY_OUTBOX_DIR, f)).mtimeMs;
39197
39251
  } catch {
39198
39252
  continue;
39199
39253
  }
@@ -39250,7 +39304,7 @@ async function processNoticeOutboxFile(filename) {
39250
39304
  try {
39251
39305
  let mtimeMs;
39252
39306
  try {
39253
- mtimeMs = statSync2(fullPath).mtimeMs;
39307
+ mtimeMs = statSync3(fullPath).mtimeMs;
39254
39308
  } catch {
39255
39309
  return;
39256
39310
  }
@@ -39970,7 +40024,7 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
39970
40024
  let bytes;
39971
40025
  let size;
39972
40026
  try {
39973
- const st = statSync2(realPath);
40027
+ const st = statSync3(realPath);
39974
40028
  if (!st.isFile()) {
39975
40029
  return { content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }], isError: true };
39976
40030
  }
@@ -40545,7 +40599,7 @@ async function replayPendingTelegramMarkers() {
40545
40599
  let paneFreshAgeMs = null;
40546
40600
  if (AGENT_DIR) {
40547
40601
  try {
40548
- paneFreshAgeMs = Math.max(0, now - statSync2(join13(AGENT_DIR, "pane.log")).mtimeMs);
40602
+ paneFreshAgeMs = Math.max(0, now - statSync3(join13(AGENT_DIR, "pane.log")).mtimeMs);
40549
40603
  } catch {
40550
40604
  }
40551
40605
  }
@@ -40698,6 +40752,7 @@ var telegramHttp = {
40698
40752
  var nextOffset = loadPersistedOffset(TELEGRAM_OFFSET_FILE);
40699
40753
  var isShuttingDown = false;
40700
40754
  var acquiredLockPath = null;
40755
+ var stopLockHeartbeat = null;
40701
40756
  async function pollLoop() {
40702
40757
  while (!isShuttingDown) {
40703
40758
  try {
@@ -41090,7 +41145,7 @@ async function pollLoop() {
41090
41145
  let paneLogFreshAgeMs = null;
41091
41146
  if (AGENT_DIR) {
41092
41147
  try {
41093
- const paneMtimeMs = statSync2(join13(AGENT_DIR, "pane.log")).mtimeMs;
41148
+ const paneMtimeMs = statSync3(join13(AGENT_DIR, "pane.log")).mtimeMs;
41094
41149
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
41095
41150
  } catch {
41096
41151
  }
@@ -41257,6 +41312,10 @@ function shutdown(reason) {
41257
41312
  recentDmPersister?.dispose();
41258
41313
  } catch {
41259
41314
  }
41315
+ try {
41316
+ stopLockHeartbeat?.();
41317
+ } catch {
41318
+ }
41260
41319
  try {
41261
41320
  releaseMcpSpawnLock(acquiredLockPath);
41262
41321
  } catch {
@@ -41281,6 +41340,7 @@ process.on("SIGHUP", () => shutdown("SIGHUP"));
41281
41340
  process.exit(0);
41282
41341
  } else if (lockResult.kind === "acquired") {
41283
41342
  acquiredLockPath = lockResult.path;
41343
+ stopLockHeartbeat = startMcpSpawnLockHeartbeat(acquiredLockPath);
41284
41344
  }
41285
41345
  }
41286
41346
  process.stderr.write(
@@ -36,7 +36,7 @@ import {
36
36
  writeDirectChatSessionState,
37
37
  writeEgressAllowlist,
38
38
  writePersistentClaudeWrapper
39
- } from "./chunk-V5CRVBVC.js";
39
+ } from "./chunk-RD7MIR3G.js";
40
40
  import "./chunk-XWVM4KPK.js";
41
41
  export {
42
42
  EGRESS_BASELINE_DOMAINS,
@@ -77,4 +77,4 @@ export {
77
77
  writeEgressAllowlist,
78
78
  writePersistentClaudeWrapper
79
79
  };
80
- //# sourceMappingURL=persistent-session-GRCJPRH5.js.map
80
+ //# sourceMappingURL=persistent-session-2AC3P5DJ.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  paneLogPath
3
- } from "./chunk-V5CRVBVC.js";
3
+ } from "./chunk-RD7MIR3G.js";
4
4
  import "./chunk-XWVM4KPK.js";
5
5
 
6
6
  // src/lib/responsiveness-probe.ts
@@ -437,4 +437,4 @@ export {
437
437
  readAndResetSlackReplyBindingClassifications,
438
438
  readAndResetSlackReplyTargetClassifications
439
439
  };
440
- //# sourceMappingURL=responsiveness-probe-DNM5APN5.js.map
440
+ //# sourceMappingURL=responsiveness-probe-LGJNUSPU.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@integrity-labs/agt-cli",
3
- "version": "0.28.404",
3
+ "version": "0.28.406",
4
4
  "description": "Augmented Team CLI — agent provisioning and management",
5
5
  "type": "module",
6
6
  "engines": {