switchroom 0.19.38 → 0.19.40

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.
Files changed (45) hide show
  1. package/dist/agent-scheduler/index.js +10 -1
  2. package/dist/auth-broker/index.js +15 -6
  3. package/dist/cli/notion-write-pretool.mjs +10 -1
  4. package/dist/cli/switchroom.js +1141 -566
  5. package/dist/host-control/main.js +220 -8
  6. package/dist/vault/approvals/kernel-server.js +15 -6
  7. package/dist/vault/broker/server.js +15 -6
  8. package/package.json +1 -1
  9. package/profiles/_base/start.sh.hbs +10 -0
  10. package/telegram-plugin/dist/bridge/bridge.js +8 -0
  11. package/telegram-plugin/dist/gateway/gateway.js +450 -206
  12. package/telegram-plugin/dist/server.js +8 -0
  13. package/telegram-plugin/gateway/backstop-delivery.ts +48 -0
  14. package/telegram-plugin/gateway/compaction-marker.ts +84 -0
  15. package/telegram-plugin/gateway/gateway.ts +3 -3
  16. package/telegram-plugin/gateway/handback-preturn-signal.ts +16 -0
  17. package/telegram-plugin/gateway/liveness-wiring.ts +15 -0
  18. package/telegram-plugin/gateway/outbound-send-path.ts +20 -0
  19. package/telegram-plugin/gateway/outbox-sweep.ts +116 -18
  20. package/telegram-plugin/gateway/silence-poke-session-event.ts +13 -0
  21. package/telegram-plugin/gateway/stream-render.ts +277 -18
  22. package/telegram-plugin/hooks/compaction-marker-precompact.mjs +70 -0
  23. package/telegram-plugin/hooks/hooks.json +11 -0
  24. package/telegram-plugin/hooks/tool-label-pretool.mjs +119 -1
  25. package/telegram-plugin/session-tail.ts +20 -0
  26. package/telegram-plugin/silence-poke.ts +28 -0
  27. package/telegram-plugin/tests/activity-ever-opened-sticky.test.ts +19 -12
  28. package/telegram-plugin/tests/fixtures/pretool-main-2.1.219.json +15 -0
  29. package/telegram-plugin/tests/fixtures/pretool-subagent-2.1.219.json +16 -0
  30. package/telegram-plugin/tests/outbox-delivery.test.ts +38 -1
  31. package/telegram-plugin/tests/outbox-flush-ack-claim-race.test.ts +213 -0
  32. package/telegram-plugin/tests/outbox-reply-then-recap-e2e.test.ts +1 -1
  33. package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +4 -4
  34. package/telegram-plugin/tests/outbox-sweep-listen-button.test.ts +71 -8
  35. package/telegram-plugin/tests/queued-card-surface.test.ts +262 -0
  36. package/telegram-plugin/tests/send-reply-golden.test.ts +47 -0
  37. package/telegram-plugin/tests/sidechain-label-filter-pretool.test.ts +272 -0
  38. package/telegram-plugin/tests/silence-poke-compaction.test.ts +222 -0
  39. package/vendor/hindsight-memory/scripts/lib/client.py +7 -4
  40. package/vendor/hindsight-memory/scripts/lib/config.py +151 -0
  41. package/vendor/hindsight-memory/scripts/retain.py +19 -15
  42. package/vendor/hindsight-memory/scripts/tests/test_backfill.py +14 -1
  43. package/vendor/hindsight-memory/scripts/tests/test_observation_scopes.py +192 -3
  44. package/vendor/hindsight-memory/scripts/tests/test_reconcile_durability.py +18 -2
  45. package/vendor/hindsight-memory/scripts/tests/test_subagent_retain.py +57 -4
@@ -21726,7 +21726,7 @@ var require_dist = __commonJS((exports) => {
21726
21726
  });
21727
21727
 
21728
21728
  // ../src/memory/observation-scopes.ts
