@integrity-labs/agt-cli 0.28.458 → 0.28.459

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.
@@ -18906,7 +18906,7 @@ var require_filters = __commonJS({
18906
18906
  return r.copySafeness(str, res);
18907
18907
  }
18908
18908
  _exports.indent = indent;
18909
- function join21(arr, del, attr) {
18909
+ function join22(arr, del, attr) {
18910
18910
  del = del || "";
18911
18911
  if (attr) {
18912
18912
  arr = lib.map(arr, function(v) {
@@ -18915,7 +18915,7 @@ var require_filters = __commonJS({
18915
18915
  }
18916
18916
  return arr.join(del);
18917
18917
  }
18918
- _exports.join = join21;
18918
+ _exports.join = join22;
18919
18919
  function last(arr) {
18920
18920
  return arr[arr.length - 1];
18921
18921
  }
@@ -22516,11 +22516,11 @@ async function waitForResolution(cfg, callbackId, opts) {
22516
22516
  }
22517
22517
  } catch {
22518
22518
  }
22519
- await sleep2(interval);
22519
+ await sleep3(interval);
22520
22520
  }
22521
22521
  return { kind: "timeout" };
22522
22522
  }
22523
- function sleep2(ms) {
22523
+ function sleep3(ms) {
22524
22524
  return new Promise((r) => setTimeout(r, ms));
22525
22525
  }
22526
22526
  function generateOptionToken() {
@@ -34052,6 +34052,160 @@ function classifyTranscriptRateLimit(jsonl, startMs, endMs, now) {
34052
34052
  return newest;
34053
34053
  }
34054
34054
 
34055
+ // ../core/dist/claude-code-usage/turn-failure-classifier.js
34056
+ var UNKNOWN_TURN_FAILURE = Object.freeze({
34057
+ outcome: "unknown",
34058
+ atMs: null,
34059
+ failureClass: null,
34060
+ httpStatus: null,
34061
+ attempt: null,
34062
+ maxAttempts: null
34063
+ });
34064
+ var EXCLUDED_STATUSES = /* @__PURE__ */ new Set([429]);
34065
+ function classifyTransientStatus(status) {
34066
+ if (!Number.isFinite(status))
34067
+ return null;
34068
+ if (EXCLUDED_STATUSES.has(status))
34069
+ return null;
34070
+ if (status === 529)
34071
+ return "overloaded";
34072
+ if (status >= 500 && status <= 599)
34073
+ return "server_error";
34074
+ return null;
34075
+ }
34076
+ function numberOrNull(value) {
34077
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
34078
+ }
34079
+ function isErrorShaped(record2) {
34080
+ if (record2.isApiErrorMessage === true)
34081
+ return true;
34082
+ if (record2.type === "system" && record2.level === "error")
34083
+ return true;
34084
+ if (record2.type === "assistant" && typeof record2.error === "string" && record2.error)
34085
+ return true;
34086
+ return false;
34087
+ }
34088
+ function classifyRecord(record2, tsMs) {
34089
+ if (record2.isSidechain === true)
34090
+ return null;
34091
+ if (record2.type === "system" && record2.subtype === "api_error") {
34092
+ const error2 = record2.error;
34093
+ const status = typeof error2 === "object" && error2 !== null ? numberOrNull(error2.status) : null;
34094
+ if (status === null)
34095
+ return null;
34096
+ const failureClass = classifyTransientStatus(status);
34097
+ if (!failureClass)
34098
+ return null;
34099
+ return {
34100
+ outcome: "retrying",
34101
+ atMs: tsMs,
34102
+ failureClass,
34103
+ httpStatus: status,
34104
+ attempt: numberOrNull(record2.retryAttempt),
34105
+ maxAttempts: numberOrNull(record2.maxRetries)
34106
+ };
34107
+ }
34108
+ if (record2.type !== "assistant")
34109
+ return null;
34110
+ if (record2.isApiErrorMessage === true) {
34111
+ const status = numberOrNull(record2.apiErrorStatus);
34112
+ const failureClass = status === null ? null : classifyTransientStatus(status);
34113
+ if (!failureClass)
34114
+ return null;
34115
+ return {
34116
+ outcome: "failed",
34117
+ atMs: tsMs,
34118
+ failureClass,
34119
+ httpStatus: status,
34120
+ attempt: null,
34121
+ maxAttempts: null
34122
+ };
34123
+ }
34124
+ const message = record2.message;
34125
+ if (typeof message !== "object" || message === null)
34126
+ return null;
34127
+ const msg = message;
34128
+ if (msg.model === "<synthetic>")
34129
+ return null;
34130
+ const usage = msg.usage;
34131
+ if (typeof usage !== "object" || usage === null)
34132
+ return null;
34133
+ const u = usage;
34134
+ const spent = Number(u.input_tokens ?? 0) + Number(u.output_tokens ?? 0) + Number(u.cache_creation_input_tokens ?? 0) + Number(u.cache_read_input_tokens ?? 0);
34135
+ if (!Number.isFinite(spent) || spent <= 0)
34136
+ return null;
34137
+ return {
34138
+ outcome: "served",
34139
+ atMs: tsMs,
34140
+ failureClass: null,
34141
+ httpStatus: null,
34142
+ attempt: null,
34143
+ maxAttempts: null
34144
+ };
34145
+ }
34146
+ function pickNewerTurnFailure(current, next) {
34147
+ if (next.outcome === "unknown")
34148
+ return current;
34149
+ if (current.outcome === "unknown")
34150
+ return next;
34151
+ return (next.atMs ?? 0) >= (current.atMs ?? 0) ? next : current;
34152
+ }
34153
+ function isShapeRecognised(record2) {
34154
+ if (record2.type === "system" && record2.subtype === "api_error") {
34155
+ const error2 = record2.error;
34156
+ return typeof error2 === "object" && error2 !== null && numberOrNull(error2.status) !== null;
34157
+ }
34158
+ if (record2.type === "assistant" && record2.isApiErrorMessage === true) {
34159
+ return numberOrNull(record2.apiErrorStatus) !== null;
34160
+ }
34161
+ return false;
34162
+ }
34163
+ function analyzeTranscriptTurnFailure(jsonl, startMs, endMs, opts = {}) {
34164
+ const maxKeys = opts.maxKeys ?? 40;
34165
+ let newest = UNKNOWN_TURN_FAILURE;
34166
+ let coarse = 0;
34167
+ let fine = 0;
34168
+ const keys = /* @__PURE__ */ new Set();
34169
+ for (const line of jsonl.split("\n")) {
34170
+ const trimmed = line.trim();
34171
+ if (!trimmed)
34172
+ continue;
34173
+ let obj;
34174
+ try {
34175
+ obj = JSON.parse(trimmed);
34176
+ } catch {
34177
+ continue;
34178
+ }
34179
+ if (typeof obj !== "object" || obj === null)
34180
+ continue;
34181
+ const record2 = obj;
34182
+ const ts = record2.timestamp;
34183
+ if (typeof ts !== "string" || !ts)
34184
+ continue;
34185
+ const tsMs = new Date(ts).getTime();
34186
+ if (!Number.isFinite(tsMs) || tsMs < startMs || tsMs > endMs)
34187
+ continue;
34188
+ const classified = classifyRecord(record2, tsMs);
34189
+ if (classified)
34190
+ newest = pickNewerTurnFailure(newest, classified);
34191
+ if (!isErrorShaped(record2) || record2.isSidechain === true)
34192
+ continue;
34193
+ coarse++;
34194
+ if (isShapeRecognised(record2)) {
34195
+ fine++;
34196
+ continue;
34197
+ }
34198
+ if (keys.size < maxKeys) {
34199
+ for (const key2 of Object.keys(record2)) {
34200
+ if (keys.size >= maxKeys)
34201
+ break;
34202
+ keys.add(key2);
34203
+ }
34204
+ }
34205
+ }
34206
+ return { result: newest, coarse, fine, unrecognisedKeys: [...keys].sort() };
34207
+ }
34208
+
34055
34209
  // ../core/dist/claude-code-usage/transcript-location.js
34056
34210
  function encodeClaudeProjectPath(projectDir) {
34057
34211
  return "-" + projectDir.replace(/^\//, "").replace(/[/.]/g, "-");
@@ -34908,7 +35062,7 @@ var FLAG_REGISTRY = [
34908
35062
  },
34909
35063
  {
34910
35064
  key: "wedge-transient-notice",
34911
- description: 'When a wedge-respawn was preceded by a transient LLM-API error (529/429/503/500 that exhausted its retries and wedged the turn), the manager writes the ENG-6058 give-up signal tagged reason=transient_overload so the channel sweeps post a friendly "I hit a brief overload \u2014 please resend" notice instead of leaving the user in silence (ENG-7360, extends ENG-6861 to the retry-exhaustion + wedge path). Boolean gate; ships dark \u2014 channel-visible copy soaks per host before going wide.',
35065
+ description: `Tell the person waiting when their turn dies on a transient LLM-API failure (529 overloaded / 5xx). TWO consumers now share this gate. (1) ENG-7360: a wedge-respawn preceded by such an error writes the ENG-6058 give-up signal tagged reason=transient_overload so the channel sweeps post a "please resend" notice. (2) ENG-8269: the channel MCPs watch the dispatched turn in Claude Code's own transcript and notify the conversation that was actually waiting \u2014 Slack, Telegram AND direct chat \u2014 plus a "still working on this" notice after ~3min on Slack/Telegram only (direct chat already shows a client-side one at 90s). NOTE: (2) fires on a MUCH larger population than (1) \u2014 any dispatched turn that dies, not only one that also wedged the session \u2014 and (1) has never actually been able to fire, because its pane.log detector cannot match the banner Claude Code renders today. So flipping this on is in practice enabling (2) for the first time. Boolean gate; ships dark \u2014 channel-visible copy soaks per host before going wide. Materialized into the channel-MCP spawn env: a Docker-isolated agent never mounts the host flags-cache, so a central flip reaches it only that way.`,
34912
35066
  flagType: "boolean",
34913
35067
  defaultValue: false,
34914
35068
  envVar: "AGT_WEDGE_TRANSIENT_NOTICE_ENABLED"
@@ -35731,6 +35885,12 @@ function giveUpNoticeText(reason = null) {
35731
35885
  }
35732
35886
  return "\u26A0\uFE0F I couldn't read your last message \u2014 please resend it.";
35733
35887
  }
35888
+ function turnFailedNoticeText() {
35889
+ return "\u26A0\uFE0F Something went wrong on my side and I couldn\u2019t finish that. Sorry \u2014 send it again when you get a chance and I\u2019ll pick it straight up.";
35890
+ }
35891
+ function turnRetryingNoticeText() {
35892
+ return "\u23F3 Still working on this \u2014 things are running slower than usual on my side. I\u2019ll reply as soon as I have it.";
35893
+ }
35734
35894
  function oldestPendingMarkerAgeMs(dir, now = Date.now(), opts) {
35735
35895
  if (!dir) return null;
35736
35896
  let names;
@@ -36706,6 +36866,149 @@ async function watchForRateLimitRefusal(opts) {
36706
36866
  }
36707
36867
  }
36708
36868
 
36869
+ // src/turn-failure-watch.ts
36870
+ import { readFileSync as readFileSync7, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
36871
+ import { join as join8 } from "path";
36872
+ function turnFailureNoticeEnabled(env2) {
36873
+ return resolveHostBooleanFlag({
36874
+ key: "wedge-transient-notice",
36875
+ envVar: "AGT_WEDGE_TRANSIENT_NOTICE_ENABLED",
36876
+ defaultValue: false,
36877
+ ...env2 ? { env: env2 } : {}
36878
+ });
36879
+ }
36880
+ var DEFAULT_FAILURE_WATCH_MS = 5 * 6e4;
36881
+ var FAST_POLL_MS = 500;
36882
+ var SLOW_POLL_MS = 3e3;
36883
+ var FAST_PHASE_MS = 1e4;
36884
+ var RETRYING_NOTICE_AFTER_MS = 3 * 6e4;
36885
+ var sleep2 = (ms) => new Promise((resolve3) => setTimeout(resolve3, ms));
36886
+ function classifyTurnFailureSince(opts) {
36887
+ const dir = opts.transcriptDir ?? agentTranscriptDir({ cwd: opts.cwd, home: opts.home });
36888
+ let entries;
36889
+ try {
36890
+ entries = readdirSync3(dir);
36891
+ } catch {
36892
+ return { result: UNKNOWN_TURN_FAILURE, coarse: 0, fine: 0, unrecognisedKeys: [] };
36893
+ }
36894
+ let newest = UNKNOWN_TURN_FAILURE;
36895
+ let coarse = 0;
36896
+ let fine = 0;
36897
+ const unrecognised = /* @__PURE__ */ new Set();
36898
+ for (const name of entries) {
36899
+ if (!name.endsWith(".jsonl")) continue;
36900
+ const path = join8(dir, name);
36901
+ let fingerprint;
36902
+ try {
36903
+ const st = statSync2(path);
36904
+ if (!st.isFile() || st.mtimeMs < opts.sinceMs) continue;
36905
+ fingerprint = `${st.mtimeMs}:${st.size}`;
36906
+ } catch {
36907
+ continue;
36908
+ }
36909
+ const cached2 = opts.cache?.get(path);
36910
+ let scan;
36911
+ if (cached2 && cached2.fingerprint === fingerprint) {
36912
+ scan = cached2.scan;
36913
+ } else {
36914
+ let content;
36915
+ try {
36916
+ content = readFileSync7(path, "utf-8");
36917
+ } catch {
36918
+ continue;
36919
+ }
36920
+ const analysis = analyzeTranscriptTurnFailure(content, opts.sinceMs, opts.nowMs);
36921
+ scan = {
36922
+ result: analysis.result,
36923
+ coarse: analysis.coarse,
36924
+ fine: analysis.fine,
36925
+ unrecognisedKeys: analysis.unrecognisedKeys
36926
+ };
36927
+ opts.cache?.set(path, { fingerprint, scan });
36928
+ }
36929
+ newest = pickNewerTurnFailure(newest, scan.result);
36930
+ coarse += scan.coarse;
36931
+ fine += scan.fine;
36932
+ for (const key2 of scan.unrecognisedKeys) unrecognised.add(key2);
36933
+ }
36934
+ return { result: newest, coarse, fine, unrecognisedKeys: [...unrecognised].sort() };
36935
+ }
36936
+ function emitDriftTelemetryIfBlind(args) {
36937
+ if (args.coarse <= 0 || args.fine > 0) return;
36938
+ try {
36939
+ process.stderr.write(
36940
+ `agt.transcript.api_error.unclassified ${JSON.stringify({
36941
+ channel: args.channel,
36942
+ agent_code: process.env.AGT_AGENT_CODE_NAME ?? "unknown",
36943
+ coarse: args.coarse,
36944
+ keys: args.unrecognisedKeys.slice(0, 20)
36945
+ })}
36946
+ `
36947
+ );
36948
+ } catch {
36949
+ }
36950
+ }
36951
+ async function watchForTurnFailure(opts) {
36952
+ const now = opts.now ?? (() => Date.now());
36953
+ const wait = opts.wait ?? sleep2;
36954
+ const timeoutMs = opts.timeoutMs ?? DEFAULT_FAILURE_WATCH_MS;
36955
+ const fastPollMs = opts.fastPollMs ?? FAST_POLL_MS;
36956
+ const slowPollMs = opts.slowPollMs ?? SLOW_POLL_MS;
36957
+ const fastPhaseMs = opts.fastPhaseMs ?? FAST_PHASE_MS;
36958
+ const retryingAfterMs = opts.retryingNoticeAfterMs ?? RETRYING_NOTICE_AFTER_MS;
36959
+ const startedAt = now();
36960
+ const deadline = startedAt + timeoutMs;
36961
+ let longRetryFired = false;
36962
+ let driftEmitted = false;
36963
+ const cache = /* @__PURE__ */ new Map();
36964
+ for (; ; ) {
36965
+ let scan;
36966
+ try {
36967
+ scan = classifyTurnFailureSince({
36968
+ sinceMs: opts.sinceMs,
36969
+ // No upper bound, deliberately. Two reasons, and the second is a
36970
+ // correctness requirement of the cache above:
36971
+ // 1. For a post-dispatch watch the question is "has anything since the
36972
+ // dispatch killed this turn?" — a future-dated entry is still an
36973
+ // answer, and rejecting it on clock skew would drop the notice.
36974
+ // 2. A window that widens each poll makes a cached scan unsound: an
36975
+ // entry stamped a hair ahead of our clock would be parsed as
36976
+ // out-of-window, cached as "not seen", and — because a DEAD turn
36977
+ // produces no further appends to invalidate the entry — never looked
36978
+ // at again. That is exactly the silence this feature exists to end.
36979
+ nowMs: Number.POSITIVE_INFINITY,
36980
+ transcriptDir: opts.transcriptDir,
36981
+ cwd: opts.cwd,
36982
+ home: opts.home,
36983
+ cache
36984
+ });
36985
+ } catch {
36986
+ return null;
36987
+ }
36988
+ if (!driftEmitted && scan.coarse > 0 && scan.fine === 0) {
36989
+ driftEmitted = true;
36990
+ emitDriftTelemetryIfBlind({
36991
+ channel: opts.channel ?? "unknown",
36992
+ coarse: scan.coarse,
36993
+ fine: scan.fine,
36994
+ unrecognisedKeys: scan.unrecognisedKeys
36995
+ });
36996
+ }
36997
+ const { result } = scan;
36998
+ if (result.outcome === "failed") return result;
36999
+ if (result.outcome === "served") return null;
37000
+ if (result.outcome === "retrying" && !longRetryFired && opts.onLongRetry && now() - startedAt >= retryingAfterMs) {
37001
+ longRetryFired = true;
37002
+ try {
37003
+ await opts.onLongRetry(result);
37004
+ } catch {
37005
+ }
37006
+ }
37007
+ if (now() >= deadline) return null;
37008
+ await wait(now() - startedAt < fastPhaseMs ? fastPollMs : slowPollMs);
37009
+ }
37010
+ }
37011
+
36709
37012
  // src/usage-limit-reactive-decision.ts
36710
37013
  import { createHash } from "crypto";
36711
37014
  function shouldReadPredictiveMarker(mode) {
@@ -36732,7 +37035,7 @@ function describeUnparsedRefusal(text) {
36732
37035
  import { execFile as execFile2 } from "child_process";
36733
37036
  import { existsSync as existsSync3, mkdirSync, writeFileSync as writeFileSync2 } from "fs";
36734
37037
  import { homedir as homedir6 } from "os";
36735
- import { join as join8 } from "path";
37038
+ import { join as join9 } from "path";
36736
37039
  var DEFAULT_CLAUDE_EVAL_MODEL = "claude-haiku-4-5-20251001";
36737
37040
  var DEFAULT_ANTHROPIC_MESSAGES_URL = "https://api.anthropic.com/v1/messages";
36738
37041
  var ANTHROPIC_API_VERSION = "2023-06-01";
@@ -36831,12 +37134,12 @@ async function runAnthropicMessages(prompt, opts) {
36831
37134
  var emptyMcpConfigPath = null;
36832
37135
  function ensureEmptyMcpConfig() {
36833
37136
  if (emptyMcpConfigPath && existsSync3(emptyMcpConfigPath)) return emptyMcpConfigPath;
36834
- const dir = join8(homedir6(), ".augmented");
37137
+ const dir = join9(homedir6(), ".augmented");
36835
37138
  try {
36836
37139
  mkdirSync(dir, { recursive: true });
36837
37140
  } catch {
36838
37141
  }
36839
- const p2 = join8(dir, ".reply-intent-empty-mcp.json");
37142
+ const p2 = join9(dir, ".reply-intent-empty-mcp.json");
36840
37143
  writeFileSync2(p2, JSON.stringify({ mcpServers: {} }));
36841
37144
  emptyMcpConfigPath = p2;
36842
37145
  return p2;
@@ -36983,8 +37286,8 @@ function emitTransientApiErrorTelemetry(channel, match, original) {
36983
37286
  }
36984
37287
 
36985
37288
  // src/slack-pending-inbound-cleanup.ts
36986
- import { existsSync as existsSync4, readdirSync as readdirSync3, statSync as statSync2, unlinkSync } from "fs";
36987
- import { join as join9 } from "path";
37289
+ import { existsSync as existsSync4, readdirSync as readdirSync4, statSync as statSync3, unlinkSync } from "fs";
37290
+ import { join as join10 } from "path";
36988
37291
  function sanitizeMarkerSegment(value) {
36989
37292
  return value.replace(/[^A-Za-z0-9_-]/g, "_");
36990
37293
  }
@@ -36998,9 +37301,9 @@ function applyToMatchingMarkers(dir, prefix, suffix, op) {
36998
37301
  if (!dir) return 0;
36999
37302
  let applied = 0;
37000
37303
  try {
37001
- for (const f of readdirSync3(dir)) {
37304
+ for (const f of readdirSync4(dir)) {
37002
37305
  if (!f.startsWith(prefix) || !f.endsWith(suffix)) continue;
37003
- op(join9(dir, f));
37306
+ op(join10(dir, f));
37004
37307
  applied += 1;
37005
37308
  }
37006
37309
  } catch {
@@ -37029,11 +37332,11 @@ function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultCle
37029
37332
  if (!dir) return null;
37030
37333
  const channelPrefix = `${sanitizeMarkerSegment(channel)}__`;
37031
37334
  try {
37032
- const entries = readdirSync3(dir).filter((f) => f.startsWith(channelPrefix) && f.endsWith(".json")).map((f) => {
37033
- const full = join9(dir, f);
37335
+ const entries = readdirSync4(dir).filter((f) => f.startsWith(channelPrefix) && f.endsWith(".json")).map((f) => {
37336
+ const full = join10(dir, f);
37034
37337
  let mtime = 0;
37035
37338
  try {
37036
- mtime = statSync2(full).mtimeMs;
37339
+ mtime = statSync3(full).mtimeMs;
37037
37340
  } catch {
37038
37341
  }
37039
37342
  return { name: f, full, mtime };
@@ -37049,12 +37352,12 @@ function clearOldestSlackPendingMarkerInChannel(dir, channel, clear = defaultCle
37049
37352
 
37050
37353
  // src/recovery-ledger.ts
37051
37354
  import { existsSync as existsSync5, unlinkSync as unlinkSync2 } from "fs";
37052
- import { join as join10 } from "path";
37355
+ import { join as join11 } from "path";
37053
37356
  function recoveryLedgerEntryExists(ledgerDir, markerName, exists = (p2) => existsSync5(p2)) {
37054
37357
  if (!ledgerDir || !markerName) return false;
37055
37358
  if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return false;
37056
37359
  try {
37057
- return exists(join10(ledgerDir, markerName));
37360
+ return exists(join11(ledgerDir, markerName));
37058
37361
  } catch {
37059
37362
  return false;
37060
37363
  }
@@ -37065,7 +37368,7 @@ function removeRecoveryLedgerEntry(ledgerDir, markerName, unlink = (p2) => {
37065
37368
  if (!ledgerDir || !markerName) return;
37066
37369
  if (markerName.includes("/") || markerName.includes("\\") || markerName.includes("..")) return;
37067
37370
  try {
37068
- unlink(join10(ledgerDir, markerName));
37371
+ unlink(join11(ledgerDir, markerName));
37069
37372
  } catch {
37070
37373
  }
37071
37374
  }
@@ -37302,18 +37605,18 @@ function applyHotThreadGuard(input) {
37302
37605
  }
37303
37606
 
37304
37607
  // src/slack-hot-thread-telemetry.ts
37305
- import { readFileSync as readFileSync7, writeFileSync as writeFileSync3 } from "fs";
37306
- import { join as join11 } from "path";
37608
+ import { readFileSync as readFileSync8, writeFileSync as writeFileSync3 } from "fs";
37609
+ import { join as join12 } from "path";
37307
37610
  var HOT_THREAD_CLASSIFICATION_COUNTER_SUFFIX = "-hot-thread-classifications.json";
37308
37611
  function hotThreadKey(mode, outcome, proactive) {
37309
37612
  return `${mode}|${outcome}|${proactive ? "true" : "false"}`;
37310
37613
  }
37311
37614
  function recordHotThreadClassification(agentDir, channel, classification) {
37312
37615
  if (!agentDir || !channel) return;
37313
- const path = join11(agentDir, `${channel}${HOT_THREAD_CLASSIFICATION_COUNTER_SUFFIX}`);
37616
+ const path = join12(agentDir, `${channel}${HOT_THREAD_CLASSIFICATION_COUNTER_SUFFIX}`);
37314
37617
  let counts = {};
37315
37618
  try {
37316
- const parsed = JSON.parse(readFileSync7(path, "utf-8"));
37619
+ const parsed = JSON.parse(readFileSync8(path, "utf-8"));
37317
37620
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
37318
37621
  for (const [k, v] of Object.entries(parsed)) {
37319
37622
  if (typeof v === "number" && Number.isInteger(v) && v >= 0) counts[k] = v;
@@ -37330,7 +37633,7 @@ function recordHotThreadClassification(agentDir, channel, classification) {
37330
37633
  }
37331
37634
 
37332
37635
  // src/restart-confirm.ts
37333
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync8, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
37636
+ import { existsSync as existsSync6, mkdirSync as mkdirSync2, readFileSync as readFileSync9, renameSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync4 } from "fs";
37334
37637
  import { dirname } from "path";
37335
37638
  import { randomUUID } from "crypto";
37336
37639
  var RESTART_CONFIRM_MAX_AGE_MS = 10 * 60 * 1e3;
@@ -37356,7 +37659,7 @@ function writeRestartConfirmMarker(filePath, marker) {
37356
37659
  function readRestartConfirmMarker(filePath) {
37357
37660
  try {
37358
37661
  if (!existsSync6(filePath)) return null;
37359
- const parsed = JSON.parse(readFileSync8(filePath, "utf8"));
37662
+ const parsed = JSON.parse(readFileSync9(filePath, "utf8"));
37360
37663
  if (!parsed || typeof parsed !== "object") return null;
37361
37664
  return parsed;
37362
37665
  } catch {
@@ -37501,21 +37804,21 @@ import {
37501
37804
  ftruncateSync,
37502
37805
  mkdirSync as mkdirSync9,
37503
37806
  openSync,
37504
- readFileSync as readFileSync20,
37505
- readdirSync as readdirSync6,
37807
+ readFileSync as readFileSync21,
37808
+ readdirSync as readdirSync7,
37506
37809
  renameSync as renameSync5,
37507
- statSync as statSync4,
37810
+ statSync as statSync5,
37508
37811
  unlinkSync as unlinkSync7,
37509
37812
  watch,
37510
37813
  writeFileSync as writeFileSync14,
37511
37814
  writeSync
37512
37815
  } from "fs";
37513
- import { basename, join as join20, resolve as resolve2 } from "path";
37816
+ import { basename, join as join21, resolve as resolve2 } from "path";
37514
37817
  import { homedir as homedir7 } from "os";
37515
37818
  import { createHash as createHash3, randomUUID as randomUUID2 } from "crypto";
37516
37819
 
37517
37820
  // src/slack-thread-store.ts
37518
- import { mkdirSync as mkdirSync3, readFileSync as readFileSync9, writeFileSync as writeFileSync5 } from "fs";
37821
+ import { mkdirSync as mkdirSync3, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "fs";
37519
37822
  import { dirname as dirname2 } from "path";
37520
37823
  function isParticipatingThread(entry) {
37521
37824
  if (!entry) return false;
@@ -37530,7 +37833,7 @@ function loadThreadStore(filePath, opts = {}) {
37530
37833
  const ttlMs = ttlDays * 24 * 60 * 60 * 1e3;
37531
37834
  let raw;
37532
37835
  try {
37533
- raw = readFileSync9(filePath, "utf-8");
37836
+ raw = readFileSync10(filePath, "utf-8");
37534
37837
  } catch {
37535
37838
  return { threads: /* @__PURE__ */ new Map(), pruned: 0 };
37536
37839
  }
@@ -37631,7 +37934,7 @@ function isThreadEntry(value) {
37631
37934
  }
37632
37935
 
37633
37936
  // src/dm-restart-notice.ts
37634
- import { mkdirSync as mkdirSync4, readFileSync as readFileSync10, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
37937
+ import { mkdirSync as mkdirSync4, readFileSync as readFileSync11, unlinkSync as unlinkSync4, writeFileSync as writeFileSync6 } from "fs";
37635
37938
  import { dirname as dirname3 } from "path";
37636
37939
  var RECENT_DM_VERSION = 1;
37637
37940
  var DEFAULT_RECENT_DM_TTL_MS = 30 * 60 * 1e3;
@@ -37655,7 +37958,7 @@ function loadRecentDms(filePath, opts = {}) {
37655
37958
  const ttlMs = opts.ttlMs ?? DEFAULT_RECENT_DM_TTL_MS;
37656
37959
  let raw;
37657
37960
  try {
37658
- raw = readFileSync10(filePath, "utf-8");
37961
+ raw = readFileSync11(filePath, "utf-8");
37659
37962
  } catch (err) {
37660
37963
  const code = err.code;
37661
37964
  if (code !== "ENOENT") {
@@ -37740,7 +38043,7 @@ var CHANNEL_ADD_RESTART_MAX_AGE_MS = 15 * 60 * 1e3;
37740
38043
  function readChannelAddRestartMarker(filePath) {
37741
38044
  let raw;
37742
38045
  try {
37743
- raw = readFileSync10(filePath, "utf-8");
38046
+ raw = readFileSync11(filePath, "utf-8");
37744
38047
  } catch {
37745
38048
  return null;
37746
38049
  }
@@ -37798,13 +38101,13 @@ async function runOrRetry(fn, opts) {
37798
38101
  }
37799
38102
 
37800
38103
  // src/turn-initiator-marker.ts
37801
- import { writeFileSync as writeFileSync7, readFileSync as readFileSync11, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
37802
- import { dirname as dirname4, join as join12 } from "path";
38104
+ import { writeFileSync as writeFileSync7, readFileSync as readFileSync12, mkdirSync as mkdirSync5, renameSync as renameSync2 } from "fs";
38105
+ import { dirname as dirname4, join as join13 } from "path";
37803
38106
  var TURN_INITIATOR_MAX_AGE_MS = 5 * 60 * 1e3;
37804
38107
  var TURN_INITIATOR_LEDGER_MAX_ENTRIES = 20;
37805
38108
  var TURN_INITIATOR_LEDGER_FILENAME = ".turn-initiator-ledger.json";
37806
38109
  function turnInitiatorLedgerPath(singleSlotFile) {
37807
- return join12(dirname4(singleSlotFile), TURN_INITIATOR_LEDGER_FILENAME);
38110
+ return join13(dirname4(singleSlotFile), TURN_INITIATOR_LEDGER_FILENAME);
37808
38111
  }
37809
38112
  function foldTurnInitiatorLedger(existing, marker, maxAgeMs = TURN_INITIATOR_MAX_AGE_MS, maxEntries = TURN_INITIATOR_LEDGER_MAX_ENTRIES) {
37810
38113
  const now = marker.ts;
@@ -37829,7 +38132,7 @@ function updateTurnInitiatorLedger(singleSlotFile, marker) {
37829
38132
  const ledgerFile = turnInitiatorLedgerPath(singleSlotFile);
37830
38133
  let existing = null;
37831
38134
  try {
37832
- const parsed = JSON.parse(readFileSync11(ledgerFile, "utf8"));
38135
+ const parsed = JSON.parse(readFileSync12(ledgerFile, "utf8"));
37833
38136
  if (parsed && parsed.v === 1 && Array.isArray(parsed.entries)) existing = parsed;
37834
38137
  } catch {
37835
38138
  }
@@ -37856,7 +38159,7 @@ function writeTurnInitiatorMarker(input) {
37856
38159
  }
37857
38160
 
37858
38161
  // src/slack-bot-photo.ts
37859
- import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync8 } from "fs";
38162
+ import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "fs";
37860
38163
  import { dirname as dirname5 } from "path";
37861
38164
  async function applyBotPhoto(opts) {
37862
38165
  const fetchImpl = opts.fetchImpl ?? fetch;
@@ -37866,7 +38169,7 @@ async function applyBotPhoto(opts) {
37866
38169
  const { token, avatarUrl, markerPath } = opts;
37867
38170
  if (markerPath && existsSync7(markerPath)) {
37868
38171
  try {
37869
- if (readFileSync12(markerPath, "utf-8").trim() === avatarUrl) {
38172
+ if (readFileSync13(markerPath, "utf-8").trim() === avatarUrl) {
37870
38173
  return { status: "skipped-unchanged" };
37871
38174
  }
37872
38175
  } catch {
@@ -37991,8 +38294,8 @@ function conversationalLaneMeta(expectsReply = true) {
37991
38294
  }
37992
38295
 
37993
38296
  // src/inbound-lane-telemetry.ts
37994
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync9 } from "fs";
37995
- import { join as join13 } from "path";
38297
+ import { readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "fs";
38298
+ import { join as join14 } from "path";
37996
38299
  var LANE_CLASSIFICATION_COUNTER_SUFFIX = "-lane-classifications.json";
37997
38300
  var SUSPECTED_MISCLASSIFICATION_KEY = "suspected_misclassification";
37998
38301
  var HUMAN_CHANNEL_SOURCES = /* @__PURE__ */ new Set([
@@ -38009,10 +38312,10 @@ function isSuspectedMisclassification(lane, source) {
38009
38312
  }
38010
38313
  function recordLaneClassification(agentDir, channel, classification) {
38011
38314
  if (!agentDir) return;
38012
- const path = join13(agentDir, `${channel}${LANE_CLASSIFICATION_COUNTER_SUFFIX}`);
38315
+ const path = join14(agentDir, `${channel}${LANE_CLASSIFICATION_COUNTER_SUFFIX}`);
38013
38316
  let counts = {};
38014
38317
  try {
38015
- const parsed = JSON.parse(readFileSync13(path, "utf-8"));
38318
+ const parsed = JSON.parse(readFileSync14(path, "utf-8"));
38016
38319
  if (parsed && typeof parsed === "object") counts = parsed;
38017
38320
  } catch {
38018
38321
  }
@@ -38028,8 +38331,8 @@ function recordLaneClassification(agentDir, channel, classification) {
38028
38331
  }
38029
38332
 
38030
38333
  // src/slack-inbound-registry.ts
38031
- import { readdirSync as readdirSync4, readFileSync as readFileSync14 } from "fs";
38032
- import { join as join14 } from "path";
38334
+ import { readdirSync as readdirSync5, readFileSync as readFileSync15 } from "fs";
38335
+ import { join as join15 } from "path";
38033
38336
  var DEFAULT_MAX_ENTRIES = 500;
38034
38337
  var DEFAULT_CLEARED_TTL_MS = 6 * 60 * 60 * 1e3;
38035
38338
  function entryKey(channel, messageTs) {
@@ -38141,7 +38444,7 @@ function createInboundRegistry(opts = {}) {
38141
38444
  function seedFromMarkerDir(dir) {
38142
38445
  let names;
38143
38446
  try {
38144
- names = readdirSync4(dir);
38447
+ names = readdirSync5(dir);
38145
38448
  } catch {
38146
38449
  return;
38147
38450
  }
@@ -38150,7 +38453,7 @@ function createInboundRegistry(opts = {}) {
38150
38453
  if (name.includes(".retry-") || name.includes(".poison")) continue;
38151
38454
  let marker;
38152
38455
  try {
38153
- marker = JSON.parse(readFileSync14(join14(dir, name), "utf-8"));
38456
+ marker = JSON.parse(readFileSync15(join15(dir, name), "utf-8"));
38154
38457
  } catch {
38155
38458
  continue;
38156
38459
  }
@@ -38195,8 +38498,8 @@ function slackInboundId(channel, threadTs, messageTs) {
38195
38498
  }
38196
38499
 
38197
38500
  // src/inbound-delivery-ledger.ts
38198
- import { existsSync as existsSync8, mkdirSync as mkdirSync7, readdirSync as readdirSync5, readFileSync as readFileSync15, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
38199
- import { join as join15 } from "path";
38501
+ import { existsSync as existsSync8, mkdirSync as mkdirSync7, readdirSync as readdirSync6, readFileSync as readFileSync16, renameSync as renameSync3, writeFileSync as writeFileSync10 } from "fs";
38502
+ import { join as join16 } from "path";
38200
38503
  function safeInboundId(inboundId) {
38201
38504
  return inboundId.replace(/[^A-Za-z0-9_-]/g, "_");
38202
38505
  }
@@ -38204,8 +38507,8 @@ var defaultDeps = {
38204
38507
  mkdir: (dir) => mkdirSync7(dir, { recursive: true }),
38205
38508
  writeFile: (path, data) => writeFileSync10(path, data, "utf8"),
38206
38509
  rename: (from, to) => renameSync3(from, to),
38207
- readdir: (dir) => readdirSync5(dir),
38208
- readFile: (path) => readFileSync15(path, "utf8"),
38510
+ readdir: (dir) => readdirSync6(dir),
38511
+ readFile: (path) => readFileSync16(path, "utf8"),
38209
38512
  exists: (path) => existsSync8(path)
38210
38513
  };
38211
38514
  function writeInboundDeliveryLedgerEntry(dir, record2, deps = defaultDeps) {
@@ -38214,7 +38517,7 @@ function writeInboundDeliveryLedgerEntry(dir, record2, deps = defaultDeps) {
38214
38517
  if (!safe || safe.includes("/") || safe.includes("\\") || safe.includes("..")) return;
38215
38518
  try {
38216
38519
  deps.mkdir(dir);
38217
- const final = join15(dir, `${safe}.json`);
38520
+ const final = join16(dir, `${safe}.json`);
38218
38521
  const tmp = `${final}.tmp`;
38219
38522
  deps.writeFile(tmp, JSON.stringify(record2));
38220
38523
  deps.rename(tmp, final);
@@ -38370,8 +38673,8 @@ function describeChannelRedirect(input) {
38370
38673
  }
38371
38674
 
38372
38675
  // src/slack-reply-binding-telemetry.ts
38373
- import { readFileSync as readFileSync16, writeFileSync as writeFileSync11 } from "fs";
38374
- import { join as join16 } from "path";
38676
+ import { readFileSync as readFileSync17, writeFileSync as writeFileSync11 } from "fs";
38677
+ import { join as join17 } from "path";
38375
38678
  var REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX = "-reply-binding-classifications.json";
38376
38679
  var UNKNOWN_INBOUND_ID_KEY = "unknown_inbound_id";
38377
38680
  var CHANNEL_MISTARGET_CORRECTED_KEY = "channel_mistarget_corrected";
@@ -38386,10 +38689,10 @@ function slackReplyBindingMode() {
38386
38689
  }
38387
38690
  function recordReplyBindingClassification(agentDir, channel, input) {
38388
38691
  if (!agentDir) return;
38389
- const path = join16(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38692
+ const path = join17(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38390
38693
  let counts = {};
38391
38694
  try {
38392
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
38695
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
38393
38696
  if (parsed && typeof parsed === "object") counts = parsed;
38394
38697
  } catch {
38395
38698
  }
@@ -38404,10 +38707,10 @@ function recordReplyBindingClassification(agentDir, channel, input) {
38404
38707
  }
38405
38708
  function recordScheduledChannelOverride(agentDir, channel, input) {
38406
38709
  if (!agentDir) return;
38407
- const path = join16(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38710
+ const path = join17(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38408
38711
  let counts = {};
38409
38712
  try {
38410
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
38713
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
38411
38714
  if (parsed && typeof parsed === "object") counts = parsed;
38412
38715
  } catch {
38413
38716
  }
@@ -38420,10 +38723,10 @@ function recordScheduledChannelOverride(agentDir, channel, input) {
38420
38723
  }
38421
38724
  function recordChannelMistarget(agentDir, channel, input) {
38422
38725
  if (!agentDir) return;
38423
- const path = join16(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38726
+ const path = join17(agentDir, `${channel}${REPLY_BINDING_CLASSIFICATION_COUNTER_SUFFIX}`);
38424
38727
  let counts = {};
38425
38728
  try {
38426
- const parsed = JSON.parse(readFileSync16(path, "utf-8"));
38729
+ const parsed = JSON.parse(readFileSync17(path, "utf-8"));
38427
38730
  if (parsed && typeof parsed === "object") counts = parsed;
38428
38731
  } catch {
38429
38732
  }
@@ -38438,8 +38741,8 @@ function recordChannelMistarget(agentDir, channel, input) {
38438
38741
  }
38439
38742
 
38440
38743
  // src/slack-reply-target-telemetry.ts
38441
- import { readFileSync as readFileSync17, writeFileSync as writeFileSync12 } from "fs";
38442
- import { join as join17 } from "path";
38744
+ import { readFileSync as readFileSync18, writeFileSync as writeFileSync12 } from "fs";
38745
+ import { join as join18 } from "path";
38443
38746
  var REPLY_TARGET_CLASSIFICATION_COUNTER_SUFFIX = "-reply-target-classifications.json";
38444
38747
  function pendingThreadsBucket(n) {
38445
38748
  if (n <= 0) return "0";
@@ -38472,10 +38775,10 @@ function classifyReplyTarget(input) {
38472
38775
  }
38473
38776
  function recordReplyTargetClassification(agentDir, channel, classification) {
38474
38777
  if (!agentDir) return;
38475
- const path = join17(agentDir, `${channel}${REPLY_TARGET_CLASSIFICATION_COUNTER_SUFFIX}`);
38778
+ const path = join18(agentDir, `${channel}${REPLY_TARGET_CLASSIFICATION_COUNTER_SUFFIX}`);
38476
38779
  let counts = {};
38477
38780
  try {
38478
- const parsed = JSON.parse(readFileSync17(path, "utf-8"));
38781
+ const parsed = JSON.parse(readFileSync18(path, "utf-8"));
38479
38782
  if (parsed && typeof parsed === "object") counts = parsed;
38480
38783
  } catch {
38481
38784
  }
@@ -38491,8 +38794,8 @@ function recordReplyTargetClassification(agentDir, channel, classification) {
38491
38794
  }
38492
38795
 
38493
38796
  // src/scheduled-turn-marker.ts
38494
- import { readFileSync as readFileSync18, unlinkSync as unlinkSync5 } from "fs";
38495
- import { join as join18 } from "path";
38797
+ import { readFileSync as readFileSync19, unlinkSync as unlinkSync5 } from "fs";
38798
+ import { join as join19 } from "path";
38496
38799
  var SCHEDULED_TURN_MARKER_FILENAME2 = ".current-scheduled-turn.json";
38497
38800
  var SCHEDULED_TURN_MAX_AGE_MS = 15 * 60 * 1e3;
38498
38801
  function validateScheduledTurnMarker(raw, now, maxAgeMs = SCHEDULED_TURN_MAX_AGE_MS) {
@@ -38530,7 +38833,7 @@ function readScheduledTurnMarker(agentDir, now = Date.now()) {
38530
38833
  if (!agentDir) return null;
38531
38834
  try {
38532
38835
  const raw = JSON.parse(
38533
- readFileSync18(join18(agentDir, SCHEDULED_TURN_MARKER_FILENAME2), "utf8")
38836
+ readFileSync19(join19(agentDir, SCHEDULED_TURN_MARKER_FILENAME2), "utf8")
38534
38837
  );
38535
38838
  return validateScheduledTurnMarker(raw, now);
38536
38839
  } catch {
@@ -38540,7 +38843,7 @@ function readScheduledTurnMarker(agentDir, now = Date.now()) {
38540
38843
  function clearScheduledTurnMarker(agentDir) {
38541
38844
  if (!agentDir) return;
38542
38845
  try {
38543
- unlinkSync5(join18(agentDir, SCHEDULED_TURN_MARKER_FILENAME2));
38846
+ unlinkSync5(join19(agentDir, SCHEDULED_TURN_MARKER_FILENAME2));
38544
38847
  } catch {
38545
38848
  }
38546
38849
  }
@@ -39338,14 +39641,14 @@ async function actuateHostRestart(opts) {
39338
39641
  import {
39339
39642
  existsSync as existsSync9,
39340
39643
  mkdirSync as mkdirSync8,
39341
- readFileSync as readFileSync19,
39644
+ readFileSync as readFileSync20,
39342
39645
  renameSync as renameSync4,
39343
- statSync as statSync3,
39646
+ statSync as statSync4,
39344
39647
  unlinkSync as unlinkSync6,
39345
39648
  utimesSync,
39346
39649
  writeFileSync as writeFileSync13
39347
39650
  } from "fs";
39348
- import { join as join19 } from "path";
39651
+ import { join as join20 } from "path";
39349
39652
  var STALE_LOCK_MS = 9e4;
39350
39653
  var HEARTBEAT_INTERVAL_MS = 3e4;
39351
39654
  function defaultIsPidAlive(pid) {
@@ -39368,7 +39671,7 @@ function acquireMcpSpawnLock(args) {
39368
39671
  const nowMs = options.nowMs ?? (() => Date.now());
39369
39672
  const lockMtimeMs = options.lockMtimeMs ?? defaultLockMtimeMs;
39370
39673
  const staleMs = options.staleMs ?? STALE_LOCK_MS;
39371
- const path = join19(agentDir, basename2);
39674
+ const path = join20(agentDir, basename2);
39372
39675
  const existing = readLockHolder(path);
39373
39676
  if (existing) {
39374
39677
  if (existing.pid === selfPid) {
@@ -39427,7 +39730,7 @@ function startMcpSpawnLockHeartbeat(lockPath, opts = {}) {
39427
39730
  }
39428
39731
  function defaultLockMtimeMs(path) {
39429
39732
  try {
39430
- return statSync3(path).mtimeMs;
39733
+ return statSync4(path).mtimeMs;
39431
39734
  } catch {
39432
39735
  return null;
39433
39736
  }
@@ -39435,7 +39738,7 @@ function defaultLockMtimeMs(path) {
39435
39738
  function readLockHolder(path) {
39436
39739
  if (!existsSync9(path)) return null;
39437
39740
  try {
39438
- const raw = readFileSync19(path, "utf8");
39741
+ const raw = readFileSync20(path, "utf8");
39439
39742
  const parsed = JSON.parse(raw);
39440
39743
  const pid = typeof parsed.pid === "number" ? parsed.pid : Number(parsed.pid);
39441
39744
  if (!Number.isFinite(pid) || pid <= 0) return null;
@@ -39884,18 +40187,18 @@ var SLACK_TEAM_PEER_USER_IDS = parseTeamPeerUserIdsEnv(
39884
40187
  process.env.SLACK_TEAM_PEER_USER_IDS
39885
40188
  );
39886
40189
  var PEER_HINT_SEEN = /* @__PURE__ */ new Set();
39887
- var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join20(homedir7(), ".augmented", AGENT_CODE_NAME) : null;
39888
- var SLACK_MCP_CONFIG_PATH = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "project", ".mcp.json") : null;
40190
+ var SLACK_AGENT_DIR = AGENT_CODE_NAME ? join21(homedir7(), ".augmented", AGENT_CODE_NAME) : null;
40191
+ var SLACK_MCP_CONFIG_PATH = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "project", ".mcp.json") : null;
39889
40192
  var liveAllowedUsersCache = null;
39890
40193
  function readLiveAllowedUsers() {
39891
40194
  if (!SLACK_MCP_CONFIG_PATH) return null;
39892
40195
  try {
39893
- const mtimeMs = statSync4(SLACK_MCP_CONFIG_PATH).mtimeMs;
40196
+ const mtimeMs = statSync5(SLACK_MCP_CONFIG_PATH).mtimeMs;
39894
40197
  if (liveAllowedUsersCache && liveAllowedUsersCache.mtimeMs === mtimeMs) {
39895
40198
  return liveAllowedUsersCache.value;
39896
40199
  }
39897
40200
  const value = extractAllowedUsersFromMcpJson(
39898
- readFileSync20(SLACK_MCP_CONFIG_PATH, "utf-8")
40201
+ readFileSync21(SLACK_MCP_CONFIG_PATH, "utf-8")
39899
40202
  );
39900
40203
  if (value === null) return null;
39901
40204
  liveAllowedUsersCache = { mtimeMs, value };
@@ -39911,12 +40214,12 @@ var livePingAllowedUsersCache = null;
39911
40214
  function readLivePingAllowedUsers() {
39912
40215
  if (!SLACK_MCP_CONFIG_PATH) return null;
39913
40216
  try {
39914
- const mtimeMs = statSync4(SLACK_MCP_CONFIG_PATH).mtimeMs;
40217
+ const mtimeMs = statSync5(SLACK_MCP_CONFIG_PATH).mtimeMs;
39915
40218
  if (livePingAllowedUsersCache && livePingAllowedUsersCache.mtimeMs === mtimeMs) {
39916
40219
  return livePingAllowedUsersCache.value;
39917
40220
  }
39918
40221
  const value = extractPingAllowedUsersFromMcpJson(
39919
- readFileSync20(SLACK_MCP_CONFIG_PATH, "utf-8")
40222
+ readFileSync21(SLACK_MCP_CONFIG_PATH, "utf-8")
39920
40223
  );
39921
40224
  if (value === null) return null;
39922
40225
  livePingAllowedUsersCache = { mtimeMs, value };
@@ -39928,17 +40231,17 @@ function readLivePingAllowedUsers() {
39928
40231
  function getEffectivePingAllowedUsers() {
39929
40232
  return readLivePingAllowedUsers() ?? PING_ALLOWED_USERS;
39930
40233
  }
39931
- var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
39932
- var SLACK_RESTART_CONTEXT_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-restart-context") : null;
39933
- var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
39934
- var SLACK_RECOVERY_LEDGER_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, ".agt-slack-recovery-ledger") : null;
39935
- var SLACK_DELIVERY_LEDGER_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, ".agt-inbound-delivery-ledger") : null;
40234
+ var SLACK_PENDING_INBOUND_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-pending-inbound") : null;
40235
+ var SLACK_RESTART_CONTEXT_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-restart-context") : null;
40236
+ var SLACK_RECOVERY_OUTBOX_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-recovery-outbox") : null;
40237
+ var SLACK_RECOVERY_LEDGER_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, ".agt-slack-recovery-ledger") : null;
40238
+ var SLACK_DELIVERY_LEDGER_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, ".agt-inbound-delivery-ledger") : null;
39936
40239
  var slackInboundRegistry = createInboundRegistry();
39937
- var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
39938
- var SLACK_RECENT_DMS_FILE = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-recent-dms.json") : null;
39939
- var SLACK_CHANNEL_ADD_RESTART_FILE = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-channel-add-restart.json") : null;
40240
+ var SLACK_RESTART_CONFIRM_FILE = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-restart-confirm.json") : null;
40241
+ var SLACK_RECENT_DMS_FILE = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-recent-dms.json") : null;
40242
+ var SLACK_CHANNEL_ADD_RESTART_FILE = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-channel-add-restart.json") : null;
39940
40243
  var SLACK_MAX_RECOVERY_ATTEMPTS = 3;
39941
- var SLACK_AVATAR_MARKER_PATH = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-avatar-applied") : null;
40244
+ var SLACK_AVATAR_MARKER_PATH = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-avatar-applied") : null;
39942
40245
  function redactSlackId(id) {
39943
40246
  if (!id) return "<none>";
39944
40247
  return createHash3("sha256").update(id).digest("hex").slice(0, 8);
@@ -39949,7 +40252,7 @@ function safeSlackMarkerName(channel, threadTs, messageTs) {
39949
40252
  }
39950
40253
  function slackPendingInboundPath(channel, threadTs, messageTs) {
39951
40254
  if (!SLACK_PENDING_INBOUND_DIR) return null;
39952
- return join20(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
40255
+ return join21(SLACK_PENDING_INBOUND_DIR, safeSlackMarkerName(channel, threadTs, messageTs));
39953
40256
  }
39954
40257
  function writeSlackPendingInboundMarker(channel, threadTs, messageTs, undeliverable = false, discretionary = false, payload) {
39955
40258
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
@@ -40000,7 +40303,7 @@ function rewriteSlackMarkerInPlace(path, marker) {
40000
40303
  function markSlackMarkerSeenInPlace(fullPath) {
40001
40304
  let marker;
40002
40305
  try {
40003
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
40306
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
40004
40307
  } catch {
40005
40308
  return;
40006
40309
  }
@@ -40014,7 +40317,7 @@ function attachSlackReplayPayload(channel, threadTs, messageTs, payload) {
40014
40317
  if (!path) return;
40015
40318
  let marker;
40016
40319
  try {
40017
- marker = JSON.parse(readFileSync20(path, "utf-8"));
40320
+ marker = JSON.parse(readFileSync21(path, "utf-8"));
40018
40321
  } catch {
40019
40322
  return;
40020
40323
  }
@@ -40025,7 +40328,7 @@ function readSlackPendingInboundMarker(channel, threadTs, messageTs) {
40025
40328
  const path = slackPendingInboundPath(channel, threadTs, messageTs);
40026
40329
  if (!path || !existsSync10(path)) return null;
40027
40330
  try {
40028
- return JSON.parse(readFileSync20(path, "utf-8"));
40331
+ return JSON.parse(readFileSync21(path, "utf-8"));
40029
40332
  } catch {
40030
40333
  return null;
40031
40334
  }
@@ -40141,7 +40444,7 @@ function scheduleBusyAck(channel, threadTs, messageTs, isThreadReply, arrivedWhi
40141
40444
  let paneLogFreshAgeMs = null;
40142
40445
  if (SLACK_AGENT_DIR) {
40143
40446
  try {
40144
- const paneMtimeMs = statSync4(join20(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
40447
+ const paneMtimeMs = statSync5(join21(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
40145
40448
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
40146
40449
  } catch {
40147
40450
  }
@@ -40174,7 +40477,7 @@ function __resetSlackBusyAckNoticeThrottle() {
40174
40477
  function clearSlackMarkerFileWithHeal(fullPath) {
40175
40478
  let marker = null;
40176
40479
  try {
40177
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
40480
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
40178
40481
  } catch {
40179
40482
  }
40180
40483
  if (marker && decideRecoveryHeal({
@@ -40192,7 +40495,7 @@ function clearSlackMarkerFileWithHeal(fullPath) {
40192
40495
  function markSlackMarkerSeenWithHeal(fullPath) {
40193
40496
  let marker = null;
40194
40497
  try {
40195
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
40498
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
40196
40499
  } catch {
40197
40500
  return;
40198
40501
  }
@@ -40261,10 +40564,10 @@ function slackNextRetryName(filename) {
40261
40564
  async function processSlackRecoveryOutboxFile(filename) {
40262
40565
  if (!SLACK_RECOVERY_OUTBOX_DIR) return;
40263
40566
  if (filename.endsWith(".poison.json") || filename.endsWith(".tmp")) return;
40264
- const fullPath = join20(SLACK_RECOVERY_OUTBOX_DIR, filename);
40567
+ const fullPath = join21(SLACK_RECOVERY_OUTBOX_DIR, filename);
40265
40568
  let payload;
40266
40569
  try {
40267
- payload = JSON.parse(readFileSync20(fullPath, "utf-8"));
40570
+ payload = JSON.parse(readFileSync21(fullPath, "utf-8"));
40268
40571
  } catch (err) {
40269
40572
  process.stderr.write(
40270
40573
  `slack-channel(${AGENT_CODE_NAME}): recovery outbox parse failed (${filename}): ${err.message}
@@ -40368,7 +40671,7 @@ async function processSlackRecoveryOutboxFile(filename) {
40368
40671
  const next = slackNextRetryName(filename);
40369
40672
  if (next) {
40370
40673
  try {
40371
- renameSync5(fullPath, join20(SLACK_RECOVERY_OUTBOX_DIR, next.next));
40674
+ renameSync5(fullPath, join21(SLACK_RECOVERY_OUTBOX_DIR, next.next));
40372
40675
  if (next.attempt >= SLACK_MAX_RECOVERY_ATTEMPTS) {
40373
40676
  process.stderr.write(
40374
40677
  `slack-channel(${AGENT_CODE_NAME}): ghost-reply recovery exhausted retries \u2014 moved to ${next.next}
@@ -40399,7 +40702,7 @@ function scanSlackRecoveryRetries() {
40399
40702
  if (!SLACK_RECOVERY_OUTBOX_DIR) return;
40400
40703
  let entries;
40401
40704
  try {
40402
- entries = readdirSync6(SLACK_RECOVERY_OUTBOX_DIR);
40705
+ entries = readdirSync7(SLACK_RECOVERY_OUTBOX_DIR);
40403
40706
  } catch {
40404
40707
  return;
40405
40708
  }
@@ -40408,7 +40711,7 @@ function scanSlackRecoveryRetries() {
40408
40711
  if (!f.includes(".retry-") || f.endsWith(".poison.json")) continue;
40409
40712
  let mtimeMs;
40410
40713
  try {
40411
- mtimeMs = statSync4(join20(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
40714
+ mtimeMs = statSync5(join21(SLACK_RECOVERY_OUTBOX_DIR, f)).mtimeMs;
40412
40715
  } catch {
40413
40716
  continue;
40414
40717
  }
@@ -40429,7 +40732,7 @@ function startSlackRecoveryOutboxWatcher() {
40429
40732
  return;
40430
40733
  }
40431
40734
  try {
40432
- for (const f of readdirSync6(SLACK_RECOVERY_OUTBOX_DIR)) {
40735
+ for (const f of readdirSync7(SLACK_RECOVERY_OUTBOX_DIR)) {
40433
40736
  if (isFirstAttemptSlackOutboxFile(f)) void processSlackRecoveryOutboxFile(f);
40434
40737
  }
40435
40738
  } catch {
@@ -40438,7 +40741,7 @@ function startSlackRecoveryOutboxWatcher() {
40438
40741
  const watcher = watch(SLACK_RECOVERY_OUTBOX_DIR, (event, filename) => {
40439
40742
  if (event !== "rename" || !filename) return;
40440
40743
  if (!isFirstAttemptSlackOutboxFile(filename)) return;
40441
- if (existsSync10(join20(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
40744
+ if (existsSync10(join21(SLACK_RECOVERY_OUTBOX_DIR, filename))) {
40442
40745
  void processSlackRecoveryOutboxFile(filename);
40443
40746
  }
40444
40747
  });
@@ -40453,7 +40756,7 @@ function startSlackRecoveryOutboxWatcher() {
40453
40756
  retryTimer.unref?.();
40454
40757
  }
40455
40758
  startSlackRecoveryOutboxWatcher();
40456
- var SLACK_NOTICE_OUTBOX_DIR = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "slack-notice-outbox") : null;
40759
+ var SLACK_NOTICE_OUTBOX_DIR = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "slack-notice-outbox") : null;
40457
40760
  var SLACK_NOTICE_MAX_AGE_MS = 9e4;
40458
40761
  var SLACK_NOTICE_INFLIGHT = /* @__PURE__ */ new Set();
40459
40762
  async function processSlackNoticeOutboxFile(filename) {
@@ -40461,11 +40764,11 @@ async function processSlackNoticeOutboxFile(filename) {
40461
40764
  if (filename.startsWith(".") || filename.endsWith(".tmp") || !filename.endsWith(".json")) return;
40462
40765
  if (SLACK_NOTICE_INFLIGHT.has(filename)) return;
40463
40766
  SLACK_NOTICE_INFLIGHT.add(filename);
40464
- const fullPath = join20(SLACK_NOTICE_OUTBOX_DIR, filename);
40767
+ const fullPath = join21(SLACK_NOTICE_OUTBOX_DIR, filename);
40465
40768
  try {
40466
40769
  let mtimeMs;
40467
40770
  try {
40468
- mtimeMs = statSync4(fullPath).mtimeMs;
40771
+ mtimeMs = statSync5(fullPath).mtimeMs;
40469
40772
  } catch {
40470
40773
  return;
40471
40774
  }
@@ -40478,7 +40781,7 @@ async function processSlackNoticeOutboxFile(filename) {
40478
40781
  }
40479
40782
  let payload;
40480
40783
  try {
40481
- const parsed = JSON.parse(readFileSync20(fullPath, "utf-8"));
40784
+ const parsed = JSON.parse(readFileSync21(fullPath, "utf-8"));
40482
40785
  if (!parsed || typeof parsed !== "object") throw new Error("not an object");
40483
40786
  payload = parsed;
40484
40787
  } catch {
@@ -40561,13 +40864,13 @@ function startSlackNoticeOutboxWatcher() {
40561
40864
  return;
40562
40865
  }
40563
40866
  try {
40564
- for (const f of readdirSync6(SLACK_NOTICE_OUTBOX_DIR)) void processSlackNoticeOutboxFile(f);
40867
+ for (const f of readdirSync7(SLACK_NOTICE_OUTBOX_DIR)) void processSlackNoticeOutboxFile(f);
40565
40868
  } catch {
40566
40869
  }
40567
40870
  try {
40568
40871
  const watcher = watch(SLACK_NOTICE_OUTBOX_DIR, (event, filename) => {
40569
40872
  if (event !== "rename" || !filename) return;
40570
- if (existsSync10(join20(SLACK_NOTICE_OUTBOX_DIR, filename))) {
40873
+ if (existsSync10(join21(SLACK_NOTICE_OUTBOX_DIR, filename))) {
40571
40874
  void processSlackNoticeOutboxFile(filename);
40572
40875
  }
40573
40876
  });
@@ -40590,7 +40893,7 @@ function sweepSlackStaleMarkers(thresholdMs) {
40590
40893
  if (!existsSync10(SLACK_PENDING_INBOUND_DIR)) return;
40591
40894
  let filenames;
40592
40895
  try {
40593
- filenames = readdirSync6(SLACK_PENDING_INBOUND_DIR);
40896
+ filenames = readdirSync7(SLACK_PENDING_INBOUND_DIR);
40594
40897
  } catch (err) {
40595
40898
  process.stderr.write(
40596
40899
  `slack-channel(${AGENT_CODE_NAME}): stale-marker readdir failed: ${err.message}
@@ -40604,10 +40907,10 @@ function sweepSlackStaleMarkers(thresholdMs) {
40604
40907
  for (const filename of filenames) {
40605
40908
  if (!filename.endsWith(".json")) continue;
40606
40909
  if (filename.endsWith(".tmp")) continue;
40607
- const fullPath = join20(SLACK_PENDING_INBOUND_DIR, filename);
40910
+ const fullPath = join21(SLACK_PENDING_INBOUND_DIR, filename);
40608
40911
  let marker;
40609
40912
  try {
40610
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
40913
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
40611
40914
  } catch (err) {
40612
40915
  process.stderr.write(
40613
40916
  `slack-channel(${AGENT_CODE_NAME}): stale-marker parse failed for ${redactSlackId(filename)}: ${err.message}
@@ -40665,13 +40968,13 @@ var slackOrphanSweepTimer = setInterval(() => {
40665
40968
  checkSlackWatchdogGiveUpNotice();
40666
40969
  }, orphanSweepIntervalMs());
40667
40970
  slackOrphanSweepTimer.unref?.();
40668
- var SLACK_PROGRESS_HEARTBEAT_PATH = SLACK_AGENT_DIR ? join20(SLACK_AGENT_DIR, "channel-progress-heartbeat.json") : null;
40971
+ var SLACK_PROGRESS_HEARTBEAT_PATH = SLACK_AGENT_DIR ? join21(SLACK_AGENT_DIR, "channel-progress-heartbeat.json") : null;
40669
40972
  var slackTrackedProgress = null;
40670
40973
  var slackProgressTickRunning = false;
40671
40974
  function readSlackProgressHeartbeat() {
40672
40975
  if (!SLACK_PROGRESS_HEARTBEAT_PATH || !existsSync10(SLACK_PROGRESS_HEARTBEAT_PATH)) return null;
40673
40976
  try {
40674
- return parseProgressHeartbeat(readFileSync20(SLACK_PROGRESS_HEARTBEAT_PATH, "utf-8"));
40977
+ return parseProgressHeartbeat(readFileSync21(SLACK_PROGRESS_HEARTBEAT_PATH, "utf-8"));
40675
40978
  } catch {
40676
40979
  return null;
40677
40980
  }
@@ -40694,11 +40997,11 @@ function findSlackProgressTarget() {
40694
40997
  let best = null;
40695
40998
  let bestMs = Infinity;
40696
40999
  try {
40697
- for (const name of readdirSync6(SLACK_PENDING_INBOUND_DIR)) {
41000
+ for (const name of readdirSync7(SLACK_PENDING_INBOUND_DIR)) {
40698
41001
  if (!name.endsWith(".json")) continue;
40699
41002
  let m;
40700
41003
  try {
40701
- m = JSON.parse(readFileSync20(join20(SLACK_PENDING_INBOUND_DIR, name), "utf-8"));
41004
+ m = JSON.parse(readFileSync21(join21(SLACK_PENDING_INBOUND_DIR, name), "utf-8"));
40702
41005
  } catch {
40703
41006
  continue;
40704
41007
  }
@@ -40845,11 +41148,11 @@ function listPendingSlackConversations() {
40845
41148
  if (!SLACK_PENDING_INBOUND_DIR || !existsSync10(SLACK_PENDING_INBOUND_DIR)) return [];
40846
41149
  const byKey = /* @__PURE__ */ new Map();
40847
41150
  try {
40848
- for (const name of readdirSync6(SLACK_PENDING_INBOUND_DIR)) {
41151
+ for (const name of readdirSync7(SLACK_PENDING_INBOUND_DIR)) {
40849
41152
  if (!name.endsWith(".json")) continue;
40850
41153
  try {
40851
41154
  const marker = JSON.parse(
40852
- readFileSync20(join20(SLACK_PENDING_INBOUND_DIR, name), "utf8")
41155
+ readFileSync21(join21(SLACK_PENDING_INBOUND_DIR, name), "utf8")
40853
41156
  );
40854
41157
  if (typeof marker.channel !== "string" || !marker.channel) continue;
40855
41158
  if (typeof marker.thread_ts !== "string" || !marker.thread_ts) continue;
@@ -40868,6 +41171,95 @@ function listPendingSlackConversations() {
40868
41171
  }
40869
41172
  return [...byKey.values()];
40870
41173
  }
41174
+ var lastSlackTurnFailedNoticeAt = /* @__PURE__ */ new Map();
41175
+ var lastSlackTurnRetryingNoticeAt = /* @__PURE__ */ new Map();
41176
+ async function postSlackTurnNotice(args) {
41177
+ if (!BOT_TOKEN || !args.channel) return { ok: false, error: "no_bot_token_or_channel" };
41178
+ try {
41179
+ const res = await fetch("https://slack.com/api/chat.postMessage", {
41180
+ method: "POST",
41181
+ headers: {
41182
+ "Content-Type": "application/json",
41183
+ Authorization: `Bearer ${BOT_TOKEN}`
41184
+ },
41185
+ body: JSON.stringify({
41186
+ channel: args.channel,
41187
+ text: args.text,
41188
+ // Anchor to the originating message, matching the give-up notice: the
41189
+ // user must see this against the message it concerns.
41190
+ ...args.threadTs ? { thread_ts: args.threadTs } : {}
41191
+ }),
41192
+ signal: AbortSignal.timeout(1e4)
41193
+ });
41194
+ const body = await res.json().catch(() => ({}));
41195
+ if (body.ok === true) return { ok: true };
41196
+ return { ok: false, error: body.error ?? `http_${res.status}` };
41197
+ } catch (err) {
41198
+ return { ok: false, error: err.name || "fetch_failed" };
41199
+ }
41200
+ }
41201
+ function armSlackTurnFailureWatch(args) {
41202
+ const channel = args.channel;
41203
+ if (!channel) return;
41204
+ const conversationKey = args.threadTs ? `${channel}:${args.threadTs}` : channel;
41205
+ void (async () => {
41206
+ try {
41207
+ const failure = await watchForTurnFailure({
41208
+ sinceMs: args.sinceMs,
41209
+ channel: "slack",
41210
+ onLongRetry: async () => {
41211
+ const now2 = Date.now();
41212
+ if (!shouldPostUndeliverableNotice(lastSlackTurnRetryingNoticeAt.get(conversationKey), now2)) {
41213
+ return;
41214
+ }
41215
+ lastSlackTurnRetryingNoticeAt.set(conversationKey, now2);
41216
+ const posted2 = await postSlackTurnNotice({
41217
+ channel,
41218
+ threadTs: args.threadTs,
41219
+ text: turnRetryingNoticeText()
41220
+ });
41221
+ if (!posted2.ok) lastSlackTurnRetryingNoticeAt.delete(conversationKey);
41222
+ process.stderr.write(
41223
+ `slack-channel(${AGENT_CODE_NAME}): [turn-failure] retrying notice ${posted2.ok ? "posted" : `FAILED (${posted2.error})`} channel=${redactSlackId(channel)}
41224
+ `
41225
+ );
41226
+ }
41227
+ });
41228
+ if (!failure) return;
41229
+ const now = Date.now();
41230
+ if (!shouldPostUndeliverableNotice(lastSlackTurnFailedNoticeAt.get(conversationKey), now)) {
41231
+ process.stderr.write(
41232
+ `slack-channel(${AGENT_CODE_NAME}): [turn-failure] suppressed (throttled) channel=${redactSlackId(channel)} class=${failure.failureClass}
41233
+ `
41234
+ );
41235
+ return;
41236
+ }
41237
+ lastSlackTurnFailedNoticeAt.set(conversationKey, now);
41238
+ const posted = await postSlackTurnNotice({
41239
+ channel,
41240
+ threadTs: args.threadTs,
41241
+ text: turnFailedNoticeText()
41242
+ });
41243
+ if (!posted.ok) {
41244
+ lastSlackTurnFailedNoticeAt.delete(conversationKey);
41245
+ process.stderr.write(
41246
+ `slack-channel(${AGENT_CODE_NAME}): [turn-failure] NOTICE POST FAILED (${posted.error}) channel=${redactSlackId(channel)} - the user is still waiting on a dead turn
41247
+ `
41248
+ );
41249
+ return;
41250
+ }
41251
+ process.stderr.write(
41252
+ `slack-channel(${AGENT_CODE_NAME}): [turn-failure] notified channel=${redactSlackId(channel)} ts=${redactSlackId(args.messageTs)} class=${failure.failureClass} status=${failure.httpStatus}
41253
+ `
41254
+ );
41255
+ } catch (err) {
41256
+ process.stderr.write(
41257
+ `slack-channel(${AGENT_CODE_NAME}): [turn-failure] watch error: ${err.message}
41258
+ `
41259
+ );
41260
+ }
41261
+ })();
41262
+ }
40871
41263
  function postSlackWatchdogGiveUpNotice(channel, threadTs, isThreadReply, reason) {
40872
41264
  if (!BOT_TOKEN || !channel) return;
40873
41265
  const now = Date.now();
@@ -40901,7 +41293,7 @@ function postSlackWatchdogGiveUpNotice(channel, threadTs, isThreadReply, reason)
40901
41293
  }
40902
41294
  function checkSlackWatchdogGiveUpNotice() {
40903
41295
  if (!SLACK_AGENT_DIR) return;
40904
- const signal = readGiveUpSignal(join20(SLACK_AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
41296
+ const signal = readGiveUpSignal(join21(SLACK_AGENT_DIR, GIVE_UP_SIGNAL_FILENAME));
40905
41297
  const signalAtMs = signal?.atMs ?? null;
40906
41298
  const act = decideGiveUpNotice({
40907
41299
  signalAtMs,
@@ -40933,7 +41325,7 @@ function readRestartTopicForMarker(filename, channel, threadTs) {
40933
41325
  if (!SLACK_RESTART_CONTEXT_DIR) return null;
40934
41326
  let raw;
40935
41327
  try {
40936
- raw = readFileSync20(join20(SLACK_RESTART_CONTEXT_DIR, filename), "utf-8");
41328
+ raw = readFileSync21(join21(SLACK_RESTART_CONTEXT_DIR, filename), "utf-8");
40937
41329
  } catch {
40938
41330
  return null;
40939
41331
  }
@@ -40942,7 +41334,7 @@ function readRestartTopicForMarker(filename, channel, threadTs) {
40942
41334
  function removeRestartContextHint(filename) {
40943
41335
  if (!SLACK_RESTART_CONTEXT_DIR) return;
40944
41336
  try {
40945
- unlinkSync7(join20(SLACK_RESTART_CONTEXT_DIR, filename));
41337
+ unlinkSync7(join21(SLACK_RESTART_CONTEXT_DIR, filename));
40946
41338
  } catch {
40947
41339
  }
40948
41340
  }
@@ -40950,14 +41342,14 @@ function clearAllRestartContextHints() {
40950
41342
  if (!SLACK_RESTART_CONTEXT_DIR) return;
40951
41343
  let names;
40952
41344
  try {
40953
- names = readdirSync6(SLACK_RESTART_CONTEXT_DIR);
41345
+ names = readdirSync7(SLACK_RESTART_CONTEXT_DIR);
40954
41346
  } catch {
40955
41347
  return;
40956
41348
  }
40957
41349
  for (const name of names) {
40958
41350
  if (!name.endsWith(".json")) continue;
40959
41351
  try {
40960
- unlinkSync7(join20(SLACK_RESTART_CONTEXT_DIR, name));
41352
+ unlinkSync7(join21(SLACK_RESTART_CONTEXT_DIR, name));
40961
41353
  } catch {
40962
41354
  }
40963
41355
  }
@@ -40974,7 +41366,7 @@ async function notifyStrandedInboundsOnFirstConnect() {
40974
41366
  if (!SLACK_PENDING_INBOUND_DIR || !existsSync10(SLACK_PENDING_INBOUND_DIR)) return;
40975
41367
  let filenames;
40976
41368
  try {
40977
- filenames = readdirSync6(SLACK_PENDING_INBOUND_DIR);
41369
+ filenames = readdirSync7(SLACK_PENDING_INBOUND_DIR);
40978
41370
  } catch {
40979
41371
  hadFailure = true;
40980
41372
  return;
@@ -40984,10 +41376,10 @@ async function notifyStrandedInboundsOnFirstConnect() {
40984
41376
  let notified = 0;
40985
41377
  for (const filename of filenames) {
40986
41378
  if (!filename.endsWith(".json")) continue;
40987
- const fullPath = join20(SLACK_PENDING_INBOUND_DIR, filename);
41379
+ const fullPath = join21(SLACK_PENDING_INBOUND_DIR, filename);
40988
41380
  let marker;
40989
41381
  try {
40990
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
41382
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
40991
41383
  } catch {
40992
41384
  continue;
40993
41385
  }
@@ -41161,7 +41553,7 @@ function noteThreadActivityByMessageTs(channel, messageTs) {
41161
41553
  markSeenAllSlackPendingMarkersForThread2(channel, messageTs);
41162
41554
  markSeenSlackPendingMarkerByMessageTs2(channel, messageTs);
41163
41555
  }
41164
- var RESTART_FLAGS_DIR = join20(homedir7(), ".augmented", "restart-flags");
41556
+ var RESTART_FLAGS_DIR = join21(homedir7(), ".augmented", "restart-flags");
41165
41557
  function actuateHostRestartSlack() {
41166
41558
  return actuateHostRestart({
41167
41559
  agtHost: AGT_HOST,
@@ -41766,7 +42158,7 @@ async function handleSlashCommandEnvelope(payload) {
41766
42158
  if (!existsSync10(RESTART_FLAGS_DIR)) {
41767
42159
  mkdirSync9(RESTART_FLAGS_DIR, { recursive: true });
41768
42160
  }
41769
- const flagPath = join20(RESTART_FLAGS_DIR, `${codeName}.flag`);
42161
+ const flagPath = join21(RESTART_FLAGS_DIR, `${codeName}.flag`);
41770
42162
  const flag = {
41771
42163
  codeName,
41772
42164
  source: "slack",
@@ -41909,7 +42301,7 @@ async function handleRestartCommand(opts) {
41909
42301
  if (!existsSync10(RESTART_FLAGS_DIR)) {
41910
42302
  mkdirSync9(RESTART_FLAGS_DIR, { recursive: true });
41911
42303
  }
41912
- const flagPath = join20(RESTART_FLAGS_DIR, `${codeName}.flag`);
42304
+ const flagPath = join21(RESTART_FLAGS_DIR, `${codeName}.flag`);
41913
42305
  const flag = {
41914
42306
  codeName,
41915
42307
  source: "slack",
@@ -42031,7 +42423,7 @@ var SLACK_HOT_THREAD_WINDOW_MS = parseHotThreadWindowMs(
42031
42423
  );
42032
42424
  function resolveThreadStorePath() {
42033
42425
  if (!AGENT_CODE_NAME) return null;
42034
- return join20(homedir7(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
42426
+ return join21(homedir7(), ".augmented", AGENT_CODE_NAME, "slack-tracked-threads.json");
42035
42427
  }
42036
42428
  function parseTtlDays(raw) {
42037
42429
  if (!raw) return void 0;
@@ -42070,9 +42462,9 @@ if (!BOT_TOKEN || !APP_TOKEN) {
42070
42462
  var slackStderrLogStream = null;
42071
42463
  if (AGENT_CODE_NAME) {
42072
42464
  try {
42073
- const logDir = join20(homedir7(), ".augmented", AGENT_CODE_NAME);
42465
+ const logDir = join21(homedir7(), ".augmented", AGENT_CODE_NAME);
42074
42466
  mkdirSync9(logDir, { recursive: true });
42075
- slackStderrLogStream = createWriteStream(join20(logDir, "slack-channel-stderr.log"), {
42467
+ slackStderrLogStream = createWriteStream(join21(logDir, "slack-channel-stderr.log"), {
42076
42468
  flags: "a",
42077
42469
  mode: 384
42078
42470
  });
@@ -42962,7 +43354,7 @@ ${result.formatted}` : "No messages in range, or the bot is not a member of this
42962
43354
  let bytes;
42963
43355
  let size;
42964
43356
  try {
42965
- const stat2 = statSync4(resolvedPath);
43357
+ const stat2 = statSync5(resolvedPath);
42966
43358
  if (!stat2.isFile()) {
42967
43359
  return {
42968
43360
  content: [{ type: "text", text: `Upload refused: ${resolvedPath} is not a regular file.` }],
@@ -42970,7 +43362,7 @@ ${result.formatted}` : "No messages in range, or the bot is not a member of this
42970
43362
  };
42971
43363
  }
42972
43364
  size = stat2.size;
42973
- bytes = readFileSync20(resolvedPath);
43365
+ bytes = readFileSync21(resolvedPath);
42974
43366
  } catch (err) {
42975
43367
  return {
42976
43368
  content: [{ type: "text", text: `Failed to read file: ${err.message}` }],
@@ -43745,7 +44137,7 @@ async function replayPendingSlackMarkers() {
43745
44137
  if (!sessionAlive) return;
43746
44138
  let filenames;
43747
44139
  try {
43748
- filenames = readdirSync6(SLACK_PENDING_INBOUND_DIR);
44140
+ filenames = readdirSync7(SLACK_PENDING_INBOUND_DIR);
43749
44141
  } catch {
43750
44142
  return;
43751
44143
  }
@@ -43753,7 +44145,7 @@ async function replayPendingSlackMarkers() {
43753
44145
  let paneFreshAgeMs = null;
43754
44146
  if (SLACK_AGENT_DIR) {
43755
44147
  try {
43756
- paneFreshAgeMs = Math.max(0, now - statSync4(join20(SLACK_AGENT_DIR, "pane.log")).mtimeMs);
44148
+ paneFreshAgeMs = Math.max(0, now - statSync5(join21(SLACK_AGENT_DIR, "pane.log")).mtimeMs);
43757
44149
  } catch {
43758
44150
  }
43759
44151
  }
@@ -43761,10 +44153,10 @@ async function replayPendingSlackMarkers() {
43761
44153
  const entries = [];
43762
44154
  for (const name of filenames) {
43763
44155
  if (!name.endsWith(".json") || name.endsWith(".tmp")) continue;
43764
- const fullPath = join20(SLACK_PENDING_INBOUND_DIR, name);
44156
+ const fullPath = join21(SLACK_PENDING_INBOUND_DIR, name);
43765
44157
  let marker;
43766
44158
  try {
43767
- marker = JSON.parse(readFileSync20(fullPath, "utf-8"));
44159
+ marker = JSON.parse(readFileSync21(fullPath, "utf-8"));
43768
44160
  } catch {
43769
44161
  continue;
43770
44162
  }
@@ -44362,7 +44754,7 @@ async function connectSocketMode() {
44362
44754
  let paneLogFreshAgeMs = null;
44363
44755
  if (SLACK_AGENT_DIR) {
44364
44756
  try {
44365
- const paneMtimeMs = statSync4(join20(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
44757
+ const paneMtimeMs = statSync5(join21(SLACK_AGENT_DIR, "pane.log")).mtimeMs;
44366
44758
  paneLogFreshAgeMs = Math.max(0, Date.now() - paneMtimeMs);
44367
44759
  } catch {
44368
44760
  }
@@ -44548,6 +44940,14 @@ ${forwarded.text}` : forwarded.text;
44548
44940
  sinceMs: dispatchedAtMs
44549
44941
  });
44550
44942
  }
44943
+ if (shouldEngage && !isFromBot && turnFailureNoticeEnabled()) {
44944
+ armSlackTurnFailureWatch({
44945
+ channel,
44946
+ threadTs,
44947
+ messageTs: ts,
44948
+ sinceMs: dispatchedAtMs
44949
+ });
44950
+ }
44551
44951
  if (shouldEngage) seedSlackProgressHeartbeat();
44552
44952
  if (channel) {
44553
44953
  conversationIngestClient?.ingest({