21729
- var OBSERVATION_SCOPES;
21729
+ var OBSERVATION_SCOPES, OBSERVATION_SCOPE_STRATEGIES;
21730
21730
  var init_observation_scopes = __esm(() => {
21731
21731
  OBSERVATION_SCOPES = [
21732
21732
  "per_tag",
@@ -21734,10 +21734,16 @@ var init_observation_scopes = __esm(() => {
21734
21734
  "all_combinations",
21735
21735
  "shared"
21736
21736
  ];
21737
+ OBSERVATION_SCOPE_STRATEGIES = [
21738
+ "curated",
21739
+ "shared",
21740
+ "combined",
21741
+ "off"
21742
+ ];
21737
21743
  });
21738
21744
 
21739
21745
  // ../src/config/schema.ts
21740
- var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
21746
+ var CodeRepoEntrySchema, AgentBindMountSchema, HttpDiffPollSchema, PollSpecSchema, TelegramMessageActionSchema, WebhookActionSchema, ActionSpecSchema, ScheduleEntrySchema, AgentSoulSchema, AgentToolsSchema, ObservationScopesSchema, ObservationScopeStrategySchema, AgentMemorySchema, HookEntrySchema, AgentHooksSchema, SubagentSchema, SessionSchema, SessionContinuitySchema, webhookDispatchRule, TelegramChannelSchema, ChannelsSchema, TIMEZONE_REGEX, ApproverIdSchema, GoogleWorkspaceTierSchema, GoogleWorkspaceConfigSchema, LiteLLMConfigSchema, HindsightPerOpLlmSchema, HindsightConfigSchema, MicrosoftWorkspaceConfigSchema, NotionWorkspaceConfigSchema, AgentGoogleWorkspaceConfigSchema, MicrosoftAccountEmailSchema, MicrosoftToolTokenSchema, MicrosoftAccountBindingSchema, AgentMicrosoftWorkspaceConfigSchema, AgentNotionWorkspaceConfigSchema, ReactionsSchema, ReactionDispatchSchema, releaseBlockFields, ReleaseBlock, RootReleaseBlock, NetworkIsolationSchema, servesField, knowsField, profileFields, ProfileSchema, _omitExtends, defaultsFields, AgentDefaultsSchema, AgentSchema, TelegramConfigSchema, MemoryBackendConfigSchema, VaultConfigSchema, QuotaConfigSchema, AutoReleaseCheckSchema, HostControlConfigSchema, WebServiceConfigSchema, FleetHealthConfigSchema, HostdConfigSchema, CronEgressSchema, CronConfigSchema, UserSchema, SwitchroomConfigSchema;
21741
21747
  var init_schema = __esm(() => {
21742
21748
  init_zod();
21743
21749
  init_observation_scopes();
@@ -21852,7 +21858,8 @@ var init_schema = __esm(() => {
21852
21858
  allow: exports_external.array(exports_external.string()).default([]).describe("Allowed tools (use ['all'] for unrestricted)"),
21853
21859
  deny: exports_external.array(exports_external.string()).default([]).describe("Denied tools (overrides allow)")
21854
21860
  }).optional();
21855
- ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Per-row observation scope stamped on every memory this agent " + `retains. "shared" makes Hindsight's consolidation write the ` + "resulting observations into ONE global untagged scope instead of a " + "scope per tag \u2014 what several agents pooling one bank need so their " + "observations actually merge rather than sitting in parallel " + "per-tag silos. OMITTED BY DEFAULT: unset means the field never goes " + "on the wire and the engine's own default stands, which is the " + "shipped behaviour. Applies to every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
21861
+ ObservationScopesSchema = exports_external.enum(OBSERVATION_SCOPES).optional().describe("Operator PIN for the per-row observation scope, overriding the " + 'default `curated` strategy (see docs/configuration.md). "shared" ' + "forces Hindsight's consolidation to write EVERY observation into ONE " + "global untagged scope \u2014 what several agents pooling one bank want so " + "their observations merge rather than sitting in parallel silos. " + '"combined" is the pin form of opting out (restores the pre-#4035 ' + "engine default). UNSET does NOT mean 'nothing on the wire': the " + "`curated` strategy still computes a scope per retain. Applies to " + "every retain path (Stop hook, " + "sidechain, boot reconcile, queue drain, backfill, session-handoff " + "mirror) and is carried on the queued payload, so a retain that fails " + "now and drains later still lands in this scope. Accepted values: " + `${OBSERVATION_SCOPES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
21862
+ ObservationScopeStrategySchema = exports_external.enum(OBSERVATION_SCOPE_STRATEGIES).optional().describe("Per-retain strategy that decides how the observation scope is " + 'computed when no `observation_scopes` pin is set. "curated" ' + "(the default since #4035) strips volatile per-session provenance " + "tags from each retain's consolidation scope \u2014 keeping the stable " + "semantic ones on the source fact \u2014 so an agent's observations dedup " + "and merge across sessions instead of pocketing one-per-session. " + '"shared" sends every retain to the one global untagged scope. ' + '"combined" / "off" opt OUT: no per-row scope goes on the wire, ' + "restoring the pre-feature engine default (byte-identical to an " + "unconfigured client). OMITTED BY DEFAULT: unset leaves the plugin's " + "own default (`curated`) in force. A manual `observation_scopes` pin " + "wins over this strategy. Accepted values: " + `${OBSERVATION_SCOPE_STRATEGIES.join(", ")}. ` + "Cascade: override (per-agent wins over default).");
21856
21863
  AgentMemorySchema = exports_external.object({
21857
21864
  collection: exports_external.string().describe("Hindsight collection name for this agent"),
21858
21865
  auto_recall: exports_external.boolean().default(true).describe("Auto-search memories before each response"),
@@ -21888,6 +21895,7 @@ var init_schema = __esm(() => {
21888
21895
  }).optional().describe("Personality traits (1-5 each) steering how this bank frames recall, " + "reflect, and observation synthesis \u2014 a coach leans empathy-high, a " + "lawyer/analyst leans skepticism/literalism-high. Maps to the engine's " + "flat `disposition_skepticism`/`_literalism`/`_empathy` fields. " + "Cascade: per-key merge (an agent overrides individual traits and " + "inherits the rest, matching `recall`)."),
21889
21896
  directive_capture_nudge: exports_external.boolean().optional().describe("Deterministic directive-capture nudge (issue #2848 Stage B). When " + "on (switchroom default true \u2014 Stage A measured a ~55% miss rate on " + "durable corrections), the auto-recall hook regex-detects correction " + '/ standing-rule-shaped inbound ("always/never \u2026", "from now on \u2026", ' + `"stop doing \u2026", a stated preference, "that's wrong, it's \u2026") and ` + "appends a terse advisory to the turn's context telling the model to " + "persist the rule with mcp__hindsight__create_directive if it IS " + "durable. Detection is pure regex \u2014 the model does the judgment " + "in-session and calls create_directive itself (no model callsite, no " + "silent hook-side write). Set false to disable per-agent. " + "Cascade: override (per-agent wins over default)."),
21890
21897
  observation_scopes: ObservationScopesSchema,
21898
+ observation_scope_strategy: ObservationScopeStrategySchema,
21891
21899
  recall: exports_external.object({
21892
21900
  max_memories: exports_external.number().int().min(0).optional().describe("Cap on the number of memories injected into the prompt by " + "auto-recall, regardless of token budget. Plugin default is 12. " + "0 disables the cap (all memories Hindsight returns are injected)."),
21893
21901
  cache_ttl_secs: exports_external.number().int().min(0).optional().describe("Per-session recall cache TTL in seconds. When > 0, identical " + "(prompt, bank) within the same session reuse the cached recall " + "result instead of round-tripping to Hindsight. 0 disables. " + "Default is 600 (10 min) for switchroom-managed agents."),
@@ -22288,6 +22296,7 @@ var init_schema = __esm(() => {
22288
22296
  isolation: exports_external.enum(["default", "strict"]).optional(),
22289
22297
  directive_capture_nudge: exports_external.boolean().optional(),
22290
22298
  observation_scopes: ObservationScopesSchema,
22299
+ observation_scope_strategy: ObservationScopeStrategySchema,
22291
22300
  recall: exports_external.object({
22292
22301
  max_memories: exports_external.number().int().min(0).optional(),
22293
22302
  cache_ttl_secs: exports_external.number().int().min(0).optional(),
@@ -35875,9 +35884,9 @@ function renderAuthLine(state7, agentName3, now = Date.now()) {
35875
35884
 
35876
35885
  // gateway/quota-cache.ts
35877
35886
  import { existsSync as existsSync44, readFileSync as readFileSync44, writeFileSync as writeFileSync37, mkdirSync as mkdirSync39 } from "fs";
35878
- import { join as join53, dirname as dirname22 } from "path";
35887
+ import { join as join54, dirname as dirname22 } from "path";
35879
35888
  function defaultCachePath() {
35880
- return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join53(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35889
+ return process.env.SWITCHROOM_QUOTA_CACHE_PATH ?? join54(process.env.HOME ?? "/tmp", ".switchroom", "quota-cache.json");
35881
35890
  }
35882
35891
  function readQuotaCache(opts = {}) {
35883
35892
  const path3 = opts.path ?? defaultCachePath();
@@ -35924,7 +35933,7 @@ var init_quota_cache = __esm(() => {
35924
35933
 
35925
35934
  // gateway/boot-probes.ts
35926
35935
  import { readFileSync as readFileSync45, readdirSync as readdirSync12, existsSync as existsSync45 } from "fs";
35927
- import { join as join54 } from "path";
35936
+ import { join as join55 } from "path";
35928
35937
  import { execFile as execFileCb } from "child_process";
35929
35938
  import { promisify } from "util";
35930
35939
  async function withTimeout(label, p, timeoutMs = PROBE_TIMEOUT_MS) {
@@ -35966,8 +35975,8 @@ function mapPlan(billingType, hasExtra) {
35966
35975
  }
35967
35976
  async function probeAccount(agentDir) {
35968
35977
  return withTimeout("Account", (async () => {
35969
- const claudeDir = join54(agentDir, ".claude");
35970
- const claudeJsonPath = join54(claudeDir, ".claude.json");
35978
+ const claudeDir = join55(agentDir, ".claude");
35979
+ const claudeJsonPath = join55(claudeDir, ".claude.json");
35971
35980
  let cfg = {};
35972
35981
  try {
35973
35982
  const raw = readFileSync45(claudeJsonPath, "utf8");
@@ -35988,8 +35997,8 @@ async function probeAccount(agentDir) {
35988
35997
  let tokenStr = "";
35989
35998
  let status = "ok";
35990
35999
  for (const candidate of [
35991
- join54(claudeDir, ".oauth-token.meta.json"),
35992
- join54(claudeDir, "accounts", "default", ".oauth-token.meta.json")
36000
+ join55(claudeDir, ".oauth-token.meta.json"),
36001
+ join55(claudeDir, "accounts", "default", ".oauth-token.meta.json")
35993
36002
  ]) {
35994
36003
  if (existsSync45(candidate)) {
35995
36004
  try {
@@ -36375,9 +36384,9 @@ async function probeQuota(claudeConfigDir, _agentDir, fetchImpl = fetch, opts =
36375
36384
  let claudeDirForProbe = null;
36376
36385
  for (const candidate of [
36377
36386
  claudeConfigDir,
36378
- join54(claudeConfigDir, "accounts", "default")
36387
+ join55(claudeConfigDir, "accounts", "default")
36379
36388
  ]) {
36380
- if (existsSync45(join54(candidate, ".oauth-token"))) {
36389
+ if (existsSync45(join55(candidate, ".oauth-token"))) {
36381
36390
  claudeDirForProbe = candidate;
36382
36391
  break;
36383
36392
  }
@@ -36612,7 +36621,7 @@ async function probeSkills(agentDir, opts = {}) {
36612
36621
  return withTimeout("Skills", (async () => {
36613
36622
  const fs3 = opts.fs ?? realSkillsFs;
36614
36623
  const max = opts.maxNamesShown ?? 3;
36615
- const skillsDir = join54(agentDir, ".claude", "skills");
36624
+ const skillsDir = join55(agentDir, ".claude", "skills");
36616
36625
  if (!fs3.exists(skillsDir)) {
36617
36626
  return { status: "ok", label: "Skills", detail: "no skills dir" };
36618
36627
  }
@@ -36627,17 +36636,17 @@ async function probeSkills(agentDir, opts = {}) {
36627
36636
  }
36628
36637
  const dangling = [];
36629
36638
  for (const name of entries) {
36630
- const skillPath = join54(skillsDir, name);
36639
+ const skillPath = join55(skillsDir, name);
36631
36640
  if (!fs3.exists(skillPath)) {
36632
36641
  dangling.push(name);
36633
36642
  continue;
36634
36643
  }
36635
- const skillMd = join54(skillPath, "SKILL.md");
36644
+ const skillMd = join55(skillPath, "SKILL.md");
36636
36645
  if (!fs3.exists(skillMd) && !fs3.exists(skillPath + ".md")) {
36637
36646
  continue;
36638
36647
  }
36639
36648
  }
36640
- const overlayDir = opts.overlaySkillsDir ?? join54(agentDir, "skills.d");
36649
+ const overlayDir = opts.overlaySkillsDir ?? join55(agentDir, "skills.d");
36641
36650
  const overlaySlugs = new Set;
36642
36651
  if (fs3.exists(overlayDir)) {
36643
36652
  let overlayEntries = [];
@@ -36679,7 +36688,7 @@ function renderBucketedSkills(switchroom, agent) {
36679
36688
  }
36680
36689
  async function probeConnections(agentDir, opts = {}) {
36681
36690
  return withTimeout("Connections", (async () => {
36682
- const path3 = join54(agentDir, ".claude", "connection-health.json");
36691
+ const path3 = join55(agentDir, ".claude", "connection-health.json");
36683
36692
  const read = opts.readFileImpl ?? ((p) => readFileSync45(p, "utf8"));
36684
36693
  let issues = [];
36685
36694
  try {
@@ -36720,7 +36729,7 @@ async function probeDrift(agentDir, opts = {}) {
36720
36729
  }
36721
36730
  } catch {}
36722
36731
  try {
36723
- const raw = readFileSync45(join54(agentDir, ".switchroom-drift.json"), "utf8");
36732
+ const raw = readFileSync45(join55(agentDir, ".switchroom-drift.json"), "utf8");
36724
36733
  const report = JSON.parse(raw);
36725
36734
  if (report?.version === 1 && Array.isArray(report.findings)) {
36726
36735
  const gen = report.generatedAt ? Date.parse(report.generatedAt) : NaN;
@@ -36772,8 +36781,8 @@ var init_boot_probes = __esm(() => {
36772
36781
  realSchedulerFs = {
36773
36782
  readFile: (p) => readFileSync45(p, "utf-8"),
36774
36783
  mtimeMs: (p) => {
36775
- const { statSync: statSync16 } = __require("fs");
36776
- return statSync16(p).mtimeMs;
36784
+ const { statSync: statSync17 } = __require("fs");
36785
+ return statSync17(p).mtimeMs;
36777
36786
  },
36778
36787
  exists: (p) => existsSync45(p)
36779
36788
  };
@@ -37080,8 +37089,8 @@ __export(exports_boot_card, {
37080
37089
  readRoutingMode: () => readRoutingMode,
37081
37090
  containerBootStartMs: () => containerBootStartMs
37082
37091
  });
37083
- import { join as join55 } from "path";
37084
- import { readFileSync as readFileSync49, statSync as statSync16 } from "fs";
37092
+ import { join as join56 } from "path";
37093
+ import { readFileSync as readFileSync49, statSync as statSync17 } from "fs";
37085
37094
  function resolvePersonaName(slug, loadConfig3) {
37086
37095
  try {
37087
37096
  const config = loadConfig3 ? loadConfig3() : loadConfig();
@@ -37124,8 +37133,8 @@ function containerBootStartMs(fsImpl = { readFileSync: readFileSync49 }) {
37124
37133
  return null;
37125
37134
  }
37126
37135
  }
37127
- function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync49, statSync: statSync16 }) {
37128
- const path3 = join55(agentDir, ".routing-mode");
37136
+ function readRoutingMode(agentDir, bootStartMs, fsImpl = { readFileSync: readFileSync49, statSync: statSync17 }) {
37137
+ const path3 = join56(agentDir, ".routing-mode");
37129
37138
  try {
37130
37139
  const raw = fsImpl.readFileSync(path3, "utf-8");
37131
37140
  const line = raw.split(`
@@ -37232,7 +37241,7 @@ function renderBootCard(opts) {
37232
37241
  return stackCardLines(flatLines);
37233
37242
  }
37234
37243
  async function runAllProbes(opts) {
37235
- const claudeDir = join55(opts.agentDir, ".claude");
37244
+ const claudeDir = join56(opts.agentDir, ".claude");
37236
37245
  const probes = {};
37237
37246
  const slug = opts.agentSlug ?? opts.agentName;
37238
37247
  await Promise.allSettled([
@@ -38094,7 +38103,7 @@ __export(exports_tmux2, {
38094
38103
  captureAgentPane: () => captureAgentPane2
38095
38104
  });
38096
38105
  import { execFileSync as execFileSync8 } from "node:child_process";
38097
- import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync21, unlinkSync as unlinkSync29, writeFileSync as writeFileSync49 } from "node:fs";
38106
+ import { chmodSync as chmodSync13, mkdirSync as mkdirSync50, readdirSync as readdirSync15, statSync as statSync22, unlinkSync as unlinkSync30, writeFileSync as writeFileSync49 } from "node:fs";
38098
38107
  import { resolve as resolve12 } from "node:path";
38099
38108
  function captureAgentPane2(opts) {
38100
38109
  const { agentName: agentName3, agentDir, reason } = opts;
@@ -38223,13 +38232,13 @@ function pruneOldReports2(dir, retain) {
38223
38232
  const full = resolve12(dir, n);
38224
38233
  let mtimeMs = 0;
38225
38234
  try {
38226
- mtimeMs = statSync21(full).mtimeMs;
38235
+ mtimeMs = statSync22(full).mtimeMs;
38227
38236
  } catch {}
38228
38237
  return { full, mtimeMs };
38229
38238
  }).sort((a, b) => b.mtimeMs - a.mtimeMs);
38230
38239
  for (const stale of files.slice(retain)) {
38231
38240
  try {
38232
- unlinkSync29(stale.full);
38241
+ unlinkSync30(stale.full);
38233
38242
  } catch {}
38234
38243
  }
38235
38244
  }
@@ -39203,14 +39212,14 @@ import {
39203
39212
  mkdirSync as mkdirSync51,
39204
39213
  readdirSync as readdirSync16,
39205
39214
  rmSync as rmSync8,
39206
- statSync as statSync22,
39215
+ statSync as statSync23,
39207
39216
  renameSync as renameSync27,
39208
39217
  realpathSync as realpathSync4,
39209
39218
  chmodSync as chmodSync14,
39210
39219
  openSync as openSync15,
39211
39220
  closeSync as closeSync15,
39212
39221
  existsSync as existsSync57,
39213
- unlinkSync as unlinkSync30,
39222
+ unlinkSync as unlinkSync31,
39214
39223
  appendFileSync as appendFileSync9
39215
39224
  } from "fs";
39216
39225
 
@@ -39264,8 +39273,8 @@ function fsyncPathSync(path) {
39264
39273
  }
39265
39274
 
39266
39275
  // gateway/gateway.ts
39267
- import { homedir as homedir19 } from "os";
39268
- import { join as join68, sep as sep4, basename as basename17 } from "path";
39276
+ import { homedir as homedir20 } from "os";
39277
+ import { join as join69, sep as sep4, basename as basename17 } from "path";
39269
39278
 
39270
39279
  // plugin-logger.ts
39271
39280
  import { appendFileSync, mkdirSync, renameSync as renameSync2, statSync, existsSync } from "fs";
@@ -47609,6 +47618,23 @@ function computeLabel(toolName, input) {
47609
47618
  }
47610
47619
  return "Working\u2026";
47611
47620
  }
47621
+ function isSubagentToolCall(event) {
47622
+ if (!event || typeof event !== "object")
47623
+ return false;
47624
+ const at = event.agent_type;
47625
+ if (typeof at === "string" && at.length > 0)
47626
+ return at !== "main";
47627
+ const aid = event.agent_id;
47628
+ const sid = event.session_id;
47629
+ return typeof aid === "string" && aid.length > 0 && typeof sid === "string" && sid.length > 0 && aid !== sid;
47630
+ }
47631
+ function hasAttributionSignal(event) {
47632
+ if (!event || typeof event !== "object")
47633
+ return false;
47634
+ const at = event.agent_type;
47635
+ const aid = event.agent_id;
47636
+ return typeof at === "string" && at.length > 0 || typeof aid === "string" && aid.length > 0;
47637
+ }
47612
47638
  function main() {
47613
47639
  const raw = readStdin().trim();
47614
47640
  if (!raw)
@@ -47627,6 +47653,14 @@ function main() {
47627
47653
  const toolName = event.tool_name;
47628
47654
  if (!sessionId || !toolUseId || !toolName)
47629
47655
  process.exit(0);
47656
+ if (!hasAttributionSignal(event)) {
47657
+ try {
47658
+ process.stderr.write(`[tool-label-pretool] WARNING: PreToolUse payload carries neither ` + `agent_type nor agent_id \u2014 the sidechain-attribution invariant may be ` + `broken by a Claude Code schema change; sub-agent labels may be ` + `mis-attributed to the parent session (tool_use_id=${toolUseId}).
47659
+ `);
47660
+ } catch {}
47661
+ }
47662
+ if (isSubagentToolCall(event))
47663
+ process.exit(0);
47630
47664
  let label;
47631
47665
  try {
47632
47666
  label = computeLabel(toolName, event.tool_input);
@@ -53507,6 +53541,13 @@ function createHandbackPreturnSignal(deps) {
53507
53541
  dropEntry(entry);
53508
53542
  return adoption;
53509
53543
  },
53544
+ hasPendingForTurnId(turnId) {
53545
+ for (const e of byKey.values()) {
53546
+ if (e.adoptTurnId === turnId && !e.consumed)
53547
+ return true;
53548
+ }
53549
+ return false;
53550
+ },
53510
53551
  isPreTurnRecord(turnKey2) {
53511
53552
  return turnKey2.startsWith(PRETURN_TURNKEY_PREFIX);
53512
53553
  },
@@ -70340,6 +70381,8 @@ function tick(now) {
70340
70381
  const ceiling = thresholds.fallbackHardCeiling ?? Number.POSITIVE_INFINITY;
70341
70382
  const underCeiling = silence < ceiling;
70342
70383
  if (underCeiling) {
70384
+ if (activeDeps.isCompactionInFlight?.(key) === true)
70385
+ continue;
70343
70386
  const forceDisable = process.env.SWITCHROOM_SILENCE_DEFER_INFLIGHT_TOOLS === "0";
70344
70387
  if (!forceDisable && activeDeps.isLegitimatelyWorking != null) {
70345
70388
  if (activeDeps.isLegitimatelyWorking(key))
@@ -71564,6 +71607,14 @@ function projectTranscriptLine(line) {
71564
71607
  { kind: "turn_end", durationMs: obj.durationMs ?? 0 }
71565
71608
  ];
71566
71609
  }
71610
+ if (type === "system" && obj.subtype === "compact_boundary") {
71611
+ const meta = obj.compactMetadata;
71612
+ return [{
71613
+ kind: "compact_boundary",
71614
+ trigger: typeof meta?.trigger === "string" ? meta.trigger : null,
71615
+ compactDurationMs: typeof meta?.durationMs === "number" ? meta.durationMs : null
71616
+ }];
71617
+ }
71567
71618
  return [];
71568
71619
  }
71569
71620
 
@@ -77095,6 +77146,10 @@ function compactIfLarge(path2) {
77095
77146
  } catch {}
77096
77147
  }
77097
77148
 
77149
+ // gateway/outbox-sweep.ts
77150
+ init_rich_send();
77151
+ init_format();
77152
+
77098
77153
  // registry/subagents-schema.ts
77099
77154
  function countRunningBackgroundSubagents(db2) {
77100
77155
  const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
@@ -78896,13 +78951,28 @@ async function deliverCapturedProse(deps, args) {
78896
78951
  const plainChunks = splitMarkdownChunks(plain, RICH_MESSAGE_MAX_CHARS);
78897
78952
  try {
78898
78953
  let liveThreadId = threadId;
78954
+ const plainSentIds = [];
78955
+ const plainSentTexts = [];
78899
78956
  for (const c of plainChunks) {
78900
78957
  const sent = await retryWithThreadFallback(robustApiCall, (tid) => bot.api.sendMessage(chatId, c, tid != null ? { message_thread_id: tid } : {}), { threadId: liveThreadId, chat_id: chatId, verb: "captured-prose-plain-fallback.sendMessage" });
78958
+ const sentId = sent.message_id;
78959
+ if (sentId != null) {
78960
+ plainSentIds.push(sentId);
78961
+ plainSentTexts.push(c);
78962
+ }
78901
78963
  if (liveThreadId != null && sent.message_thread_id == null) {
78902
78964
  liveThreadId = undefined;
78903
78965
  }
78904
78966
  }
78905
78967
  outboundDedup.record(chatId, threadId, text4, Date.now(), registryKey);
78968
+ if (HISTORY_ENABLED && plainSentIds.length > 0) {
78969
+ try {
78970
+ recordOutbound2({ chat_id: chatId, thread_id: threadId ?? null, message_ids: plainSentIds, texts: plainSentTexts });
78971
+ } catch (histErr) {
78972
+ process.stderr.write(`telegram gateway: history recordOutbound (captured-prose plain fallback) failed: ${histErr instanceof Error ? histErr.message : String(histErr)}
78973
+ `);
78974
+ }
78975
+ }
78906
78976
  process.stderr.write(`telegram gateway: captured-prose recovered via plain-text fallback ` + `(chat=${chatId} origin=${originTurnId})
78907
78977
  `);
78908
78978
  } catch (plainErr) {
@@ -80104,23 +80174,35 @@ function decideFallbackTeardownNotice2(input) {
80104
80174
  // gateway/stream-render.ts
80105
80175
  var PARKED_TURN_START_MAX = 16;
80106
80176
  var PARKED_TURN_START_TTL_MS = 30 * 60000;
80177
+ var QUEUED_CARD_ENABLED = process.env.SWITCHROOM_QUEUED_CARD !== "0";
80178
+ var QUEUED_CARD_HTML = "\u23f3 Queued \u2014 waiting for the current task to finish\u2026";
80179
+ var QUEUED_CARD_FOLDED_HTML = "\u2705 Folded into the current task.";
80180
+ var QUEUED_CARD_EXPIRED_HTML = "\u26a0\ufe0f This queued message timed out before it could start.";
80107
80181
  var parkedTurnStarts = [];
80108
- function pruneParkedTurnStarts(now) {
80182
+ function pruneParkedTurnStarts(now, onDrop) {
80109
80183
  for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
80110
80184
  if (now - parkedTurnStarts[i].parkedAt > PARKED_TURN_START_TTL_MS) {
80111
80185
  const [dropped] = parkedTurnStarts.splice(i, 1);
80112
80186
  process.stderr.write(`telegram gateway: parked-turn-start expired chat=${dropped.chatId ?? "-"} ` + `msg=${dropped.messageId ?? "-"} age_ms=${now - dropped.parkedAt}
80113
80187
  `);
80188
+ try {
80189
+ onDrop?.(dropped);
80190
+ } catch {}
80114
80191
  }
80115
80192
  }
80116
80193
  }
80117
- function parkTurnStart(env, now) {
80118
- parkedTurnStarts.push({ ...env, parkedAt: now });
80194
+ function parkTurnStart(env, now, onEvict) {
80195
+ const entry = { ...env, parkedAt: now };
80196
+ parkedTurnStarts.push(entry);
80119
80197
  while (parkedTurnStarts.length > PARKED_TURN_START_MAX) {
80120
80198
  const [dropped] = parkedTurnStarts.splice(0, 1);
80121
80199
  process.stderr.write(`telegram gateway: parked-turn-start evicted (cap ${PARKED_TURN_START_MAX}) ` + `chat=${dropped.chatId ?? "-"} msg=${dropped.messageId ?? "-"}
80122
80200
  `);
80201
+ try {
80202
+ onEvict?.(dropped);
80203
+ } catch {}
80123
80204
  }
80205
+ return entry;
80124
80206
  }
80125
80207
  function takeParkedTurnStart() {
80126
80208
  return parkedTurnStarts.pop() ?? null;
@@ -80128,11 +80210,56 @@ function takeParkedTurnStart() {
80128
80210
  function discardParkedTurnStart(rawContent) {
80129
80211
  for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
80130
80212
  if (parkedTurnStarts[i].rawContent === rawContent) {
80131
- parkedTurnStarts.splice(i, 1);
80132
- return true;
80213
+ const [dropped] = parkedTurnStarts.splice(i, 1);
80214
+ return dropped;
80133
80215
  }
80134
80216
  }
80135
- return false;
80217
+ return null;
80218
+ }
80219
+ async function openQueuedCard(deps, chatId, threadId, replyToMessageId) {
80220
+ try {
80221
+ const sent = await deps.robustApiCall(() => deps.bot.api.sendRichMessage(chatId, richMessage(QUEUED_CARD_HTML), {
80222
+ ...threadId != null ? { message_thread_id: threadId } : {},
80223
+ ...replyToMessageId != null ? { reply_parameters: { message_id: replyToMessageId, allow_sending_without_reply: true } } : {},
80224
+ disable_notification: true
80225
+ }), {
80226
+ chat_id: chatId,
80227
+ ...threadId != null ? { threadId } : {},
80228
+ verb: "queued-card.send"
80229
+ });
80230
+ return sent?.message_id ?? null;
80231
+ } catch (err) {
80232
+ process.stderr.write(`telegram gateway: queued card send failed: ${err.message}
80233
+ `);
80234
+ return null;
80235
+ }
80236
+ }
80237
+ function finalizeQueuedCard(deps, chatId, threadId, messageId, html) {
80238
+ deps.robustApiCall(() => deps.bot.api.editMessageText(chatId, messageId, richMessage(html), {}), {
80239
+ chat_id: chatId,
80240
+ ...threadId != null ? { threadId } : {},
80241
+ verb: "queued-card.finalize"
80242
+ }).catch(() => {
80243
+ return;
80244
+ });
80245
+ }
80246
+ function deleteQueuedCard(deps, chatId, messageId) {
80247
+ deps.robustApiCall(() => deps.bot.api.deleteMessage(chatId, messageId), { chat_id: chatId, verb: "queued-card.delete-orphan" }).catch(() => {
80248
+ return;
80249
+ });
80250
+ }
80251
+ function envThreadIdNum(threadId) {
80252
+ if (threadId == null)
80253
+ return null;
80254
+ const n = Number(threadId);
80255
+ return Number.isFinite(n) ? n : null;
80256
+ }
80257
+ function finalizeParkedEntryCard(deps, entry, html) {
80258
+ if (!QUEUED_CARD_ENABLED)
80259
+ return;
80260
+ if (entry.queuedCardMessageId == null || entry.chatId == null)
80261
+ return;
80262
+ finalizeQueuedCard(deps, entry.chatId, envThreadIdNum(entry.threadId), entry.queuedCardMessageId, html);
80136
80263
  }
80137
80264
  function beginTurn(deps, ev) {
80138
80265
  const {
@@ -80246,6 +80373,16 @@ function beginTurn(deps, ev) {
80246
80373
  `);
80247
80374
  }
80248
80375
  }
80376
+ if (QUEUED_CARD_ENABLED && ev.queuedCardMessageId != null && next.activityMessageId == null) {
80377
+ next.activityMessageId = ev.queuedCardMessageId;
80378
+ next.activityEverOpened = true;
80379
+ process.stderr.write(`telegram gateway: queued card adopted turnId=${turnId} card=${ev.queuedCardMessageId}
80380
+ `);
80381
+ } else if (QUEUED_CARD_ENABLED && ev.queuedCardMessageId != null && ev.chatId != null && next.activityMessageId != null && next.activityMessageId !== ev.queuedCardMessageId) {
80382
+ deleteQueuedCard(deps, ev.chatId, ev.queuedCardMessageId);
80383
+ process.stderr.write(`telegram gateway: queued card superseded by handback adoption turnId=${turnId} ` + `queued=${ev.queuedCardMessageId} adopted=${next.activityMessageId}
80384
+ `);
80385
+ }
80249
80386
  startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
80250
80387
  setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
80251
80388
  scheduleEarlyLivenessOpen(next);
@@ -80394,7 +80531,7 @@ function handleSessionEvent(deps, ev) {
80394
80531
  ackDelivery(deliveryQueue, chatKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId, ev.rawContent);
80395
80532
  }
80396
80533
  }
80397
- pruneParkedTurnStarts(now);
80534
+ pruneParkedTurnStarts(now, (e) => finalizeParkedEntryCard(deps, e, QUEUED_CARD_EXPIRED_HTML));
80398
80535
  if (!ev.chatId) {
80399
80536
  beginTurn(deps, ev);
80400
80537
  return;
@@ -80405,18 +80542,38 @@ function handleSessionEvent(deps, ev) {
80405
80542
  beginTurn(deps, ev);
80406
80543
  return;
80407
80544
  }
80408
- parkTurnStart({
80545
+ const parkedEntry = parkTurnStart({
80409
80546
  chatId: ev.chatId,
80410
80547
  messageId: ev.messageId,
80411
80548
  threadId: ev.threadId,
80412
80549
  rawContent: ev.rawContent
80413
- }, now);
80550
+ }, now, (evicted) => finalizeParkedEntryCard(deps, evicted, QUEUED_CARD_EXPIRED_HTML));
80414
80551
  process.stderr.write(`telegram gateway: turn-start parked (session busy) chat=${ev.chatId} ` + `thread=${enqThreadIdNum ?? "-"} msg=${ev.messageId ?? "-"} ` + `parked=${parkedTurnStarts.length}
80415
80552
  `);
80553
+ const parkTurnId = deriveTurnId(ev.chatId, enqThreadIdNum ?? null, ev.messageId);
80554
+ const handbackOwnsSurface = HANDBACK_PRETURN_ENABLED && parkTurnId != null && handbackPreturnSignal.hasPendingForTurnId(parkTurnId);
80555
+ if (handbackOwnsSurface) {
80556
+ process.stderr.write(`telegram gateway: queued card suppressed (handback owns surface) ` + `turnId=${parkTurnId} msg=${ev.messageId ?? "-"}
80557
+ `);
80558
+ }
80559
+ if (QUEUED_CARD_ENABLED && !handbackOwnsSurface) {
80560
+ const cardChatId = ev.chatId;
80561
+ const replyToRaw = ev.messageId != null ? Number(ev.messageId) : null;
80562
+ const replyTo = replyToRaw != null && Number.isFinite(replyToRaw) ? replyToRaw : null;
80563
+ openQueuedCard(deps, cardChatId, enqThreadIdNum ?? null, replyTo).then((cardId) => {
80564
+ if (cardId == null)
80565
+ return;
80566
+ if (parkedTurnStarts.includes(parkedEntry)) {
80567
+ parkedEntry.queuedCardMessageId = cardId;
80568
+ } else {
80569
+ deleteQueuedCard(deps, cardChatId, cardId);
80570
+ }
80571
+ });
80572
+ }
80416
80573
  return;
80417
80574
  }
80418
80575
  case "dequeue": {
80419
- pruneParkedTurnStarts(Date.now());
80576
+ pruneParkedTurnStarts(Date.now(), (e) => finalizeParkedEntryCard(deps, e, QUEUED_CARD_EXPIRED_HTML));
80420
80577
  const parked = takeParkedTurnStart();
80421
80578
  if (parked == null)
80422
80579
  return;
@@ -80424,8 +80581,10 @@ function handleSessionEvent(deps, ev) {
80424
80581
  return;
80425
80582
  }
80426
80583
  case "queue_remove": {
80427
- pruneParkedTurnStarts(Date.now());
80428
- discardParkedTurnStart(ev.rawContent);
80584
+ pruneParkedTurnStarts(Date.now(), (e) => finalizeParkedEntryCard(deps, e, QUEUED_CARD_EXPIRED_HTML));
80585
+ const removed = discardParkedTurnStart(ev.rawContent);
80586
+ if (removed != null)
80587
+ finalizeParkedEntryCard(deps, removed, QUEUED_CARD_FOLDED_HTML);
80429
80588
  return;
80430
80589
  }
80431
80590
  case "model": {
@@ -80895,6 +81054,7 @@ function handleSessionEvent(deps, ev) {
80895
81054
  let sentIds = [];
80896
81055
  let chunkCount = 0;
80897
81056
  let delivered = false;
81057
+ let earlyJournaled = false;
80898
81058
  try {
80899
81059
  const delivery = await deliverAnswer({
80900
81060
  chatId: backstopChatId,
@@ -80902,7 +81062,21 @@ function handleSessionEvent(deps, ev) {
80902
81062
  text: capturedText,
80903
81063
  turnId: turn.turnId,
80904
81064
  cardMessageId: backstopCardMessageId,
80905
- replyToMessageId: turn.sourceMessageId
81065
+ replyToMessageId: turn.sourceMessageId,
81066
+ onAckClaim: (ackIds) => {
81067
+ try {
81068
+ journalExternalDelivery({
81069
+ turnNonce: turn.turnId,
81070
+ text: capturedText,
81071
+ tgMessageId: ackIds.length > 0 ? ackIds[0] : undefined,
81072
+ deliverySource: "flush"
81073
+ }, STATE_DIR);
81074
+ earlyJournaled = true;
81075
+ } catch (err) {
81076
+ process.stderr.write(`telegram gateway: turn-flush send-ack journal write failed (non-fatal): ${err.message}
81077
+ `);
81078
+ }
81079
+ }
80906
81080
  });
80907
81081
  sentIds = delivery.sentIds;
80908
81082
  chunkCount = delivery.chunkCount;
@@ -80948,7 +81122,7 @@ function handleSessionEvent(deps, ev) {
80948
81122
  chunkCount,
80949
81123
  cardMessageId: backstopCardMessageId
80950
81124
  });
80951
- if (delivered) {
81125
+ if (delivered && !earlyJournaled) {
80952
81126
  try {
80953
81127
  journalExternalDelivery({
80954
81128
  turnNonce: turn.turnId,
@@ -84944,8 +85118,8 @@ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
84944
85118
  var HINDSIGHT_PG_APP_ANON_MIB = 2560;
84945
85119
  var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
84946
85120
  var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
84947
- var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 3072;
84948
- var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 7168;
85121
+ var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 6144;
85122
+ var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 12288;
84949
85123
  function pgMib(mib) {
84950
85124
  return `${mib}MB`;
84951
85125
  }
@@ -84989,7 +85163,7 @@ var HINDSIGHT_DEFAULT_RERANKER_MAX_CANDIDATES = 150;
84989
85163
  var HINDSIGHT_DEFAULT_RERANKER_LOCAL_MAX_CONCURRENT = 4;
84990
85164
  var HINDSIGHT_DEFAULT_RECALL_MAX_CONCURRENT = 8;
84991
85165
  var HINDSIGHT_DEFAULT_REFLECT_WALL_TIMEOUT_S = 600;
84992
- var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 500;
85166
+ var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_MEMORIES_PER_ROUND = 250;
84993
85167
  var HINDSIGHT_DEFAULT_CONSOLIDATION_SLOT_LIMIT = 6;
84994
85168
  var HINDSIGHT_DEFAULT_CONSOLIDATION_MAX_SLOTS = 1;
84995
85169
  var HINDSIGHT_DEFAULT_RECENCY_DECAY_FUNCTION = "exponential";
@@ -91731,6 +91905,7 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
91731
91905
  const stderr = deps.stderr ?? (() => {});
91732
91906
  const chunkCount = chunks.length;
91733
91907
  let attempts = 0;
91908
+ let ackClaimed = false;
91734
91909
  for (let attempt = 1;attempt <= Math.max(1, maxAttempts); attempt++) {
91735
91910
  attempts = attempt;
91736
91911
  const resume = ledger.unsentIndices(turnId, chunkCount);
@@ -91754,6 +91929,18 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
91754
91929
  `);
91755
91930
  }
91756
91931
  }
91932
+ if (!ackClaimed && deps.onAckClaim != null) {
91933
+ const allLandedNow = chunkCount > 0 && ledger.unsentIndices(turnId, chunkCount).length === 0;
91934
+ if (allLandedNow && backstopReceiptIds(ledger.sentIds(turnId), cardMessageId).length > 0) {
91935
+ ackClaimed = true;
91936
+ try {
91937
+ deps.onAckClaim(ledger.sentIds(turnId));
91938
+ } catch (err) {
91939
+ stderr(`telegram gateway: backstop send-ack claim callback threw for turn ${turnId} ` + `(non-fatal): ${err instanceof Error ? err.message : String(err)}
91940
+ `);
91941
+ }
91942
+ }
91943
+ }
91757
91944
  let demotedAny = false;
91758
91945
  for (const i of ledger.landedUnconfirmedIndices(turnId, chunkCount)) {
91759
91946
  if (deps.readBack == null) {
@@ -93118,6 +93305,28 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
93118
93305
  return { purged };
93119
93306
  }
93120
93307
 
93308
+ // gateway/compaction-marker.ts
93309
+ import { statSync as statSync15, unlinkSync as unlinkSync21 } from "node:fs";
93310
+ import { homedir as homedir15 } from "node:os";
93311
+ import { join as join50 } from "node:path";
93312
+ var COMPACTION_MARKER_FILE = "compaction-in-flight.json";
93313
+ function resolveTelegramStateDir() {
93314
+ return process.env.TELEGRAM_STATE_DIR ?? join50(homedir15(), ".claude", "channels", "telegram");
93315
+ }
93316
+ function readCompactionMarkerAgeMs(stateDir, now) {
93317
+ try {
93318
+ const st = statSync15(join50(stateDir, COMPACTION_MARKER_FILE));
93319
+ return (now ?? Date.now()) - st.mtimeMs;
93320
+ } catch {
93321
+ return null;
93322
+ }
93323
+ }
93324
+ function removeCompactionMarker(stateDir) {
93325
+ try {
93326
+ unlinkSync21(join50(stateDir, COMPACTION_MARKER_FILE));
93327
+ } catch {}
93328
+ }
93329
+
93121
93330
  // gateway/hang-restart-decision.ts
93122
93331
  var HUMAN_WAIT_TOOLS2 = new Set(["ask_user"]);
93123
93332
  var BACKGROUND_TOOLS2 = new Set(["Task", "Agent"]);
@@ -93225,6 +93434,10 @@ function buildSilencePokeOptions(deps) {
93225
93434
  thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
93226
93435
  deferFallbackWhileToolInFlight: SILENCE_DEFER_INFLIGHT_TOOLS,
93227
93436
  isLegitimatelyWorking: (key) => isLegitimatelyWorking(key),
93437
+ isCompactionInFlight: () => {
93438
+ const ageMs = readCompactionMarkerAgeMs(STATE_DIR);
93439
+ return ageMs != null && ageMs >= 0 && ageMs < SILENCE_FALLBACK_HARD_MS;
93440
+ },
93228
93441
  isTurnLive: (key) => !(activeTurnStartedAt.get(key) == null && getCurrentTurnForKey(key) == null),
93229
93442
  emitMetric: (event) => {
93230
93443
  emitRuntimeMetric2(event);
@@ -93648,6 +93861,9 @@ function applySilencePokeSessionEvent(silencePoke, pendingProgress, key, ev) {
93648
93861
  if (ev.toolUseId != null && ev.toolUseId.length > 0) {
93649
93862
  silencePoke.noteToolEnd(key, ev.toolUseId, Date.now());
93650
93863
  }
93864
+ } else if (ev.kind === "compact_boundary") {
93865
+ removeCompactionMarker(resolveTelegramStateDir());
93866
+ silencePoke.noteProduction(key, Date.now());
93651
93867
  }
93652
93868
  applyBackgroundShellLiveness(silencePoke, key, ev);
93653
93869
  }
@@ -93933,17 +94149,17 @@ import {
93933
94149
  readFileSync as readFileSync37,
93934
94150
  writeSync as writeSync6
93935
94151
  } from "node:fs";
93936
- import { join as join50 } from "node:path";
94152
+ import { join as join51 } from "node:path";
93937
94153
  import { randomUUID as randomUUID5 } from "node:crypto";
93938
94154
  var PROPOSALS_FILE2 = "skill-proposals.jsonl";
93939
94155
  var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
93940
94156
  var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
93941
94157
  var PROPOSAL_SIM_THRESHOLD = 0.5;
93942
94158
  function proposalsPath2(stateDir) {
93943
- return join50(stateDir, PROPOSALS_FILE2);
94159
+ return join51(stateDir, PROPOSALS_FILE2);
93944
94160
  }
93945
94161
  function rejectedPath2(stateDir) {
93946
- return join50(stateDir, REJECTED_FILE2);
94162
+ return join51(stateDir, REJECTED_FILE2);
93947
94163
  }
93948
94164
  function ensureDir3(stateDir) {
93949
94165
  if (!existsSync41(stateDir)) {
@@ -94561,7 +94777,7 @@ function escapeBody2(s) {
94561
94777
  }
94562
94778
 
94563
94779
  // gateway/pid-file.ts
94564
- import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync21, renameSync as renameSync18 } from "node:fs";
94780
+ import { writeFileSync as writeFileSync33, readFileSync as readFileSync38, unlinkSync as unlinkSync22, renameSync as renameSync18 } from "node:fs";
94565
94781
  function writePidFile(path3, record) {
94566
94782
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
94567
94783
  writeFileSync33(tmp, JSON.stringify(record), "utf-8");
@@ -94569,7 +94785,7 @@ function writePidFile(path3, record) {
94569
94785
  }
94570
94786
  function clearPidFile(path3) {
94571
94787
  try {
94572
- unlinkSync21(path3);
94788
+ unlinkSync22(path3);
94573
94789
  } catch {}
94574
94790
  }
94575
94791
 
@@ -94786,7 +95002,7 @@ function safeCount(fn) {
94786
95002
  }
94787
95003
 
94788
95004
  // gateway/session-marker.ts
94789
- import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync19, unlinkSync as unlinkSync22 } from "node:fs";
95005
+ import { writeFileSync as writeFileSync34, readFileSync as readFileSync40, renameSync as renameSync19, unlinkSync as unlinkSync23 } from "node:fs";
94790
95006
  function writeSessionMarker(path3, marker) {
94791
95007
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
94792
95008
  writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
@@ -94816,7 +95032,7 @@ function shouldFireRestartBanner(input) {
94816
95032
  }
94817
95033
 
94818
95034
  // gateway/clean-shutdown-marker.ts
94819
- import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync20, unlinkSync as unlinkSync23 } from "node:fs";
95035
+ import { writeFileSync as writeFileSync35, readFileSync as readFileSync41, renameSync as renameSync20, unlinkSync as unlinkSync24 } from "node:fs";
94820
95036
  var DEFAULT_MAX_AGE_MS = 60000;
94821
95037
  function writeCleanShutdownMarker(path3, marker) {
94822
95038
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
@@ -94840,7 +95056,7 @@ function readCleanShutdownMarker(path3) {
94840
95056
  }
94841
95057
  function clearCleanShutdownMarker(path3) {
94842
95058
  try {
94843
- unlinkSync23(path3);
95059
+ unlinkSync24(path3);
94844
95060
  } catch {}
94845
95061
  }
94846
95062
  function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
@@ -95200,13 +95416,13 @@ import {
95200
95416
  existsSync as existsSync42,
95201
95417
  openSync as openSync10,
95202
95418
  readSync as readSync2,
95203
- statSync as statSync15,
95419
+ statSync as statSync16,
95204
95420
  closeSync as closeSync10,
95205
95421
  watch,
95206
95422
  readdirSync as readdirSync10,
95207
95423
  readFileSync as readFileSync42
95208
95424
  } from "fs";
95209
- import { join as join51 } from "path";
95425
+ import { join as join52 } from "path";
95210
95426
 
95211
95427
  // session-tail.ts
95212
95428
  function sanitizeCwdToProjectName2(cwd) {
@@ -96013,7 +96229,7 @@ function startSubagentWatcher(config) {
96013
96229
  const fs3 = config.fs ?? {
96014
96230
  existsSync: existsSync42,
96015
96231
  readdirSync: readdirSync10,
96016
- statSync: statSync15,
96232
+ statSync: statSync16,
96017
96233
  openSync: openSync10,
96018
96234
  closeSync: closeSync10,
96019
96235
  readSync: readSync2,
@@ -96529,8 +96745,8 @@ function startSubagentWatcher(config) {
96529
96745
  if (stopped)
96530
96746
  return;
96531
96747
  pruneVanishedDirWatchers();
96532
- const claudeHome = join51(agentDir, ".claude");
96533
- const projectsRoot = join51(claudeHome, "projects");
96748
+ const claudeHome = join52(agentDir, ".claude");
96749
+ const projectsRoot = join52(claudeHome, "projects");
96534
96750
  if (!fs3.existsSync(projectsRoot))
96535
96751
  return;
96536
96752
  let projectDirs;
@@ -96564,7 +96780,7 @@ function startSubagentWatcher(config) {
96564
96780
  continue;
96565
96781
  }
96566
96782
  warnedForeignSlugs.delete(pDir);
96567
- const projectPath = join51(projectsRoot, pDir);
96783
+ const projectPath = join52(projectsRoot, pDir);
96568
96784
  let sessionDirs;
96569
96785
  try {
96570
96786
  sessionDirs = fs3.readdirSync(projectPath);
@@ -96574,7 +96790,7 @@ function startSubagentWatcher(config) {
96574
96790
  for (const sDir of sessionDirs) {
96575
96791
  if (sDir.endsWith(".jsonl"))
96576
96792
  continue;
96577
- const subagentsPath = join51(projectPath, sDir, "subagents");
96793
+ const subagentsPath = join52(projectPath, sDir, "subagents");
96578
96794
  if (!fs3.existsSync(subagentsPath))
96579
96795
  continue;
96580
96796
  const watchAndScan = (dirPath) => {
@@ -96583,7 +96799,7 @@ function startSubagentWatcher(config) {
96583
96799
  const w = fs3.watch(dirPath, (_event, filename) => {
96584
96800
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
96585
96801
  return;
96586
- const filePath = join51(dirPath, filename.toString());
96802
+ const filePath = join52(dirPath, filename.toString());
96587
96803
  if (!knownFiles.has(filePath)) {
96588
96804
  scanSubagentsDir(dirPath);
96589
96805
  }
@@ -96597,7 +96813,7 @@ function startSubagentWatcher(config) {
96597
96813
  scanSubagentsDir(dirPath);
96598
96814
  };
96599
96815
  watchAndScan(subagentsPath);
96600
- const workflowsPath = join51(subagentsPath, "workflows");
96816
+ const workflowsPath = join52(subagentsPath, "workflows");
96601
96817
  if (fs3.existsSync(workflowsPath)) {
96602
96818
  let wfDirs;
96603
96819
  try {
@@ -96607,7 +96823,7 @@ function startSubagentWatcher(config) {
96607
96823
  }
96608
96824
  for (const wfDir of wfDirs) {
96609
96825
  try {
96610
- const wfPath = join51(workflowsPath, wfDir);
96826
+ const wfPath = join52(workflowsPath, wfDir);
96611
96827
  if (!fs3.statSync(wfPath).isDirectory())
96612
96828
  continue;
96613
96829
  watchAndScan(wfPath);
@@ -96627,7 +96843,7 @@ function startSubagentWatcher(config) {
96627
96843
  for (const e of entries) {
96628
96844
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
96629
96845
  continue;
96630
- const filePath = join51(subagentsPath, e);
96846
+ const filePath = join52(subagentsPath, e);
96631
96847
  if (knownFiles.has(filePath))
96632
96848
  continue;
96633
96849
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -96759,17 +96975,17 @@ import {
96759
96975
  writeFileSync as writeFileSync36,
96760
96976
  readFileSync as readFileSync43,
96761
96977
  readdirSync as readdirSync11,
96762
- unlinkSync as unlinkSync24,
96978
+ unlinkSync as unlinkSync25,
96763
96979
  existsSync as existsSync43,
96764
96980
  renameSync as renameSync21
96765
96981
  } from "node:fs";
96766
- import { join as join52, resolve as resolve10 } from "node:path";
96767
- import { homedir as homedir15 } from "node:os";
96982
+ import { join as join53, resolve as resolve10 } from "node:path";
96983
+ import { homedir as homedir16 } from "node:os";
96768
96984
  function registryDir() {
96769
- return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join52(homedir15(), ".switchroom", "worktrees"));
96985
+ return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join53(homedir16(), ".switchroom", "worktrees"));
96770
96986
  }
96771
96987
  function recordPath(id) {
96772
- return join52(registryDir(), `${id}.json`);
96988
+ return join53(registryDir(), `${id}.json`);
96773
96989
  }
96774
96990
  function ensureDir4() {
96775
96991
  mkdirSync38(registryDir(), { recursive: true });
@@ -96945,19 +97161,19 @@ function determineRestartReason(opts) {
96945
97161
 
96946
97162
  // gateway/update-announce.ts
96947
97163
  import { existsSync as existsSync48, mkdirSync as mkdirSync42, openSync as openSync11, closeSync as closeSync11, readFileSync as readFileSync50 } from "node:fs";
96948
- import { join as join57 } from "node:path";
96949
- import { homedir as homedir17 } from "node:os";
97164
+ import { join as join58 } from "node:path";
97165
+ import { homedir as homedir18 } from "node:os";
96950
97166
 
96951
97167
  // ../src/host-control/audit-reader.ts
96952
- import { homedir as homedir16 } from "node:os";
96953
- import { join as join56 } from "node:path";
97168
+ import { homedir as homedir17 } from "node:os";
97169
+ import { join as join57 } from "node:path";
96954
97170
 
96955
97171
  // ../src/host-control/audit-rotation-config.ts
96956
97172
  var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
96957
97173
 
96958
97174
  // ../src/host-control/audit-reader.ts
96959
- function defaultAuditLogPath(home2 = homedir16()) {
96960
- return join56(home2, ".switchroom", "host-control-audit.log");
97175
+ function defaultAuditLogPath(home2 = homedir17()) {
97176
+ return join57(home2, ".switchroom", "host-control-audit.log");
96961
97177
  }
96962
97178
  var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
96963
97179
  function parseAuditLine(line) {
@@ -97152,15 +97368,15 @@ function renderUpdateOutcomeLine(entry) {
97152
97368
  `);
97153
97369
  }
97154
97370
  function claimUpdateAnnouncement(requestId, opts = {}) {
97155
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join57(homedir17(), ".switchroom");
97156
- const dir = join57(stateDir, "update-announced");
97371
+ const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join58(homedir18(), ".switchroom");
97372
+ const dir = join58(stateDir, "update-announced");
97157
97373
  try {
97158
97374
  mkdirSync42(dir, { recursive: true });
97159
97375
  } catch {
97160
97376
  return false;
97161
97377
  }
97162
97378
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
97163
- const path3 = join57(dir, safeId);
97379
+ const path3 = join58(dir, safeId);
97164
97380
  try {
97165
97381
  const fd = openSync11(path3, "wx");
97166
97382
  closeSync11(fd);
@@ -97381,8 +97597,8 @@ function createIssuesCardHandle(opts) {
97381
97597
  }
97382
97598
 
97383
97599
  // issues-watcher.ts
97384
- import { existsSync as existsSync50, statSync as statSync18 } from "node:fs";
97385
- import { join as join59 } from "node:path";
97600
+ import { existsSync as existsSync50, statSync as statSync19 } from "node:fs";
97601
+ import { join as join60 } from "node:path";
97386
97602
 
97387
97603
  // ../src/issues/store.ts
97388
97604
  import {
@@ -97393,12 +97609,12 @@ import {
97393
97609
  readdirSync as readdirSync13,
97394
97610
  readFileSync as readFileSync52,
97395
97611
  renameSync as renameSync24,
97396
- statSync as statSync17,
97397
- unlinkSync as unlinkSync25,
97612
+ statSync as statSync18,
97613
+ unlinkSync as unlinkSync26,
97398
97614
  writeFileSync as writeFileSync42,
97399
97615
  writeSync as writeSync7
97400
97616
  } from "node:fs";
97401
- import { join as join58 } from "node:path";
97617
+ import { join as join59 } from "node:path";
97402
97618
  import { randomBytes as randomBytes11 } from "node:crypto";
97403
97619
  import { execSync } from "node:child_process";
97404
97620
 
@@ -97417,7 +97633,7 @@ init_redact();
97417
97633
  var ISSUES_FILE = "issues.jsonl";
97418
97634
  var ISSUES_LOCK = "issues.lock";
97419
97635
  function readAll(stateDir) {
97420
- const path3 = join58(stateDir, ISSUES_FILE);
97636
+ const path3 = join59(stateDir, ISSUES_FILE);
97421
97637
  if (!existsSync49(path3))
97422
97638
  return [];
97423
97639
  let raw;
@@ -97454,7 +97670,7 @@ function list2(stateDir, opts = {}) {
97454
97670
  });
97455
97671
  }
97456
97672
  function resolve11(stateDir, fingerprint, nowFn = Date.now) {
97457
- if (!existsSync49(join58(stateDir, ISSUES_FILE)))
97673
+ if (!existsSync49(join59(stateDir, ISSUES_FILE)))
97458
97674
  return 0;
97459
97675
  return withLock(stateDir, () => {
97460
97676
  const all2 = readAll(stateDir);
@@ -97472,7 +97688,7 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
97472
97688
  });
97473
97689
  }
97474
97690
  function writeAll(stateDir, events) {
97475
- const path3 = join58(stateDir, ISSUES_FILE);
97691
+ const path3 = join59(stateDir, ISSUES_FILE);
97476
97692
  sweepOrphanTmpFiles(stateDir);
97477
97693
  const tmp = `${path3}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
97478
97694
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -97494,11 +97710,11 @@ function sweepOrphanTmpFiles(stateDir) {
97494
97710
  for (const entry of entries) {
97495
97711
  if (!entry.startsWith(TMP_PREFIX))
97496
97712
  continue;
97497
- const tmpPath2 = join58(stateDir, entry);
97713
+ const tmpPath2 = join59(stateDir, entry);
97498
97714
  try {
97499
- const stat = statSync17(tmpPath2);
97715
+ const stat = statSync18(tmpPath2);
97500
97716
  if (stat.mtimeMs < cutoff) {
97501
- unlinkSync25(tmpPath2);
97717
+ unlinkSync26(tmpPath2);
97502
97718
  }
97503
97719
  } catch {}
97504
97720
  }
@@ -97506,7 +97722,7 @@ function sweepOrphanTmpFiles(stateDir) {
97506
97722
  var LOCK_RETRY_MS = 25;
97507
97723
  var LOCK_TIMEOUT_MS = 1e4;
97508
97724
  function withLock(stateDir, fn) {
97509
- const lockPath = join58(stateDir, ISSUES_LOCK);
97725
+ const lockPath = join59(stateDir, ISSUES_LOCK);
97510
97726
  const startedAt = Date.now();
97511
97727
  let fd = null;
97512
97728
  while (fd === null) {
@@ -97534,7 +97750,7 @@ function withLock(stateDir, fn) {
97534
97750
  closeSync12(fd);
97535
97751
  } catch {}
97536
97752
  try {
97537
- unlinkSync25(lockPath);
97753
+ unlinkSync26(lockPath);
97538
97754
  } catch {}
97539
97755
  }
97540
97756
  }
@@ -97548,13 +97764,13 @@ function tryStealStaleLock(lockPath) {
97548
97764
  const pid = Number(pidStr);
97549
97765
  if (!Number.isFinite(pid) || pid <= 0) {
97550
97766
  try {
97551
- unlinkSync25(lockPath);
97767
+ unlinkSync26(lockPath);
97552
97768
  } catch {}
97553
97769
  return true;
97554
97770
  }
97555
97771
  if (pid === process.pid) {
97556
97772
  try {
97557
- unlinkSync25(lockPath);
97773
+ unlinkSync26(lockPath);
97558
97774
  } catch {}
97559
97775
  return true;
97560
97776
  }
@@ -97569,7 +97785,7 @@ function tryStealStaleLock(lockPath) {
97569
97785
  return false;
97570
97786
  }
97571
97787
  try {
97572
- unlinkSync25(lockPath);
97788
+ unlinkSync26(lockPath);
97573
97789
  } catch {}
97574
97790
  return true;
97575
97791
  }
@@ -97591,7 +97807,7 @@ function isIssueEvent(v) {
97591
97807
  // issues-watcher.ts
97592
97808
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
97593
97809
  function startIssuesWatcher(opts) {
97594
- const path3 = join59(opts.stateDir, ISSUES_FILE);
97810
+ const path3 = join60(opts.stateDir, ISSUES_FILE);
97595
97811
  const log = opts.log ?? (() => {});
97596
97812
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
97597
97813
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -97642,7 +97858,7 @@ function defaultSignatureProvider(path3) {
97642
97858
  if (!existsSync50(path3))
97643
97859
  return null;
97644
97860
  try {
97645
- const stat = statSync18(path3);
97861
+ const stat = statSync19(path3);
97646
97862
  return `${stat.mtimeMs}:${stat.size}`;
97647
97863
  } catch {
97648
97864
  return null;
@@ -98351,7 +98567,7 @@ function readBashCommand(inputPreview) {
98351
98567
 
98352
98568
  // gateway/scoped-grant-store.ts
98353
98569
  init_atomic();
98354
- import { join as join60 } from "node:path";
98570
+ import { join as join61 } from "node:path";
98355
98571
 
98356
98572
  // scoped-approval.ts
98357
98573
  var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
@@ -98393,7 +98609,7 @@ function scopedGrantPersistEnabled(env = process.env) {
98393
98609
  return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
98394
98610
  }
98395
98611
  function createScopedGrantStore(stateDir, env = process.env, log = (l) => process.stderr.write(l)) {
98396
- const filePath = join60(stateDir, "scoped-grants.json");
98612
+ const filePath = join61(stateDir, "scoped-grants.json");
98397
98613
  const enabled7 = scopedGrantPersistEnabled(env);
98398
98614
  let unreadable = false;
98399
98615
  function read() {
@@ -98772,7 +98988,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
98772
98988
  // credits-watch.ts
98773
98989
  init_card_format();
98774
98990
  import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync51, mkdirSync as mkdirSync44 } from "fs";
98775
- import { join as join61 } from "path";
98991
+ import { join as join62 } from "path";
98776
98992
  var STATE_FILE = "credits-watch.json";
98777
98993
  var DEFAULT_CREDIT_FATAL_REASONS = new Set;
98778
98994
  var KNOWN_CREDIT_REASONS = [
@@ -98794,7 +99010,7 @@ function emptyCreditState() {
98794
99010
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
98795
99011
  }
98796
99012
  function readClaudeJsonOverage(claudeConfigDir) {
98797
- const path3 = join61(claudeConfigDir, ".claude.json");
99013
+ const path3 = join62(claudeConfigDir, ".claude.json");
98798
99014
  if (!existsSync51(path3))
98799
99015
  return null;
98800
99016
  let raw;
@@ -98877,7 +99093,7 @@ function humanizeReason(reason) {
98877
99093
  }
98878
99094
  }
98879
99095
  function loadCreditState(stateDir) {
98880
- const path3 = join61(stateDir, STATE_FILE);
99096
+ const path3 = join62(stateDir, STATE_FILE);
98881
99097
  if (!existsSync51(path3))
98882
99098
  return emptyCreditState();
98883
99099
  try {
@@ -98894,7 +99110,7 @@ function loadCreditState(stateDir) {
98894
99110
  }
98895
99111
  function saveCreditState(stateDir, state7) {
98896
99112
  mkdirSync44(stateDir, { recursive: true });
98897
- const path3 = join61(stateDir, STATE_FILE);
99113
+ const path3 = join62(stateDir, STATE_FILE);
98898
99114
  writeFileSync43(path3, JSON.stringify(state7, null, 2) + `
98899
99115
  `, { mode: 384 });
98900
99116
  }
@@ -98903,7 +99119,7 @@ function saveCreditState(stateDir, state7) {
98903
99119
  init_auth_snapshot_format();
98904
99120
  init_card_format();
98905
99121
  import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync52, mkdirSync as mkdirSync45 } from "fs";
98906
- import { join as join62 } from "path";
99122
+ import { join as join63 } from "path";
98907
99123
  var STATE_FILE2 = "quota-watch.json";
98908
99124
  function emptyQuotaWatchState() {
98909
99125
  return {};
@@ -99141,7 +99357,7 @@ function buildRecoveryMessage(agentName3, snap) {
99141
99357
  `);
99142
99358
  }
99143
99359
  function loadQuotaWatchState(stateDir) {
99144
- const path3 = join62(stateDir, STATE_FILE2);
99360
+ const path3 = join63(stateDir, STATE_FILE2);
99145
99361
  if (!existsSync52(path3))
99146
99362
  return emptyQuotaWatchState();
99147
99363
  try {
@@ -99163,7 +99379,7 @@ function loadQuotaWatchState(stateDir) {
99163
99379
  }
99164
99380
  function saveQuotaWatchState(stateDir, state7) {
99165
99381
  mkdirSync45(stateDir, { recursive: true });
99166
- const path3 = join62(stateDir, STATE_FILE2);
99382
+ const path3 = join63(stateDir, STATE_FILE2);
99167
99383
  writeFileSync44(path3, JSON.stringify(state7, null, 2) + `
99168
99384
  `, { mode: 384 });
99169
99385
  }
@@ -99216,27 +99432,27 @@ import {
99216
99432
  mkdirSync as mkdirSync46,
99217
99433
  openSync as openSync13,
99218
99434
  readFileSync as readFileSync55,
99219
- statSync as statSync19,
99220
- unlinkSync as unlinkSync26,
99435
+ statSync as statSync20,
99436
+ unlinkSync as unlinkSync27,
99221
99437
  utimesSync as utimesSync2,
99222
99438
  writeFileSync as writeFileSync45
99223
99439
  } from "node:fs";
99224
- import { join as join63 } from "node:path";
99440
+ import { join as join64 } from "node:path";
99225
99441
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
99226
99442
  var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
99227
99443
  var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
99228
99444
  function removeTurnActiveMarker2(stateDir) {
99229
99445
  try {
99230
- unlinkSync26(join63(stateDir, TURN_ACTIVE_MARKER_FILE2));
99446
+ unlinkSync27(join64(stateDir, TURN_ACTIVE_MARKER_FILE2));
99231
99447
  } catch {}
99232
99448
  }
99233
99449
  function sweepStaleTurnActiveMarker(stateDir, opts) {
99234
- const path3 = join63(stateDir, TURN_ACTIVE_MARKER_FILE2);
99450
+ const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
99235
99451
  if (!existsSync53(path3))
99236
99452
  return false;
99237
99453
  const now = opts.now ?? Date.now();
99238
99454
  try {
99239
- const st = statSync19(path3);
99455
+ const st = statSync20(path3);
99240
99456
  const ageMs = now - st.mtimeMs;
99241
99457
  const hardExpired = ageMs > opts.hardTtlMs;
99242
99458
  const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
@@ -99246,7 +99462,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
99246
99462
  try {
99247
99463
  payload = readFileSync55(path3, "utf8");
99248
99464
  } catch {}
99249
- unlinkSync26(path3);
99465
+ unlinkSync27(path3);
99250
99466
  if (opts.onRemove) {
99251
99467
  try {
99252
99468
  opts.onRemove({
@@ -99262,9 +99478,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
99262
99478
  }
99263
99479
  }
99264
99480
  function readTurnActiveMarkerAgeMs2(stateDir, now) {
99265
- const path3 = join63(stateDir, TURN_ACTIVE_MARKER_FILE2);
99481
+ const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
99266
99482
  try {
99267
- const st = statSync19(path3);
99483
+ const st = statSync20(path3);
99268
99484
  return (now ?? Date.now()) - st.mtimeMs;
99269
99485
  } catch {
99270
99486
  return null;
@@ -99276,11 +99492,11 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
99276
99492
 
99277
99493
  // gateway/gateway-heartbeat.ts
99278
99494
  import { mkdirSync as mkdirSync47, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
99279
- import { join as join64 } from "node:path";
99495
+ import { join as join65 } from "node:path";
99280
99496
  var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
99281
99497
  var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
99282
99498
  function touchGatewayHeartbeat(stateDir) {
99283
- const path3 = join64(stateDir, GATEWAY_HEARTBEAT_FILE);
99499
+ const path3 = join65(stateDir, GATEWAY_HEARTBEAT_FILE);
99284
99500
  const now = new Date;
99285
99501
  try {
99286
99502
  utimesSync3(path3, now, now);
@@ -99308,10 +99524,10 @@ import {
99308
99524
  openSync as openSync14,
99309
99525
  readFileSync as readFileSync56,
99310
99526
  renameSync as renameSync25,
99311
- unlinkSync as unlinkSync27,
99527
+ unlinkSync as unlinkSync28,
99312
99528
  writeFileSync as writeFileSync47
99313
99529
  } from "node:fs";
99314
- import { join as join65 } from "node:path";
99530
+ import { join as join66 } from "node:path";
99315
99531
  var BOOT_BEACON_FILE = "gateway-beacon.json";
99316
99532
  var HOST_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id";
99317
99533
  var CGROUP_MEMORY_EVENTS_PATH = "/sys/fs/cgroup/memory.events";
@@ -99410,7 +99626,7 @@ function sampleBeaconHost(paths = {}) {
99410
99626
  return sample3;
99411
99627
  }
99412
99628
  function writeBootBeaconFile(stateDir, beacon) {
99413
- const path3 = join65(stateDir, BOOT_BEACON_FILE);
99629
+ const path3 = join66(stateDir, BOOT_BEACON_FILE);
99414
99630
  const tmp = `${path3}.tmp-${process.pid}`;
99415
99631
  let fd;
99416
99632
  try {
@@ -99429,7 +99645,7 @@ function writeBootBeaconFile(stateDir, beacon) {
99429
99645
  } catch {}
99430
99646
  }
99431
99647
  try {
99432
- unlinkSync27(tmp);
99648
+ unlinkSync28(tmp);
99433
99649
  } catch {}
99434
99650
  return false;
99435
99651
  }
@@ -99457,6 +99673,8 @@ function attachBootBeacon(stateDir, next) {
99457
99673
  }
99458
99674
 
99459
99675
  // gateway/outbox-sweep.ts
99676
+ init_rich_send();
99677
+ init_format();
99460
99678
  init_flood_circuit_breaker();
99461
99679
  async function sweepOutbox(deps) {
99462
99680
  const now = deps.now?.() ?? Date.now();
@@ -99529,15 +99747,20 @@ async function sweepOutbox(deps) {
99529
99747
  }
99530
99748
  const resolvedChat = resolved;
99531
99749
  try {
99532
- const messageId = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
99750
+ const sendResult = await deps.send(resolvedChat.chatId, resolvedChat.threadId, decision.text ?? record2.text);
99533
99751
  appendDelivered({
99534
99752
  turnNonce: record2.turnNonce,
99535
99753
  textSha256: record2.textSha256,
99536
- tgMessageId: messageId,
99754
+ tgMessageId: sendResult.messageId,
99537
99755
  ts: now,
99538
99756
  deliverySource: "sweep",
99539
99757
  replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
99540
99758
  }, deps.stateDir);
99759
+ if (deps.recordOutbound != null && sendResult.chunks.length > 0) {
99760
+ try {
99761
+ deps.recordOutbound(resolvedChat.chatId, resolvedChat.threadId, sendResult.chunks.map((c) => c.messageId), sendResult.chunks.map((c) => c.text));
99762
+ } catch {}
99763
+ }
99541
99764
  removeClaimed(record2.turnNonce, deps.stateDir);
99542
99765
  summary.delivered++;
99543
99766
  log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
@@ -99592,27 +99815,42 @@ function createSweepBackoff(cfg = {}) {
99592
99815
  failures: () => failures
99593
99816
  };
99594
99817
  }
99818
+ async function sendChunkRich(bot, chatId, chunk2, opts) {
99819
+ try {
99820
+ return await bot.api.sendRichMessage(chatId, richMessage(chunk2), opts);
99821
+ } catch (err) {
99822
+ if (isParseEntitiesError(err)) {
99823
+ return await bot.api.sendMessage(chatId, chunk2, opts);
99824
+ }
99825
+ throw err;
99826
+ }
99827
+ }
99595
99828
  function createOutboxSend(deps) {
99596
99829
  return async (chatId, threadId, text4) => {
99597
99830
  const bot = deps.getBot();
99598
99831
  if (bot == null)
99599
99832
  throw new Error("outbox-sweep: bot unavailable");
99600
99833
  if (text4.length === 0)
99601
- return;
99834
+ return { messageId: undefined, chunks: [] };
99602
99835
  const replyMarkup = deps.resolveReplyMarkup?.(chatId, threadId, text4);
99603
- let lastId;
99604
- const chunkCount = Math.ceil(text4.length / 4000);
99605
- for (let i = 0, idx = 0;i < text4.length; i += 4000, idx++) {
99606
- const chunk2 = text4.slice(i, i + 4000);
99607
- const isLast = idx === chunkCount - 1;
99836
+ const landed = [];
99837
+ const chunks = splitMarkdownChunks(text4);
99838
+ for (let idx = 0;idx < chunks.length; idx++) {
99839
+ const chunk2 = chunks[idx];
99840
+ const isLast = idx === chunks.length - 1;
99608
99841
  const res = await retryWithThreadFallback(deps.retry, (tid) => {
99609
99842
  const base = tid != null ? { message_thread_id: tid } : {};
99610
99843
  const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
99611
- return bot.api.sendMessage(chatId, chunk2, opts);
99844
+ return sendChunkRich(bot, chatId, chunk2, opts);
99612
99845
  }, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
99613
- lastId = res?.message_id;
99846
+ const id = res?.message_id;
99847
+ if (id != null)
99848
+ landed.push({ messageId: id, text: chunk2 });
99614
99849
  }
99615
- return lastId;
99850
+ return {
99851
+ messageId: landed.length > 0 ? landed[landed.length - 1].messageId : undefined,
99852
+ chunks: landed
99853
+ };
99616
99854
  };
99617
99855
  }
99618
99856
  function startOutboxSweep(deps) {
@@ -99643,6 +99881,7 @@ function startOutboxSweep(deps) {
99643
99881
  stateDir: deps.stateDir,
99644
99882
  log: deps.log,
99645
99883
  send,
99884
+ ...deps.recordOutbound != null ? { recordOutbound: deps.recordOutbound } : {},
99646
99885
  floodWaitRemainingMs: floodProbe,
99647
99886
  textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
99648
99887
  registryChainLookup: (taskId) => {
@@ -99678,10 +99917,10 @@ function startOutboxSweep(deps) {
99678
99917
  }
99679
99918
 
99680
99919
  // ../src/build-info.ts
99681
- var VERSION2 = "0.19.38";
99682
- var COMMIT_SHA = "9ec88983";
99683
- var COMMIT_DATE = "2026-07-30T09:50:08Z";
99684
- var LATEST_PR = 4031;
99920
+ var VERSION2 = "0.19.40";
99921
+ var COMMIT_SHA = "89286b22";
99922
+ var COMMIT_DATE = "2026-07-31T04:28:56Z";
99923
+ var LATEST_PR = 4061;
99685
99924
  var COMMITS_AHEAD_OF_TAG = 0;
99686
99925
 
99687
99926
  // gateway/boot-version.ts
@@ -99758,12 +99997,12 @@ init_protocol2();
99758
99997
  init_peercred();
99759
99998
  import * as net5 from "node:net";
99760
99999
  import * as fs3 from "node:fs";
99761
- import { homedir as homedir18 } from "node:os";
99762
- import { join as join66 } from "node:path";
100000
+ import { homedir as homedir19 } from "node:os";
100001
+ import { join as join67 } from "node:path";
99763
100002
  var DEFAULT_TIMEOUT_MS4 = 2000;
99764
100003
  var UNLOCK_TIMEOUT_MS = 30000;
99765
- var LEGACY_SOCKET_PATH2 = join66(homedir18(), ".switchroom", "vault-broker.sock");
99766
- var OPERATOR_SOCKET_PATH2 = join66(homedir18(), ".switchroom", "broker-operator", "sock");
100004
+ var LEGACY_SOCKET_PATH2 = join67(homedir19(), ".switchroom", "vault-broker.sock");
100005
+ var OPERATOR_SOCKET_PATH2 = join67(homedir19(), ".switchroom", "broker-operator", "sock");
99767
100006
  function defaultBrokerSocketPath2() {
99768
100007
  if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
99769
100008
  return OPERATOR_SOCKET_PATH2;
@@ -100646,7 +100885,7 @@ function resolveVaultApprovalPosture(broker) {
100646
100885
 
100647
100886
  // registry/turns-schema.ts
100648
100887
  import { chmodSync as chmodSync12, mkdirSync as mkdirSync49 } from "fs";
100649
- import { join as join67 } from "path";
100888
+ import { join as join68 } from "path";
100650
100889
  var DatabaseClass3 = null;
100651
100890
  function loadDatabaseClass3() {
100652
100891
  if (DatabaseClass3 != null)
@@ -100718,9 +100957,9 @@ function applySchema(db3) {
100718
100957
  }
100719
100958
  function openTurnsDb(agentDir) {
100720
100959
  const Database = loadDatabaseClass3();
100721
- const dir = join67(agentDir, "telegram");
100960
+ const dir = join68(agentDir, "telegram");
100722
100961
  mkdirSync49(dir, { recursive: true, mode: 448 });
100723
- const path3 = join67(dir, "registry.db");
100962
+ const path3 = join68(dir, "registry.db");
100724
100963
  const db3 = new Database(path3, { create: true });
100725
100964
  applySchema(db3);
100726
100965
  try {
@@ -101065,7 +101304,7 @@ function selectResumeBuilder(endedVia, opts) {
101065
101304
  }
101066
101305
 
101067
101306
  // gateway/bridge-dead-watchdog.ts
101068
- import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync26, unlinkSync as unlinkSync28 } from "node:fs";
101307
+ import { readFileSync as readFileSync58, writeFileSync as writeFileSync48, renameSync as renameSync26, unlinkSync as unlinkSync29 } from "node:fs";
101069
101308
 
101070
101309
  // gateway/cron-session.ts
101071
101310
  var CRON_IDENTITY_SUFFIX2 = "-cron";
@@ -101137,7 +101376,7 @@ function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs
101137
101376
  }
101138
101377
  } catch {}
101139
101378
  try {
101140
- unlinkSync28(path3);
101379
+ unlinkSync29(path3);
101141
101380
  } catch {}
101142
101381
  return marker;
101143
101382
  }
@@ -101537,7 +101776,7 @@ if (isGatewayMain) {
101537
101776
  shutdownAnalytics();
101538
101777
  });
101539
101778
  }
101540
- var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join68(homedir19(), ".claude", "channels", "telegram");
101779
+ var STATE_DIR = process.env.TELEGRAM_STATE_DIR ?? join69(homedir20(), ".claude", "channels", "telegram");
101541
101780
  var permCardStore = createPermissionCardStore(STATE_DIR);
101542
101781
  var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
101543
101782
  var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
@@ -101637,11 +101876,11 @@ function scheduleAlwaysAllowPersistDrain() {
101637
101876
  }, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
101638
101877
  timer3.unref?.();
101639
101878
  }
101640
- var ACCESS_FILE = join68(STATE_DIR, "access.json");
101641
- var APPROVED_DIR = join68(STATE_DIR, "approved");
101642
- var ENV_FILE = join68(STATE_DIR, ".env");
101643
- var INBOX_DIR = join68(STATE_DIR, "inbox");
101644
- var PEOPLE_FILE = join68(STATE_DIR, "people.json");
101879
+ var ACCESS_FILE = join69(STATE_DIR, "access.json");
101880
+ var APPROVED_DIR = join69(STATE_DIR, "approved");
101881
+ var ENV_FILE = join69(STATE_DIR, ".env");
101882
+ var INBOX_DIR = join69(STATE_DIR, "inbox");
101883
+ var PEOPLE_FILE = join69(STATE_DIR, "people.json");
101645
101884
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
101646
101885
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
101647
101886
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -101790,7 +102029,7 @@ function assertSendable(f) {
101790
102029
  } catch {
101791
102030
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
101792
102031
  }
101793
- const inbox = join68(stateReal, "inbox");
102032
+ const inbox = join69(stateReal, "inbox");
101794
102033
  if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
101795
102034
  throw new Error(`refusing to send channel state: ${f}`);
101796
102035
  }
@@ -101915,7 +102154,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
101915
102154
  if (isGatewayMain && HISTORY_ENABLED) {
101916
102155
  try {
101917
102156
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
101918
- process.stderr.write(`telegram gateway: history capture enabled at ${join68(STATE_DIR, "history.db")}
102157
+ process.stderr.write(`telegram gateway: history capture enabled at ${join69(STATE_DIR, "history.db")}
101919
102158
  `);
101920
102159
  } catch (err) {
101921
102160
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -101934,9 +102173,9 @@ if (isGatewayMain)
101934
102173
  let markerTurnKey = null;
101935
102174
  let markerAgeMs = null;
101936
102175
  try {
101937
- const markerPath = join68(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
102176
+ const markerPath = join69(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
101938
102177
  if (existsSync57(markerPath)) {
101939
- const st = statSync22(markerPath);
102178
+ const st = statSync23(markerPath);
101940
102179
  markerAgeMs = Date.now() - st.mtimeMs;
101941
102180
  try {
101942
102181
  const payload = JSON.parse(readFileSync60(markerPath, "utf8"));
@@ -101959,10 +102198,10 @@ if (isGatewayMain)
101959
102198
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
101960
102199
  `);
101961
102200
  } else {
101962
- process.stderr.write(`telegram gateway: turn-registry initialized at ${join68(agentDir, "telegram", "registry.db")}
102201
+ process.stderr.write(`telegram gateway: turn-registry initialized at ${join69(agentDir, "telegram", "registry.db")}
101963
102202
  `);
101964
102203
  }
101965
- const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join68(STATE_DIR, "bridge-dead-escalation.json"));
102204
+ const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join69(STATE_DIR, "bridge-dead-escalation.json"));
101966
102205
  if (bridgeDeadMarker != null) {
101967
102206
  bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
101968
102207
  process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
@@ -101975,7 +102214,7 @@ if (isGatewayMain)
101975
102214
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
101976
102215
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
101977
102216
  if (pending2 != null && selfAgent) {
101978
- const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join68(STATE_DIR, "clean-shutdown.json");
102217
+ const bootResumeMarkerPath = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join69(STATE_DIR, "clean-shutdown.json");
101979
102218
  const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
101980
102219
  const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
101981
102220
  const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
@@ -102088,7 +102327,7 @@ if (isGatewayMain)
102088
102327
  `);
102089
102328
  }
102090
102329
  }
102091
- const pendingEnvPath = join68(agentDir, ".pending-turn.env");
102330
+ const pendingEnvPath = join69(agentDir, ".pending-turn.env");
102092
102331
  try {
102093
102332
  if (pending2 != null) {
102094
102333
  const lines = [
@@ -102216,7 +102455,7 @@ function checkApprovals() {
102216
102455
  return;
102217
102456
  }
102218
102457
  for (const senderId of files) {
102219
- const file = join68(APPROVED_DIR, senderId);
102458
+ const file = join69(APPROVED_DIR, senderId);
102220
102459
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync8(file, { force: true }), (err) => {
102221
102460
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
102222
102461
  `);
@@ -102302,6 +102541,7 @@ async function deliverAnswer(args) {
102302
102541
  const result = await runBackstopDelivery(backstopDeliveryLedger, turnId, chunks, args.resume ? null : args.cardMessageId, {
102303
102542
  sendChunk,
102304
102543
  readBack,
102544
+ onAckClaim: args.onAckClaim,
102305
102545
  recordOutbound: HISTORY_ENABLED ? (messageIds, texts) => {
102306
102546
  try {
102307
102547
  recordOutbound({
@@ -102390,7 +102630,7 @@ function noteAgentOutputAt(key, ts) {
102390
102630
  lastAgentOutputAt.delete(oldest);
102391
102631
  }
102392
102632
  }
102393
- var OBLIGATION_STORE_PATH = join68(STATE_DIR, "obligations.json");
102633
+ var OBLIGATION_STORE_PATH = join69(STATE_DIR, "obligations.json");
102394
102634
  var obligationStoreFs = {
102395
102635
  readFileSync: (p) => readFileSync60(p, "utf8"),
102396
102636
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -103167,7 +103407,7 @@ function emitTurnRecord(turn, endedAt) {
103167
103407
  maybeRotate(turnsPath, {
103168
103408
  statSize: (p) => {
103169
103409
  try {
103170
- return statSync22(p).size;
103410
+ return statSync23(p).size;
103171
103411
  } catch {
103172
103412
  return;
103173
103413
  }
@@ -104735,7 +104975,7 @@ var PIN_STATUS_WHILE_WORKING = (() => {
104735
104975
  })();
104736
104976
  var statusPinClaims = new Map;
104737
104977
  var statusPinRightsCache = new PinRightsCache2;
104738
- var STATUS_PIN_STORE_PATH = join68(STATE_DIR, "status-pins.json");
104978
+ var STATUS_PIN_STORE_PATH = join69(STATE_DIR, "status-pins.json");
104739
104979
  var statusPinStoreFs = {
104740
104980
  readFileSync: (p) => readFileSync60(p, "utf8"),
104741
104981
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -104743,7 +104983,7 @@ var statusPinStoreFs = {
104743
104983
  existsSync: (p) => existsSync57(p)
104744
104984
  };
104745
104985
  var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
104746
- var ACTIVITY_CARD_STORE_PATH = join68(STATE_DIR, "activity-cards-pending.json");
104986
+ var ACTIVITY_CARD_STORE_PATH = join69(STATE_DIR, "activity-cards-pending.json");
104747
104987
  var activityCardStoreFs = {
104748
104988
  readFileSync: (p) => readFileSync60(p, "utf8"),
104749
104989
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -104751,7 +104991,7 @@ var activityCardStoreFs = {
104751
104991
  existsSync: (p) => existsSync57(p)
104752
104992
  };
104753
104993
  var activityCardPersistEnabled = !STATIC;
104754
- var QUEUED_CARD_STORE_PATH = join68(STATE_DIR, "queued-cards-pending.json");
104994
+ var QUEUED_CARD_STORE_PATH = join69(STATE_DIR, "queued-cards-pending.json");
104755
104995
  var queuedCardStoreFs = {
104756
104996
  readFileSync: (p) => readFileSync60(p, "utf8"),
104757
104997
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -105043,7 +105283,7 @@ async function unpinAllStatusPins() {
105043
105283
  }
105044
105284
  }
105045
105285
  var stalePinSweepEligible = false;
105046
- var STALE_PIN_SWEEP_STORE_PATH = join68(STATE_DIR, "stale-pin-sweep.json");
105286
+ var STALE_PIN_SWEEP_STORE_PATH = join69(STATE_DIR, "stale-pin-sweep.json");
105047
105287
  var stalePinSweeper = createGatewayStalePinSweeper({
105048
105288
  telegram: { handle: () => lockedBot, call: robustApiCall },
105049
105289
  claims: () => statusPinClaims.values(),
@@ -105084,12 +105324,12 @@ var getPinnedProgressCardMessageId = null;
105084
105324
  var completeProgressCardTurn = null;
105085
105325
  var subagentWatcher = null;
105086
105326
  var workerActivityFeed = null;
105087
- var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join68(STATE_DIR, "gateway.sock");
105327
+ var SOCKET_PATH = process.env.SWITCHROOM_GATEWAY_SOCKET ?? join69(STATE_DIR, "gateway.sock");
105088
105328
  if (isGatewayMain)
105089
105329
  mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
105090
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join68(STATE_DIR, "gateway.pid.json");
105091
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join68(STATE_DIR, "gateway-session.json");
105092
- var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join68(STATE_DIR, "clean-shutdown.json");
105330
+ var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join69(STATE_DIR, "gateway.pid.json");
105331
+ var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join69(STATE_DIR, "gateway-session.json");
105332
+ var GATEWAY_CLEAN_SHUTDOWN_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_CLEAN_SHUTDOWN_MARKER ?? join69(STATE_DIR, "clean-shutdown.json");
105093
105333
  var GATEWAY_STARTED_AT_MS = Date.now();
105094
105334
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
105095
105335
  var activeBootCard = null;
@@ -105118,7 +105358,7 @@ function ensureIssuesCard(chatId, threadId) {
105118
105358
  bot: botApi,
105119
105359
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
105120
105360
  `),
105121
- persistPath: join68(stateDir, "issues-card.json")
105361
+ persistPath: join69(stateDir, "issues-card.json")
105122
105362
  });
105123
105363
  activeIssuesWatcher = startIssuesWatcher({
105124
105364
  stateDir,
@@ -105296,7 +105536,11 @@ function runDeliveryConfirmSweep() {
105296
105536
  }
105297
105537
  var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
105298
105538
  _deliveryConfirmSweep?.unref?.();
105299
- startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, resolveReplyMarkup: makeOutboxListenMarkupResolver({ resolveVoiceOutPlan: (t) => resolveVoiceOutPlan(loadAccess().voice_out, t), cachePut: (token, payload) => voiceOnDemandCache.put(token, payload), eagerVoiceEnabled, enqueuePreSynth: (j) => voicePreSynthQueue.enqueue(j) }), log: (l) => process.stderr.write(l) });
105539
+ startOutboxSweep({ isGatewayMain, stateDir: STATE_DIR, getBot: () => bot, getTurnsDb: () => turnsDb, dedupCheck: (c, t, x) => outboundDedup.check(c, t, x, Date.now()) != null, recordOutbound: HISTORY_ENABLED ? (chatId, threadId, message_ids, texts) => {
105540
+ try {
105541
+ recordOutbound({ chat_id: chatId, thread_id: threadId, message_ids, texts });
105542
+ } catch {}
105543
+ } : undefined, resolveReplyMarkup: makeOutboxListenMarkupResolver({ resolveVoiceOutPlan: (t) => resolveVoiceOutPlan(loadAccess().voice_out, t), cachePut: (token, payload) => voiceOnDemandCache.put(token, payload), eagerVoiceEnabled, enqueuePreSynth: (j) => voicePreSynthQueue.enqueue(j) }), log: (l) => process.stderr.write(l) });
105300
105544
  if (isGatewayMain)
105301
105545
  startTimer2({
105302
105546
  editMessage: async (ctx) => {
@@ -105318,14 +105562,14 @@ if (isGatewayMain)
105318
105562
  var inboundSpool;
105319
105563
  if (isGatewayMain)
105320
105564
  inboundSpool = STATIC ? undefined : createInboundSpool({
105321
- path: join68(STATE_DIR, "inbound-spool.jsonl"),
105565
+ path: join69(STATE_DIR, "inbound-spool.jsonl"),
105322
105566
  fs: {
105323
105567
  appendFileSync: (p, d) => appendFileSync9(p, d),
105324
105568
  readFileSync: (p) => readFileSync60(p, "utf8"),
105325
105569
  writeFileSync: (p, d) => writeFileSync50(p, d),
105326
105570
  renameSync: (a, b) => renameSync27(a, b),
105327
105571
  existsSync: (p) => existsSync57(p),
105328
- statSizeSync: (p) => statSync22(p).size,
105572
+ statSizeSync: (p) => statSync23(p).size,
105329
105573
  fsyncFileSync: fsyncPathSync,
105330
105574
  fsyncDirSync: fsyncPathSync
105331
105575
  },
@@ -105388,7 +105632,7 @@ async function maybeRedeliverUndeliveredAnswer() {
105388
105632
  let transcriptText;
105389
105633
  try {
105390
105634
  const projectsDir = getProjectsDirForCwd();
105391
- const path3 = join68(projectsDir, `${sessionId}.jsonl`);
105635
+ const path3 = join69(projectsDir, `${sessionId}.jsonl`);
105392
105636
  if (!existsSync57(path3)) {
105393
105637
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
105394
105638
  `);
@@ -105553,8 +105797,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
105553
105797
  isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
105554
105798
  isShuttingDown: () => shuttingDown,
105555
105799
  escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
105556
- crashLogPath: join68(STATE_DIR, "bridge-crash.log"),
105557
- markerPath: join68(STATE_DIR, "bridge-dead-escalation.json"),
105800
+ crashLogPath: join69(STATE_DIR, "bridge-crash.log"),
105801
+ markerPath: join69(STATE_DIR, "bridge-dead-escalation.json"),
105558
105802
  log: (line) => process.stderr.write(`${line}
105559
105803
  `),
105560
105804
  priorStreak: bridgeDeadPriorStreak,
@@ -105660,8 +105904,8 @@ if (isGatewayMain)
105660
105904
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
105661
105905
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
105662
105906
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
105663
- configSnapshotPath: join68(resolvedAgentDirForCard, ".config-snapshot.json"),
105664
- bootCardStatePath: join68(resolvedAgentDirForCard, ".boot-card-msgid.json"),
105907
+ configSnapshotPath: join69(resolvedAgentDirForCard, ".config-snapshot.json"),
105908
+ bootCardStatePath: join69(resolvedAgentDirForCard, ".boot-card-msgid.json"),
105665
105909
  floodStatePath: FLOOD_STATE_PATH,
105666
105910
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
105667
105911
  }, ackMsgId).then((handle) => {
@@ -106329,7 +106573,7 @@ if (isGatewayMain)
106329
106573
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
106330
106574
  if (Number.isInteger(receiverUid))
106331
106575
  allowedUids.push(receiverUid);
106332
- const socketPath = join68(STATE_DIR, "webhook.sock");
106576
+ const socketPath = join69(STATE_DIR, "webhook.sock");
106333
106577
  const webhookInject = (agentName3, inbound) => {
106334
106578
  const msg = inbound;
106335
106579
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -106557,9 +106801,9 @@ function redactOutboundText(text5, site) {
106557
106801
  var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
106558
106802
  var VOICE_OUT_HARD_CHUNK_CAP = 4096;
106559
106803
  var voiceOnDemandCache = new VoiceOnDemandCache({
106560
- persistPath: join68(STATE_DIR, "voice-ondemand.json")
106804
+ persistPath: join69(STATE_DIR, "voice-ondemand.json")
106561
106805
  });
106562
- var VOICE_CACHE_DIR = join68(STATE_DIR, "voice-cache");
106806
+ var VOICE_CACHE_DIR = join69(STATE_DIR, "voice-cache");
106563
106807
  var voicePreSynthQueue = new PreSynthQueue({
106564
106808
  runJob: async (job) => {
106565
106809
  const sidecarToken = await materializeSidecarToken2();
@@ -106961,7 +107205,7 @@ async function executeSendGif(rawArgs) {
106961
107205
  };
106962
107206
  }
106963
107207
  async function publishToTelegraph(text5, shortName, authorName) {
106964
- const accountPath = join68(STATE_DIR, "telegraph-account.json");
107208
+ const accountPath = join69(STATE_DIR, "telegraph-account.json");
106965
107209
  let account = null;
106966
107210
  try {
106967
107211
  if (existsSync57(accountPath)) {
@@ -108516,7 +108760,7 @@ function restartMarkerPath() {
108516
108760
  const agentDir = resolveAgentDirFromEnv();
108517
108761
  if (!agentDir)
108518
108762
  return null;
108519
- return join68(agentDir, "restart-pending.json");
108763
+ return join69(agentDir, "restart-pending.json");
108520
108764
  }
108521
108765
  function writeRestartMarker(marker) {
108522
108766
  const p = restartMarkerPath();
@@ -108708,7 +108952,7 @@ function _resetDockerReachableCache() {
108708
108952
  }
108709
108953
  function spawnSwitchroomDetached(args, onFailure) {
108710
108954
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
108711
- const logPath = join68(STATE_DIR, "detached-spawn.log");
108955
+ const logPath = join69(STATE_DIR, "detached-spawn.log");
108712
108956
  let outFd = null;
108713
108957
  try {
108714
108958
  mkdirSync51(STATE_DIR, { recursive: true });
@@ -109001,7 +109245,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
109001
109245
  }
109002
109246
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
109003
109247
  try {
109004
- const auditPath = join68(homedir19(), ".switchroom", "vault-audit.log");
109248
+ const auditPath = join69(homedir20(), ".switchroom", "vault-audit.log");
109005
109249
  if (!existsSync57(auditPath))
109006
109250
  return [];
109007
109251
  const raw = readFileSync60(auditPath, "utf8");
@@ -109055,7 +109299,7 @@ async function buildAgentMetadata(agentName3) {
109055
109299
  try {
109056
109300
  const agentDir = resolveAgentDirFromEnv();
109057
109301
  if (agentDir) {
109058
- const raw = readFileSync60(join68(agentDir, ".claude", ".claude.json"), "utf8");
109302
+ const raw = readFileSync60(join69(agentDir, ".claude", ".claude.json"), "utf8");
109059
109303
  claudeJson = JSON.parse(raw);
109060
109304
  }
109061
109305
  } catch {}
@@ -109184,7 +109428,7 @@ function buildModelDeps(restartCtx) {
109184
109428
  try {
109185
109429
  const agentDir = resolveAgentDirFromEnv();
109186
109430
  if (agentDir) {
109187
- const local = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
109431
+ const local = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
109188
109432
  if (local.ok)
109189
109433
  return formatQuotaLine2(local.data);
109190
109434
  }
@@ -109431,10 +109675,10 @@ function effortMenuReplyMarkup(reply) {
109431
109675
  function flushAgentHandoff(agentDir) {
109432
109676
  let removed = 0;
109433
109677
  for (const fname of [".handoff.md", ".handoff-topic"]) {
109434
- const p = join68(agentDir, fname);
109678
+ const p = join69(agentDir, fname);
109435
109679
  try {
109436
109680
  if (existsSync57(p)) {
109437
- unlinkSync30(p);
109681
+ unlinkSync31(p);
109438
109682
  removed++;
109439
109683
  }
109440
109684
  } catch (err) {
@@ -109489,7 +109733,7 @@ async function handleNewCommand(ctx) {
109489
109733
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
109490
109734
  if (agentDir != null) {
109491
109735
  try {
109492
- writeFileSync50(join68(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
109736
+ writeFileSync50(join69(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
109493
109737
  `, "utf8");
109494
109738
  } catch (err) {
109495
109739
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -109606,7 +109850,7 @@ var lockoutOps = {
109606
109850
  writeFileSync: (p, data, opts) => writeFileSync50(p, data, opts),
109607
109851
  existsSync: (p) => existsSync57(p),
109608
109852
  mkdirSync: (p, opts) => mkdirSync51(p, opts),
109609
- joinPath: (...parts) => join68(...parts)
109853
+ joinPath: (...parts) => join69(...parts)
109610
109854
  };
109611
109855
  var FLEET_FALLBACK_DEDUP_MS = 30000;
109612
109856
  function isAuthBrokerSocketReachable() {
@@ -109866,7 +110110,7 @@ async function runCreditWatch() {
109866
110110
  if (!agentDir)
109867
110111
  return;
109868
110112
  const agentName3 = getMyAgentName();
109869
- const claudeConfigDir = join68(agentDir, ".claude");
110113
+ const claudeConfigDir = join69(agentDir, ".claude");
109870
110114
  const stateDir = STATE_DIR;
109871
110115
  const reason = readClaudeJsonOverage(claudeConfigDir);
109872
110116
  const prev = loadCreditState(stateDir);
@@ -111622,7 +111866,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
111622
111866
  await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
111623
111867
  return;
111624
111868
  }
111625
- const result = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
111869
+ const result = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
111626
111870
  if (!result.ok) {
111627
111871
  await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
111628
111872
  return;
@@ -112878,7 +113122,7 @@ async function startGateway() {
112878
113122
  return;
112879
113123
  }
112880
113124
  })();
112881
- const resolvedAgentDirForBootCard = agentDir ?? join68(homedir19(), ".switchroom", "agents", agentSlug);
113125
+ const resolvedAgentDirForBootCard = agentDir ?? join69(homedir20(), ".switchroom", "agents", agentSlug);
112882
113126
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
112883
113127
  agentName: agentDisplayName,
112884
113128
  agentSlug,
@@ -112892,8 +113136,8 @@ async function startGateway() {
112892
113136
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
112893
113137
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
112894
113138
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
112895
- configSnapshotPath: join68(resolvedAgentDirForBootCard, ".config-snapshot.json"),
112896
- bootCardStatePath: join68(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
113139
+ configSnapshotPath: join69(resolvedAgentDirForBootCard, ".config-snapshot.json"),
113140
+ bootCardStatePath: join69(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
112897
113141
  floodStatePath: FLOOD_STATE_PATH,
112898
113142
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
112899
113143
  }, ackMsgId);
@@ -112926,7 +113170,7 @@ async function startGateway() {
112926
113170
  if (smAgentDir) {
112927
113171
  const resolutionTimeoutMs = resolveSessionModelResolutionTimeoutMs(process.env.SWITCHROOM_SESSION_MODEL_RESOLUTION_TIMEOUT_MS);
112928
113172
  const resolved = await waitForSessionModelResolution({
112929
- barrierExists: () => existsSync57(join68(smAgentDir, ".session-model-resolved")),
113173
+ barrierExists: () => existsSync57(join69(smAgentDir, ".session-model-resolved")),
112930
113174
  timeoutMs: resolutionTimeoutMs
112931
113175
  });
112932
113176
  if (!resolved) {
@@ -112934,7 +113178,7 @@ async function startGateway() {
112934
113178
  process.stderr.write(`telegram gateway: gw /model relaunch UNRESOLVED agent=${getMyAgentName()} target=${target} (barrier timeout after ${resolutionTimeoutMs}ms)
112935
113179
  `);
112936
113180
  } else {
112937
- const activePath = join68(smAgentDir, ".active-session-model");
113181
+ const activePath = join69(smAgentDir, ".active-session-model");
112938
113182
  if (existsSync57(activePath)) {
112939
113183
  try {
112940
113184
  const launched = readFileSync60(activePath, "utf8").trim();
@@ -112970,12 +113214,12 @@ async function startGateway() {
112970
113214
  deliverModelSwitchBootNotice({
112971
113215
  ...modelBootCardDeps,
112972
113216
  confirmation,
112973
- hasSessionModelAlert: existsSync57(join68(smAgentDir, ".session-model-alert"))
113217
+ hasSessionModelAlert: existsSync57(join69(smAgentDir, ".session-model-alert"))
112974
113218
  });
112975
113219
  }
112976
113220
  } catch {}
112977
113221
  }
112978
- const activeEffortPath = join68(smAgentDir, ".active-session-effort");
113222
+ const activeEffortPath = join69(smAgentDir, ".active-session-effort");
112979
113223
  if (existsSync57(activeEffortPath)) {
112980
113224
  try {
112981
113225
  const launchedEffort = readFileSync60(activeEffortPath, "utf8").trim();
@@ -112983,7 +113227,7 @@ async function startGateway() {
112983
113227
  sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
112984
113228
  } catch {}
112985
113229
  }
112986
- const alertPath = join68(smAgentDir, ".session-model-alert");
113230
+ const alertPath = join69(smAgentDir, ".session-model-alert");
112987
113231
  if (existsSync57(alertPath)) {
112988
113232
  let alertText = null;
112989
113233
  try {
@@ -112992,7 +113236,7 @@ async function startGateway() {
112992
113236
  alertText = null;
112993
113237
  }
112994
113238
  try {
112995
- unlinkSync30(alertPath);
113239
+ unlinkSync31(alertPath);
112996
113240
  } catch {}
112997
113241
  if (alertText && alertText.length > 0) {
112998
113242
  const operators = loadAccess().allowFrom;