switchroom 0.19.39 → 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 (31) hide show
  1. package/dist/agent-scheduler/index.js +10 -1
  2. package/dist/auth-broker/index.js +12 -3
  3. package/dist/cli/notion-write-pretool.mjs +10 -1
  4. package/dist/cli/switchroom.js +524 -189
  5. package/dist/host-control/main.js +217 -5
  6. package/dist/vault/approvals/kernel-server.js +12 -3
  7. package/dist/vault/broker/server.js +12 -3
  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 +314 -191
  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/liveness-wiring.ts +15 -0
  17. package/telegram-plugin/gateway/outbound-send-path.ts +20 -0
  18. package/telegram-plugin/gateway/outbox-sweep.ts +116 -18
  19. package/telegram-plugin/gateway/silence-poke-session-event.ts +13 -0
  20. package/telegram-plugin/gateway/stream-render.ts +39 -1
  21. package/telegram-plugin/hooks/compaction-marker-precompact.mjs +70 -0
  22. package/telegram-plugin/hooks/hooks.json +11 -0
  23. package/telegram-plugin/session-tail.ts +20 -0
  24. package/telegram-plugin/silence-poke.ts +28 -0
  25. package/telegram-plugin/tests/outbox-delivery.test.ts +38 -1
  26. package/telegram-plugin/tests/outbox-flush-ack-claim-race.test.ts +213 -0
  27. package/telegram-plugin/tests/outbox-reply-then-recap-e2e.test.ts +1 -1
  28. package/telegram-plugin/tests/outbox-sweep-flood-breaker.test.ts +4 -4
  29. package/telegram-plugin/tests/outbox-sweep-listen-button.test.ts +71 -8
  30. package/telegram-plugin/tests/send-reply-golden.test.ts +47 -0
  31. package/telegram-plugin/tests/silence-poke-compaction.test.ts +222 -0
@@ -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";
@@ -70372,6 +70381,8 @@ function tick(now) {
70372
70381
  const ceiling = thresholds.fallbackHardCeiling ?? Number.POSITIVE_INFINITY;
70373
70382
  const underCeiling = silence < ceiling;
70374
70383
  if (underCeiling) {
70384
+ if (activeDeps.isCompactionInFlight?.(key) === true)
70385
+ continue;
70375
70386
  const forceDisable = process.env.SWITCHROOM_SILENCE_DEFER_INFLIGHT_TOOLS === "0";
70376
70387
  if (!forceDisable && activeDeps.isLegitimatelyWorking != null) {
70377
70388
  if (activeDeps.isLegitimatelyWorking(key))
@@ -71596,6 +71607,14 @@ function projectTranscriptLine(line) {
71596
71607
  { kind: "turn_end", durationMs: obj.durationMs ?? 0 }
71597
71608
  ];
71598
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
+ }
71599
71618
  return [];
71600
71619
  }
71601
71620
 
@@ -77127,6 +77146,10 @@ function compactIfLarge(path2) {
77127
77146
  } catch {}
77128
77147
  }
77129
77148
 
77149
+ // gateway/outbox-sweep.ts
77150
+ init_rich_send();
77151
+ init_format();
77152
+
77130
77153
  // registry/subagents-schema.ts
77131
77154
  function countRunningBackgroundSubagents(db2) {
77132
77155
  const row = db2.prepare("SELECT count(*) AS n FROM subagents WHERE background = 1 AND status = 'running'").get();
@@ -78928,13 +78951,28 @@ async function deliverCapturedProse(deps, args) {
78928
78951
  const plainChunks = splitMarkdownChunks(plain, RICH_MESSAGE_MAX_CHARS);
78929
78952
  try {
78930
78953
  let liveThreadId = threadId;
78954
+ const plainSentIds = [];
78955
+ const plainSentTexts = [];
78931
78956
  for (const c of plainChunks) {
78932
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
+ }
78933
78963
  if (liveThreadId != null && sent.message_thread_id == null) {
78934
78964
  liveThreadId = undefined;
78935
78965
  }
78936
78966
  }
78937
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
+ }
78938
78976
  process.stderr.write(`telegram gateway: captured-prose recovered via plain-text fallback ` + `(chat=${chatId} origin=${originTurnId})
78939
78977
  `);
78940
78978
  } catch (plainErr) {
@@ -81016,6 +81054,7 @@ function handleSessionEvent(deps, ev) {
81016
81054
  let sentIds = [];
81017
81055
  let chunkCount = 0;
81018
81056
  let delivered = false;
81057
+ let earlyJournaled = false;
81019
81058
  try {
81020
81059
  const delivery = await deliverAnswer({
81021
81060
  chatId: backstopChatId,
@@ -81023,7 +81062,21 @@ function handleSessionEvent(deps, ev) {
81023
81062
  text: capturedText,
81024
81063
  turnId: turn.turnId,
81025
81064
  cardMessageId: backstopCardMessageId,
81026
- 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
+ }
81027
81080
  });
81028
81081
  sentIds = delivery.sentIds;
81029
81082
  chunkCount = delivery.chunkCount;
@@ -81069,7 +81122,7 @@ function handleSessionEvent(deps, ev) {
81069
81122
  chunkCount,
81070
81123
  cardMessageId: backstopCardMessageId
81071
81124
  });
81072
- if (delivered) {
81125
+ if (delivered && !earlyJournaled) {
81073
81126
  try {
81074
81127
  journalExternalDelivery({
81075
81128
  turnNonce: turn.turnId,
@@ -91852,6 +91905,7 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
91852
91905
  const stderr = deps.stderr ?? (() => {});
91853
91906
  const chunkCount = chunks.length;
91854
91907
  let attempts = 0;
91908
+ let ackClaimed = false;
91855
91909
  for (let attempt = 1;attempt <= Math.max(1, maxAttempts); attempt++) {
91856
91910
  attempts = attempt;
91857
91911
  const resume = ledger.unsentIndices(turnId, chunkCount);
@@ -91875,6 +91929,18 @@ async function runBackstopDelivery(ledger, turnId, chunks, cardMessageId, deps,
91875
91929
  `);
91876
91930
  }
91877
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
+ }
91878
91944
  let demotedAny = false;
91879
91945
  for (const i of ledger.landedUnconfirmedIndices(turnId, chunkCount)) {
91880
91946
  if (deps.readBack == null) {
@@ -93239,6 +93305,28 @@ function purgeStaleTurnsForChat(chatId, keys, purger, isStale = () => true) {
93239
93305
  return { purged };
93240
93306
  }
93241
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
+
93242
93330
  // gateway/hang-restart-decision.ts
93243
93331
  var HUMAN_WAIT_TOOLS2 = new Set(["ask_user"]);
93244
93332
  var BACKGROUND_TOOLS2 = new Set(["Task", "Agent"]);
@@ -93346,6 +93434,10 @@ function buildSilencePokeOptions(deps) {
93346
93434
  thresholdsMs: { fallback: SILENCE_FALLBACK_MS, fallbackHardCeiling: SILENCE_FALLBACK_HARD_MS, floor: SILENCE_FLOOR_MS },
93347
93435
  deferFallbackWhileToolInFlight: SILENCE_DEFER_INFLIGHT_TOOLS,
93348
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
+ },
93349
93441
  isTurnLive: (key) => !(activeTurnStartedAt.get(key) == null && getCurrentTurnForKey(key) == null),
93350
93442
  emitMetric: (event) => {
93351
93443
  emitRuntimeMetric2(event);
@@ -93769,6 +93861,9 @@ function applySilencePokeSessionEvent(silencePoke, pendingProgress, key, ev) {
93769
93861
  if (ev.toolUseId != null && ev.toolUseId.length > 0) {
93770
93862
  silencePoke.noteToolEnd(key, ev.toolUseId, Date.now());
93771
93863
  }
93864
+ } else if (ev.kind === "compact_boundary") {
93865
+ removeCompactionMarker(resolveTelegramStateDir());
93866
+ silencePoke.noteProduction(key, Date.now());
93772
93867
  }
93773
93868
  applyBackgroundShellLiveness(silencePoke, key, ev);
93774
93869
  }
@@ -94054,17 +94149,17 @@ import {
94054
94149
  readFileSync as readFileSync37,
94055
94150
  writeSync as writeSync6
94056
94151
  } from "node:fs";
94057
- import { join as join50 } from "node:path";
94152
+ import { join as join51 } from "node:path";
94058
94153
  import { randomUUID as randomUUID5 } from "node:crypto";
94059
94154
  var PROPOSALS_FILE2 = "skill-proposals.jsonl";
94060
94155
  var REJECTED_FILE2 = "skill-proposals-rejected.jsonl";
94061
94156
  var REJECTION_TTL_MS2 = 90 * 24 * 60 * 60 * 1000;
94062
94157
  var PROPOSAL_SIM_THRESHOLD = 0.5;
94063
94158
  function proposalsPath2(stateDir) {
94064
- return join50(stateDir, PROPOSALS_FILE2);
94159
+ return join51(stateDir, PROPOSALS_FILE2);
94065
94160
  }
94066
94161
  function rejectedPath2(stateDir) {
94067
- return join50(stateDir, REJECTED_FILE2);
94162
+ return join51(stateDir, REJECTED_FILE2);
94068
94163
  }
94069
94164
  function ensureDir3(stateDir) {
94070
94165
  if (!existsSync41(stateDir)) {
@@ -94682,7 +94777,7 @@ function escapeBody2(s) {
94682
94777
  }
94683
94778
 
94684
94779
  // gateway/pid-file.ts
94685
- 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";
94686
94781
  function writePidFile(path3, record) {
94687
94782
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
94688
94783
  writeFileSync33(tmp, JSON.stringify(record), "utf-8");
@@ -94690,7 +94785,7 @@ function writePidFile(path3, record) {
94690
94785
  }
94691
94786
  function clearPidFile(path3) {
94692
94787
  try {
94693
- unlinkSync21(path3);
94788
+ unlinkSync22(path3);
94694
94789
  } catch {}
94695
94790
  }
94696
94791
 
@@ -94907,7 +95002,7 @@ function safeCount(fn) {
94907
95002
  }
94908
95003
 
94909
95004
  // gateway/session-marker.ts
94910
- 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";
94911
95006
  function writeSessionMarker(path3, marker) {
94912
95007
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
94913
95008
  writeFileSync34(tmp, JSON.stringify(marker), "utf-8");
@@ -94937,7 +95032,7 @@ function shouldFireRestartBanner(input) {
94937
95032
  }
94938
95033
 
94939
95034
  // gateway/clean-shutdown-marker.ts
94940
- 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";
94941
95036
  var DEFAULT_MAX_AGE_MS = 60000;
94942
95037
  function writeCleanShutdownMarker(path3, marker) {
94943
95038
  const tmp = `${path3}.tmp-${process.pid}-${Date.now()}`;
@@ -94961,7 +95056,7 @@ function readCleanShutdownMarker(path3) {
94961
95056
  }
94962
95057
  function clearCleanShutdownMarker(path3) {
94963
95058
  try {
94964
- unlinkSync23(path3);
95059
+ unlinkSync24(path3);
94965
95060
  } catch {}
94966
95061
  }
94967
95062
  function shouldSuppressRecoveryBanner(marker, now, maxAgeMs = DEFAULT_MAX_AGE_MS) {
@@ -95321,13 +95416,13 @@ import {
95321
95416
  existsSync as existsSync42,
95322
95417
  openSync as openSync10,
95323
95418
  readSync as readSync2,
95324
- statSync as statSync15,
95419
+ statSync as statSync16,
95325
95420
  closeSync as closeSync10,
95326
95421
  watch,
95327
95422
  readdirSync as readdirSync10,
95328
95423
  readFileSync as readFileSync42
95329
95424
  } from "fs";
95330
- import { join as join51 } from "path";
95425
+ import { join as join52 } from "path";
95331
95426
 
95332
95427
  // session-tail.ts
95333
95428
  function sanitizeCwdToProjectName2(cwd) {
@@ -96134,7 +96229,7 @@ function startSubagentWatcher(config) {
96134
96229
  const fs3 = config.fs ?? {
96135
96230
  existsSync: existsSync42,
96136
96231
  readdirSync: readdirSync10,
96137
- statSync: statSync15,
96232
+ statSync: statSync16,
96138
96233
  openSync: openSync10,
96139
96234
  closeSync: closeSync10,
96140
96235
  readSync: readSync2,
@@ -96650,8 +96745,8 @@ function startSubagentWatcher(config) {
96650
96745
  if (stopped)
96651
96746
  return;
96652
96747
  pruneVanishedDirWatchers();
96653
- const claudeHome = join51(agentDir, ".claude");
96654
- const projectsRoot = join51(claudeHome, "projects");
96748
+ const claudeHome = join52(agentDir, ".claude");
96749
+ const projectsRoot = join52(claudeHome, "projects");
96655
96750
  if (!fs3.existsSync(projectsRoot))
96656
96751
  return;
96657
96752
  let projectDirs;
@@ -96685,7 +96780,7 @@ function startSubagentWatcher(config) {
96685
96780
  continue;
96686
96781
  }
96687
96782
  warnedForeignSlugs.delete(pDir);
96688
- const projectPath = join51(projectsRoot, pDir);
96783
+ const projectPath = join52(projectsRoot, pDir);
96689
96784
  let sessionDirs;
96690
96785
  try {
96691
96786
  sessionDirs = fs3.readdirSync(projectPath);
@@ -96695,7 +96790,7 @@ function startSubagentWatcher(config) {
96695
96790
  for (const sDir of sessionDirs) {
96696
96791
  if (sDir.endsWith(".jsonl"))
96697
96792
  continue;
96698
- const subagentsPath = join51(projectPath, sDir, "subagents");
96793
+ const subagentsPath = join52(projectPath, sDir, "subagents");
96699
96794
  if (!fs3.existsSync(subagentsPath))
96700
96795
  continue;
96701
96796
  const watchAndScan = (dirPath) => {
@@ -96704,7 +96799,7 @@ function startSubagentWatcher(config) {
96704
96799
  const w = fs3.watch(dirPath, (_event, filename) => {
96705
96800
  if (!filename || !filename.toString().startsWith("agent-") || !filename.toString().endsWith(".jsonl"))
96706
96801
  return;
96707
- const filePath = join51(dirPath, filename.toString());
96802
+ const filePath = join52(dirPath, filename.toString());
96708
96803
  if (!knownFiles.has(filePath)) {
96709
96804
  scanSubagentsDir(dirPath);
96710
96805
  }
@@ -96718,7 +96813,7 @@ function startSubagentWatcher(config) {
96718
96813
  scanSubagentsDir(dirPath);
96719
96814
  };
96720
96815
  watchAndScan(subagentsPath);
96721
- const workflowsPath = join51(subagentsPath, "workflows");
96816
+ const workflowsPath = join52(subagentsPath, "workflows");
96722
96817
  if (fs3.existsSync(workflowsPath)) {
96723
96818
  let wfDirs;
96724
96819
  try {
@@ -96728,7 +96823,7 @@ function startSubagentWatcher(config) {
96728
96823
  }
96729
96824
  for (const wfDir of wfDirs) {
96730
96825
  try {
96731
- const wfPath = join51(workflowsPath, wfDir);
96826
+ const wfPath = join52(workflowsPath, wfDir);
96732
96827
  if (!fs3.statSync(wfPath).isDirectory())
96733
96828
  continue;
96734
96829
  watchAndScan(wfPath);
@@ -96748,7 +96843,7 @@ function startSubagentWatcher(config) {
96748
96843
  for (const e of entries) {
96749
96844
  if (!e.startsWith("agent-") || !e.endsWith(".jsonl"))
96750
96845
  continue;
96751
- const filePath = join51(subagentsPath, e);
96846
+ const filePath = join52(subagentsPath, e);
96752
96847
  if (knownFiles.has(filePath))
96753
96848
  continue;
96754
96849
  const agentId = e.slice("agent-".length, -".jsonl".length);
@@ -96880,17 +96975,17 @@ import {
96880
96975
  writeFileSync as writeFileSync36,
96881
96976
  readFileSync as readFileSync43,
96882
96977
  readdirSync as readdirSync11,
96883
- unlinkSync as unlinkSync24,
96978
+ unlinkSync as unlinkSync25,
96884
96979
  existsSync as existsSync43,
96885
96980
  renameSync as renameSync21
96886
96981
  } from "node:fs";
96887
- import { join as join52, resolve as resolve10 } from "node:path";
96888
- 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";
96889
96984
  function registryDir() {
96890
- return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join52(homedir15(), ".switchroom", "worktrees"));
96985
+ return resolve10(process.env.SWITCHROOM_WORKTREE_DIR ?? join53(homedir16(), ".switchroom", "worktrees"));
96891
96986
  }
96892
96987
  function recordPath(id) {
96893
- return join52(registryDir(), `${id}.json`);
96988
+ return join53(registryDir(), `${id}.json`);
96894
96989
  }
96895
96990
  function ensureDir4() {
96896
96991
  mkdirSync38(registryDir(), { recursive: true });
@@ -97066,19 +97161,19 @@ function determineRestartReason(opts) {
97066
97161
 
97067
97162
  // gateway/update-announce.ts
97068
97163
  import { existsSync as existsSync48, mkdirSync as mkdirSync42, openSync as openSync11, closeSync as closeSync11, readFileSync as readFileSync50 } from "node:fs";
97069
- import { join as join57 } from "node:path";
97070
- import { homedir as homedir17 } from "node:os";
97164
+ import { join as join58 } from "node:path";
97165
+ import { homedir as homedir18 } from "node:os";
97071
97166
 
97072
97167
  // ../src/host-control/audit-reader.ts
97073
- import { homedir as homedir16 } from "node:os";
97074
- import { join as join56 } from "node:path";
97168
+ import { homedir as homedir17 } from "node:os";
97169
+ import { join as join57 } from "node:path";
97075
97170
 
97076
97171
  // ../src/host-control/audit-rotation-config.ts
97077
97172
  var DEFAULT_HOSTD_AUDIT_MAX_BYTES = 32 * 1024 * 1024;
97078
97173
 
97079
97174
  // ../src/host-control/audit-reader.ts
97080
- function defaultAuditLogPath(home2 = homedir16()) {
97081
- return join56(home2, ".switchroom", "host-control-audit.log");
97175
+ function defaultAuditLogPath(home2 = homedir17()) {
97176
+ return join57(home2, ".switchroom", "host-control-audit.log");
97082
97177
  }
97083
97178
  var AUDIT_READ_WINDOW_BYTES = 4 * 1024 * 1024;
97084
97179
  function parseAuditLine(line) {
@@ -97273,15 +97368,15 @@ function renderUpdateOutcomeLine(entry) {
97273
97368
  `);
97274
97369
  }
97275
97370
  function claimUpdateAnnouncement(requestId, opts = {}) {
97276
- const stateDir = opts.stateDir ?? process.env.TELEGRAM_STATE_DIR ?? join57(homedir17(), ".switchroom");
97277
- 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");
97278
97373
  try {
97279
97374
  mkdirSync42(dir, { recursive: true });
97280
97375
  } catch {
97281
97376
  return false;
97282
97377
  }
97283
97378
  const safeId = requestId.replace(/[^A-Za-z0-9_.-]/g, "_").slice(0, 200);
97284
- const path3 = join57(dir, safeId);
97379
+ const path3 = join58(dir, safeId);
97285
97380
  try {
97286
97381
  const fd = openSync11(path3, "wx");
97287
97382
  closeSync11(fd);
@@ -97502,8 +97597,8 @@ function createIssuesCardHandle(opts) {
97502
97597
  }
97503
97598
 
97504
97599
  // issues-watcher.ts
97505
- import { existsSync as existsSync50, statSync as statSync18 } from "node:fs";
97506
- 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";
97507
97602
 
97508
97603
  // ../src/issues/store.ts
97509
97604
  import {
@@ -97514,12 +97609,12 @@ import {
97514
97609
  readdirSync as readdirSync13,
97515
97610
  readFileSync as readFileSync52,
97516
97611
  renameSync as renameSync24,
97517
- statSync as statSync17,
97518
- unlinkSync as unlinkSync25,
97612
+ statSync as statSync18,
97613
+ unlinkSync as unlinkSync26,
97519
97614
  writeFileSync as writeFileSync42,
97520
97615
  writeSync as writeSync7
97521
97616
  } from "node:fs";
97522
- import { join as join58 } from "node:path";
97617
+ import { join as join59 } from "node:path";
97523
97618
  import { randomBytes as randomBytes11 } from "node:crypto";
97524
97619
  import { execSync } from "node:child_process";
97525
97620
 
@@ -97538,7 +97633,7 @@ init_redact();
97538
97633
  var ISSUES_FILE = "issues.jsonl";
97539
97634
  var ISSUES_LOCK = "issues.lock";
97540
97635
  function readAll(stateDir) {
97541
- const path3 = join58(stateDir, ISSUES_FILE);
97636
+ const path3 = join59(stateDir, ISSUES_FILE);
97542
97637
  if (!existsSync49(path3))
97543
97638
  return [];
97544
97639
  let raw;
@@ -97575,7 +97670,7 @@ function list2(stateDir, opts = {}) {
97575
97670
  });
97576
97671
  }
97577
97672
  function resolve11(stateDir, fingerprint, nowFn = Date.now) {
97578
- if (!existsSync49(join58(stateDir, ISSUES_FILE)))
97673
+ if (!existsSync49(join59(stateDir, ISSUES_FILE)))
97579
97674
  return 0;
97580
97675
  return withLock(stateDir, () => {
97581
97676
  const all2 = readAll(stateDir);
@@ -97593,7 +97688,7 @@ function resolve11(stateDir, fingerprint, nowFn = Date.now) {
97593
97688
  });
97594
97689
  }
97595
97690
  function writeAll(stateDir, events) {
97596
- const path3 = join58(stateDir, ISSUES_FILE);
97691
+ const path3 = join59(stateDir, ISSUES_FILE);
97597
97692
  sweepOrphanTmpFiles(stateDir);
97598
97693
  const tmp = `${path3}.tmp-${process.pid}-${randomBytes11(4).toString("hex")}`;
97599
97694
  const body = events.length === 0 ? "" : events.map((e) => JSON.stringify(e)).join(`
@@ -97615,11 +97710,11 @@ function sweepOrphanTmpFiles(stateDir) {
97615
97710
  for (const entry of entries) {
97616
97711
  if (!entry.startsWith(TMP_PREFIX))
97617
97712
  continue;
97618
- const tmpPath2 = join58(stateDir, entry);
97713
+ const tmpPath2 = join59(stateDir, entry);
97619
97714
  try {
97620
- const stat = statSync17(tmpPath2);
97715
+ const stat = statSync18(tmpPath2);
97621
97716
  if (stat.mtimeMs < cutoff) {
97622
- unlinkSync25(tmpPath2);
97717
+ unlinkSync26(tmpPath2);
97623
97718
  }
97624
97719
  } catch {}
97625
97720
  }
@@ -97627,7 +97722,7 @@ function sweepOrphanTmpFiles(stateDir) {
97627
97722
  var LOCK_RETRY_MS = 25;
97628
97723
  var LOCK_TIMEOUT_MS = 1e4;
97629
97724
  function withLock(stateDir, fn) {
97630
- const lockPath = join58(stateDir, ISSUES_LOCK);
97725
+ const lockPath = join59(stateDir, ISSUES_LOCK);
97631
97726
  const startedAt = Date.now();
97632
97727
  let fd = null;
97633
97728
  while (fd === null) {
@@ -97655,7 +97750,7 @@ function withLock(stateDir, fn) {
97655
97750
  closeSync12(fd);
97656
97751
  } catch {}
97657
97752
  try {
97658
- unlinkSync25(lockPath);
97753
+ unlinkSync26(lockPath);
97659
97754
  } catch {}
97660
97755
  }
97661
97756
  }
@@ -97669,13 +97764,13 @@ function tryStealStaleLock(lockPath) {
97669
97764
  const pid = Number(pidStr);
97670
97765
  if (!Number.isFinite(pid) || pid <= 0) {
97671
97766
  try {
97672
- unlinkSync25(lockPath);
97767
+ unlinkSync26(lockPath);
97673
97768
  } catch {}
97674
97769
  return true;
97675
97770
  }
97676
97771
  if (pid === process.pid) {
97677
97772
  try {
97678
- unlinkSync25(lockPath);
97773
+ unlinkSync26(lockPath);
97679
97774
  } catch {}
97680
97775
  return true;
97681
97776
  }
@@ -97690,7 +97785,7 @@ function tryStealStaleLock(lockPath) {
97690
97785
  return false;
97691
97786
  }
97692
97787
  try {
97693
- unlinkSync25(lockPath);
97788
+ unlinkSync26(lockPath);
97694
97789
  } catch {}
97695
97790
  return true;
97696
97791
  }
@@ -97712,7 +97807,7 @@ function isIssueEvent(v) {
97712
97807
  // issues-watcher.ts
97713
97808
  var DEFAULT_POLL_INTERVAL_MS2 = 2000;
97714
97809
  function startIssuesWatcher(opts) {
97715
- const path3 = join59(opts.stateDir, ISSUES_FILE);
97810
+ const path3 = join60(opts.stateDir, ISSUES_FILE);
97716
97811
  const log = opts.log ?? (() => {});
97717
97812
  const intervalMs = opts.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS2;
97718
97813
  const setIntervalFn = opts.setInterval ?? setInterval;
@@ -97763,7 +97858,7 @@ function defaultSignatureProvider(path3) {
97763
97858
  if (!existsSync50(path3))
97764
97859
  return null;
97765
97860
  try {
97766
- const stat = statSync18(path3);
97861
+ const stat = statSync19(path3);
97767
97862
  return `${stat.mtimeMs}:${stat.size}`;
97768
97863
  } catch {
97769
97864
  return null;
@@ -98472,7 +98567,7 @@ function readBashCommand(inputPreview) {
98472
98567
 
98473
98568
  // gateway/scoped-grant-store.ts
98474
98569
  init_atomic();
98475
- import { join as join60 } from "node:path";
98570
+ import { join as join61 } from "node:path";
98476
98571
 
98477
98572
  // scoped-approval.ts
98478
98573
  var SCOPED_APPROVAL_DEFAULT_TTL_MS2 = 30 * 60 * 1000;
@@ -98514,7 +98609,7 @@ function scopedGrantPersistEnabled(env = process.env) {
98514
98609
  return env.SWITCHROOM_SCOPED_GRANT_PERSIST !== "0";
98515
98610
  }
98516
98611
  function createScopedGrantStore(stateDir, env = process.env, log = (l) => process.stderr.write(l)) {
98517
- const filePath = join60(stateDir, "scoped-grants.json");
98612
+ const filePath = join61(stateDir, "scoped-grants.json");
98518
98613
  const enabled7 = scopedGrantPersistEnabled(env);
98519
98614
  let unreadable = false;
98520
98615
  function read() {
@@ -98893,7 +98988,7 @@ function isDiffPreApproved(agentName3, unifiedDiff, deps) {
98893
98988
  // credits-watch.ts
98894
98989
  init_card_format();
98895
98990
  import { readFileSync as readFileSync53, writeFileSync as writeFileSync43, existsSync as existsSync51, mkdirSync as mkdirSync44 } from "fs";
98896
- import { join as join61 } from "path";
98991
+ import { join as join62 } from "path";
98897
98992
  var STATE_FILE = "credits-watch.json";
98898
98993
  var DEFAULT_CREDIT_FATAL_REASONS = new Set;
98899
98994
  var KNOWN_CREDIT_REASONS = [
@@ -98915,7 +99010,7 @@ function emptyCreditState() {
98915
99010
  return { lastNotifiedReason: null, lastNotifiedAt: 0 };
98916
99011
  }
98917
99012
  function readClaudeJsonOverage(claudeConfigDir) {
98918
- const path3 = join61(claudeConfigDir, ".claude.json");
99013
+ const path3 = join62(claudeConfigDir, ".claude.json");
98919
99014
  if (!existsSync51(path3))
98920
99015
  return null;
98921
99016
  let raw;
@@ -98998,7 +99093,7 @@ function humanizeReason(reason) {
98998
99093
  }
98999
99094
  }
99000
99095
  function loadCreditState(stateDir) {
99001
- const path3 = join61(stateDir, STATE_FILE);
99096
+ const path3 = join62(stateDir, STATE_FILE);
99002
99097
  if (!existsSync51(path3))
99003
99098
  return emptyCreditState();
99004
99099
  try {
@@ -99015,7 +99110,7 @@ function loadCreditState(stateDir) {
99015
99110
  }
99016
99111
  function saveCreditState(stateDir, state7) {
99017
99112
  mkdirSync44(stateDir, { recursive: true });
99018
- const path3 = join61(stateDir, STATE_FILE);
99113
+ const path3 = join62(stateDir, STATE_FILE);
99019
99114
  writeFileSync43(path3, JSON.stringify(state7, null, 2) + `
99020
99115
  `, { mode: 384 });
99021
99116
  }
@@ -99024,7 +99119,7 @@ function saveCreditState(stateDir, state7) {
99024
99119
  init_auth_snapshot_format();
99025
99120
  init_card_format();
99026
99121
  import { readFileSync as readFileSync54, writeFileSync as writeFileSync44, existsSync as existsSync52, mkdirSync as mkdirSync45 } from "fs";
99027
- import { join as join62 } from "path";
99122
+ import { join as join63 } from "path";
99028
99123
  var STATE_FILE2 = "quota-watch.json";
99029
99124
  function emptyQuotaWatchState() {
99030
99125
  return {};
@@ -99262,7 +99357,7 @@ function buildRecoveryMessage(agentName3, snap) {
99262
99357
  `);
99263
99358
  }
99264
99359
  function loadQuotaWatchState(stateDir) {
99265
- const path3 = join62(stateDir, STATE_FILE2);
99360
+ const path3 = join63(stateDir, STATE_FILE2);
99266
99361
  if (!existsSync52(path3))
99267
99362
  return emptyQuotaWatchState();
99268
99363
  try {
@@ -99284,7 +99379,7 @@ function loadQuotaWatchState(stateDir) {
99284
99379
  }
99285
99380
  function saveQuotaWatchState(stateDir, state7) {
99286
99381
  mkdirSync45(stateDir, { recursive: true });
99287
- const path3 = join62(stateDir, STATE_FILE2);
99382
+ const path3 = join63(stateDir, STATE_FILE2);
99288
99383
  writeFileSync44(path3, JSON.stringify(state7, null, 2) + `
99289
99384
  `, { mode: 384 });
99290
99385
  }
@@ -99337,27 +99432,27 @@ import {
99337
99432
  mkdirSync as mkdirSync46,
99338
99433
  openSync as openSync13,
99339
99434
  readFileSync as readFileSync55,
99340
- statSync as statSync19,
99341
- unlinkSync as unlinkSync26,
99435
+ statSync as statSync20,
99436
+ unlinkSync as unlinkSync27,
99342
99437
  utimesSync as utimesSync2,
99343
99438
  writeFileSync as writeFileSync45
99344
99439
  } from "node:fs";
99345
- import { join as join63 } from "node:path";
99440
+ import { join as join64 } from "node:path";
99346
99441
  var TURN_ACTIVE_MARKER_FILE2 = "turn-active.json";
99347
99442
  var TURN_ACTIVE_HARD_TTL_MS2 = 10 * 60000;
99348
99443
  var TURN_ACTIVE_IDLE_SWEEP_MS = 60000;
99349
99444
  function removeTurnActiveMarker2(stateDir) {
99350
99445
  try {
99351
- unlinkSync26(join63(stateDir, TURN_ACTIVE_MARKER_FILE2));
99446
+ unlinkSync27(join64(stateDir, TURN_ACTIVE_MARKER_FILE2));
99352
99447
  } catch {}
99353
99448
  }
99354
99449
  function sweepStaleTurnActiveMarker(stateDir, opts) {
99355
- const path3 = join63(stateDir, TURN_ACTIVE_MARKER_FILE2);
99450
+ const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
99356
99451
  if (!existsSync53(path3))
99357
99452
  return false;
99358
99453
  const now = opts.now ?? Date.now();
99359
99454
  try {
99360
- const st = statSync19(path3);
99455
+ const st = statSync20(path3);
99361
99456
  const ageMs = now - st.mtimeMs;
99362
99457
  const hardExpired = ageMs > opts.hardTtlMs;
99363
99458
  const idleExpired = !opts.turnInFlight && ageMs > opts.idleSweepMs;
@@ -99367,7 +99462,7 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
99367
99462
  try {
99368
99463
  payload = readFileSync55(path3, "utf8");
99369
99464
  } catch {}
99370
- unlinkSync26(path3);
99465
+ unlinkSync27(path3);
99371
99466
  if (opts.onRemove) {
99372
99467
  try {
99373
99468
  opts.onRemove({
@@ -99383,9 +99478,9 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
99383
99478
  }
99384
99479
  }
99385
99480
  function readTurnActiveMarkerAgeMs2(stateDir, now) {
99386
- const path3 = join63(stateDir, TURN_ACTIVE_MARKER_FILE2);
99481
+ const path3 = join64(stateDir, TURN_ACTIVE_MARKER_FILE2);
99387
99482
  try {
99388
- const st = statSync19(path3);
99483
+ const st = statSync20(path3);
99389
99484
  return (now ?? Date.now()) - st.mtimeMs;
99390
99485
  } catch {
99391
99486
  return null;
@@ -99397,11 +99492,11 @@ function effectiveTurnAgeMs(markerAgeMs, turnStartedAt, now) {
99397
99492
 
99398
99493
  // gateway/gateway-heartbeat.ts
99399
99494
  import { mkdirSync as mkdirSync47, utimesSync as utimesSync3, writeFileSync as writeFileSync46 } from "node:fs";
99400
- import { join as join64 } from "node:path";
99495
+ import { join as join65 } from "node:path";
99401
99496
  var GATEWAY_HEARTBEAT_FILE = "gateway-heartbeat";
99402
99497
  var GATEWAY_HEARTBEAT_INTERVAL_MS = 15000;
99403
99498
  function touchGatewayHeartbeat(stateDir) {
99404
- const path3 = join64(stateDir, GATEWAY_HEARTBEAT_FILE);
99499
+ const path3 = join65(stateDir, GATEWAY_HEARTBEAT_FILE);
99405
99500
  const now = new Date;
99406
99501
  try {
99407
99502
  utimesSync3(path3, now, now);
@@ -99429,10 +99524,10 @@ import {
99429
99524
  openSync as openSync14,
99430
99525
  readFileSync as readFileSync56,
99431
99526
  renameSync as renameSync25,
99432
- unlinkSync as unlinkSync27,
99527
+ unlinkSync as unlinkSync28,
99433
99528
  writeFileSync as writeFileSync47
99434
99529
  } from "node:fs";
99435
- import { join as join65 } from "node:path";
99530
+ import { join as join66 } from "node:path";
99436
99531
  var BOOT_BEACON_FILE = "gateway-beacon.json";
99437
99532
  var HOST_BOOT_ID_PATH = "/proc/sys/kernel/random/boot_id";
99438
99533
  var CGROUP_MEMORY_EVENTS_PATH = "/sys/fs/cgroup/memory.events";
@@ -99531,7 +99626,7 @@ function sampleBeaconHost(paths = {}) {
99531
99626
  return sample3;
99532
99627
  }
99533
99628
  function writeBootBeaconFile(stateDir, beacon) {
99534
- const path3 = join65(stateDir, BOOT_BEACON_FILE);
99629
+ const path3 = join66(stateDir, BOOT_BEACON_FILE);
99535
99630
  const tmp = `${path3}.tmp-${process.pid}`;
99536
99631
  let fd;
99537
99632
  try {
@@ -99550,7 +99645,7 @@ function writeBootBeaconFile(stateDir, beacon) {
99550
99645
  } catch {}
99551
99646
  }
99552
99647
  try {
99553
- unlinkSync27(tmp);
99648
+ unlinkSync28(tmp);
99554
99649
  } catch {}
99555
99650
  return false;
99556
99651
  }
@@ -99578,6 +99673,8 @@ function attachBootBeacon(stateDir, next) {
99578
99673
  }
99579
99674
 
99580
99675
  // gateway/outbox-sweep.ts
99676
+ init_rich_send();
99677
+ init_format();
99581
99678
  init_flood_circuit_breaker();
99582
99679
  async function sweepOutbox(deps) {
99583
99680
  const now = deps.now?.() ?? Date.now();
@@ -99650,15 +99747,20 @@ async function sweepOutbox(deps) {
99650
99747
  }
99651
99748
  const resolvedChat = resolved;
99652
99749
  try {
99653
- 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);
99654
99751
  appendDelivered({
99655
99752
  turnNonce: record2.turnNonce,
99656
99753
  textSha256: record2.textSha256,
99657
- tgMessageId: messageId,
99754
+ tgMessageId: sendResult.messageId,
99658
99755
  ts: now,
99659
99756
  deliverySource: "sweep",
99660
99757
  replyAlreadyDeliveredThisTurn: record2.replyAlreadyDeliveredThisTurn === true
99661
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
+ }
99662
99764
  removeClaimed(record2.turnNonce, deps.stateDir);
99663
99765
  summary.delivered++;
99664
99766
  log(`outbox-sweep: delivered nonce=${record2.turnNonce} via=${resolvedChat.via} ` + `source=${record2.source} chars=${record2.text.length}${decision.action === "send-delayed" ? " (delayed)" : ""}
@@ -99713,27 +99815,42 @@ function createSweepBackoff(cfg = {}) {
99713
99815
  failures: () => failures
99714
99816
  };
99715
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
+ }
99716
99828
  function createOutboxSend(deps) {
99717
99829
  return async (chatId, threadId, text4) => {
99718
99830
  const bot = deps.getBot();
99719
99831
  if (bot == null)
99720
99832
  throw new Error("outbox-sweep: bot unavailable");
99721
99833
  if (text4.length === 0)
99722
- return;
99834
+ return { messageId: undefined, chunks: [] };
99723
99835
  const replyMarkup = deps.resolveReplyMarkup?.(chatId, threadId, text4);
99724
- let lastId;
99725
- const chunkCount = Math.ceil(text4.length / 4000);
99726
- for (let i = 0, idx = 0;i < text4.length; i += 4000, idx++) {
99727
- const chunk2 = text4.slice(i, i + 4000);
99728
- 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;
99729
99841
  const res = await retryWithThreadFallback(deps.retry, (tid) => {
99730
99842
  const base = tid != null ? { message_thread_id: tid } : {};
99731
99843
  const opts = isLast && replyMarkup != null ? { ...base, reply_markup: replyMarkup } : base;
99732
- return bot.api.sendMessage(chatId, chunk2, opts);
99844
+ return sendChunkRich(bot, chatId, chunk2, opts);
99733
99845
  }, { threadId: threadId ?? undefined, chat_id: chatId, verb: "outbox-sweep.sendMessage" });
99734
- lastId = res?.message_id;
99846
+ const id = res?.message_id;
99847
+ if (id != null)
99848
+ landed.push({ messageId: id, text: chunk2 });
99735
99849
  }
99736
- return lastId;
99850
+ return {
99851
+ messageId: landed.length > 0 ? landed[landed.length - 1].messageId : undefined,
99852
+ chunks: landed
99853
+ };
99737
99854
  };
99738
99855
  }
99739
99856
  function startOutboxSweep(deps) {
@@ -99764,6 +99881,7 @@ function startOutboxSweep(deps) {
99764
99881
  stateDir: deps.stateDir,
99765
99882
  log: deps.log,
99766
99883
  send,
99884
+ ...deps.recordOutbound != null ? { recordOutbound: deps.recordOutbound } : {},
99767
99885
  floodWaitRemainingMs: floodProbe,
99768
99886
  textAlreadyDelivered: (chatId, threadId, text4) => deps.dedupCheck(chatId, threadId ?? undefined, text4),
99769
99887
  registryChainLookup: (taskId) => {
@@ -99799,10 +99917,10 @@ function startOutboxSweep(deps) {
99799
99917
  }
99800
99918
 
99801
99919
  // ../src/build-info.ts
99802
- var VERSION2 = "0.19.39";
99803
- var COMMIT_SHA = "4aff2bdd";
99804
- var COMMIT_DATE = "2026-07-31T00:54:36Z";
99805
- var LATEST_PR = 4041;
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;
99806
99924
  var COMMITS_AHEAD_OF_TAG = 0;
99807
99925
 
99808
99926
  // gateway/boot-version.ts
@@ -99879,12 +99997,12 @@ init_protocol2();
99879
99997
  init_peercred();
99880
99998
  import * as net5 from "node:net";
99881
99999
  import * as fs3 from "node:fs";
99882
- import { homedir as homedir18 } from "node:os";
99883
- import { join as join66 } from "node:path";
100000
+ import { homedir as homedir19 } from "node:os";
100001
+ import { join as join67 } from "node:path";
99884
100002
  var DEFAULT_TIMEOUT_MS4 = 2000;
99885
100003
  var UNLOCK_TIMEOUT_MS = 30000;
99886
- var LEGACY_SOCKET_PATH2 = join66(homedir18(), ".switchroom", "vault-broker.sock");
99887
- 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");
99888
100006
  function defaultBrokerSocketPath2() {
99889
100007
  if (fs3.existsSync(OPERATOR_SOCKET_PATH2))
99890
100008
  return OPERATOR_SOCKET_PATH2;
@@ -100767,7 +100885,7 @@ function resolveVaultApprovalPosture(broker) {
100767
100885
 
100768
100886
  // registry/turns-schema.ts
100769
100887
  import { chmodSync as chmodSync12, mkdirSync as mkdirSync49 } from "fs";
100770
- import { join as join67 } from "path";
100888
+ import { join as join68 } from "path";
100771
100889
  var DatabaseClass3 = null;
100772
100890
  function loadDatabaseClass3() {
100773
100891
  if (DatabaseClass3 != null)
@@ -100839,9 +100957,9 @@ function applySchema(db3) {
100839
100957
  }
100840
100958
  function openTurnsDb(agentDir) {
100841
100959
  const Database = loadDatabaseClass3();
100842
- const dir = join67(agentDir, "telegram");
100960
+ const dir = join68(agentDir, "telegram");
100843
100961
  mkdirSync49(dir, { recursive: true, mode: 448 });
100844
- const path3 = join67(dir, "registry.db");
100962
+ const path3 = join68(dir, "registry.db");
100845
100963
  const db3 = new Database(path3, { create: true });
100846
100964
  applySchema(db3);
100847
100965
  try {
@@ -101186,7 +101304,7 @@ function selectResumeBuilder(endedVia, opts) {
101186
101304
  }
101187
101305
 
101188
101306
  // gateway/bridge-dead-watchdog.ts
101189
- 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";
101190
101308
 
101191
101309
  // gateway/cron-session.ts
101192
101310
  var CRON_IDENTITY_SUFFIX2 = "-cron";
@@ -101258,7 +101376,7 @@ function consumeBridgeDeadEscalationMarker(path3, nowMs3 = Date.now(), maxAgeMs
101258
101376
  }
101259
101377
  } catch {}
101260
101378
  try {
101261
- unlinkSync28(path3);
101379
+ unlinkSync29(path3);
101262
101380
  } catch {}
101263
101381
  return marker;
101264
101382
  }
@@ -101658,7 +101776,7 @@ if (isGatewayMain) {
101658
101776
  shutdownAnalytics();
101659
101777
  });
101660
101778
  }
101661
- 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");
101662
101780
  var permCardStore = createPermissionCardStore(STATE_DIR);
101663
101781
  var BLOCKED_APPROVALS_DIR = process.env.SWITCHROOM_BLOCKED_APPROVALS_DIR ?? "/state/blocked-approvals";
101664
101782
  var AGENT_NAME = process.env.SWITCHROOM_AGENT_NAME ?? "agent";
@@ -101758,11 +101876,11 @@ function scheduleAlwaysAllowPersistDrain() {
101758
101876
  }, ALWAYS_ALLOW_DRAIN_INTERVAL_MS);
101759
101877
  timer3.unref?.();
101760
101878
  }
101761
- var ACCESS_FILE = join68(STATE_DIR, "access.json");
101762
- var APPROVED_DIR = join68(STATE_DIR, "approved");
101763
- var ENV_FILE = join68(STATE_DIR, ".env");
101764
- var INBOX_DIR = join68(STATE_DIR, "inbox");
101765
- 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");
101766
101884
  function triggerSelfRestart(targetAgent, reason, delayMs = 300) {
101767
101885
  const isDocker = process.env.SWITCHROOM_RUNTIME === "docker";
101768
101886
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME;
@@ -101911,7 +102029,7 @@ function assertSendable(f) {
101911
102029
  } catch {
101912
102030
  throw new Error(`refusing to send file \u2014 cannot resolve real path: ${f}`);
101913
102031
  }
101914
- const inbox = join68(stateReal, "inbox");
102032
+ const inbox = join69(stateReal, "inbox");
101915
102033
  if (real.startsWith(stateReal + sep4) && !real.startsWith(inbox + sep4)) {
101916
102034
  throw new Error(`refusing to send channel state: ${f}`);
101917
102035
  }
@@ -102036,7 +102154,7 @@ var HISTORY_ENABLED = HISTORY_ACCESS.historyEnabled !== false;
102036
102154
  if (isGatewayMain && HISTORY_ENABLED) {
102037
102155
  try {
102038
102156
  initHistory(STATE_DIR, HISTORY_ACCESS.historyRetentionDays ?? 30);
102039
- 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")}
102040
102158
  `);
102041
102159
  } catch (err) {
102042
102160
  process.stderr.write(`telegram gateway: history init failed (${err.message}) \u2014 capture disabled
@@ -102055,9 +102173,9 @@ if (isGatewayMain)
102055
102173
  let markerTurnKey = null;
102056
102174
  let markerAgeMs = null;
102057
102175
  try {
102058
- const markerPath = join68(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
102176
+ const markerPath = join69(STATE_DIR, TURN_ACTIVE_MARKER_FILE2);
102059
102177
  if (existsSync57(markerPath)) {
102060
- const st = statSync22(markerPath);
102178
+ const st = statSync23(markerPath);
102061
102179
  markerAgeMs = Date.now() - st.mtimeMs;
102062
102180
  try {
102063
102181
  const payload = JSON.parse(readFileSync60(markerPath, "utf8"));
@@ -102080,10 +102198,10 @@ if (isGatewayMain)
102080
102198
  process.stderr.write(`telegram gateway: turn-registry boot-reaper stamped ${reaped} orphaned turn(s)` + `${timeoutTurnKey ? ` (turnKey=${timeoutTurnKey} as 'timeout', markerAgeMs=${markerAgeMs})` : " as 'restart'"}
102081
102199
  `);
102082
102200
  } else {
102083
- 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")}
102084
102202
  `);
102085
102203
  }
102086
- const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join68(STATE_DIR, "bridge-dead-escalation.json"));
102204
+ const bridgeDeadMarker = consumeBridgeDeadEscalationMarker(join69(STATE_DIR, "bridge-dead-escalation.json"));
102087
102205
  if (bridgeDeadMarker != null) {
102088
102206
  bridgeDeadPriorStreak = bridgeDeadMarker.count ?? 1;
102089
102207
  process.stderr.write(`telegram gateway: boot: prior restart was a bridge-dead escalation (reason=${bridgeDeadMarker.reason}` + `, consecutive=${bridgeDeadPriorStreak}` + `${bridgeDeadMarker.crashTail ? `, crashTail=${bridgeDeadMarker.crashTail}` : ""})
@@ -102096,7 +102214,7 @@ if (isGatewayMain)
102096
102214
  const pending2 = findLatestTurnIfInterrupted(turnsDb);
102097
102215
  const selfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
102098
102216
  if (pending2 != null && selfAgent) {
102099
- 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");
102100
102218
  const bootResumeCleanMarker = readCleanShutdownMarker(bootResumeMarkerPath);
102101
102219
  const bootResumeForceAlways = process.env.SWITCHROOM_BOOT_RESUME_ALWAYS === "1";
102102
102220
  const bootResumeMode = parseBootResumeMode(process.env.SWITCHROOM_BOOT_RESUME);
@@ -102209,7 +102327,7 @@ if (isGatewayMain)
102209
102327
  `);
102210
102328
  }
102211
102329
  }
102212
- const pendingEnvPath = join68(agentDir, ".pending-turn.env");
102330
+ const pendingEnvPath = join69(agentDir, ".pending-turn.env");
102213
102331
  try {
102214
102332
  if (pending2 != null) {
102215
102333
  const lines = [
@@ -102337,7 +102455,7 @@ function checkApprovals() {
102337
102455
  return;
102338
102456
  }
102339
102457
  for (const senderId of files) {
102340
- const file = join68(APPROVED_DIR, senderId);
102458
+ const file = join69(APPROVED_DIR, senderId);
102341
102459
  bot.api.sendMessage(senderId, "Paired! Say hi to Claude.").then(() => rmSync8(file, { force: true }), (err) => {
102342
102460
  process.stderr.write(`telegram gateway: failed to send approval confirm: ${err}
102343
102461
  `);
@@ -102423,6 +102541,7 @@ async function deliverAnswer(args) {
102423
102541
  const result = await runBackstopDelivery(backstopDeliveryLedger, turnId, chunks, args.resume ? null : args.cardMessageId, {
102424
102542
  sendChunk,
102425
102543
  readBack,
102544
+ onAckClaim: args.onAckClaim,
102426
102545
  recordOutbound: HISTORY_ENABLED ? (messageIds, texts) => {
102427
102546
  try {
102428
102547
  recordOutbound({
@@ -102511,7 +102630,7 @@ function noteAgentOutputAt(key, ts) {
102511
102630
  lastAgentOutputAt.delete(oldest);
102512
102631
  }
102513
102632
  }
102514
- var OBLIGATION_STORE_PATH = join68(STATE_DIR, "obligations.json");
102633
+ var OBLIGATION_STORE_PATH = join69(STATE_DIR, "obligations.json");
102515
102634
  var obligationStoreFs = {
102516
102635
  readFileSync: (p) => readFileSync60(p, "utf8"),
102517
102636
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -103288,7 +103407,7 @@ function emitTurnRecord(turn, endedAt) {
103288
103407
  maybeRotate(turnsPath, {
103289
103408
  statSize: (p) => {
103290
103409
  try {
103291
- return statSync22(p).size;
103410
+ return statSync23(p).size;
103292
103411
  } catch {
103293
103412
  return;
103294
103413
  }
@@ -104856,7 +104975,7 @@ var PIN_STATUS_WHILE_WORKING = (() => {
104856
104975
  })();
104857
104976
  var statusPinClaims = new Map;
104858
104977
  var statusPinRightsCache = new PinRightsCache2;
104859
- var STATUS_PIN_STORE_PATH = join68(STATE_DIR, "status-pins.json");
104978
+ var STATUS_PIN_STORE_PATH = join69(STATE_DIR, "status-pins.json");
104860
104979
  var statusPinStoreFs = {
104861
104980
  readFileSync: (p) => readFileSync60(p, "utf8"),
104862
104981
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -104864,7 +104983,7 @@ var statusPinStoreFs = {
104864
104983
  existsSync: (p) => existsSync57(p)
104865
104984
  };
104866
104985
  var statusPinPersistEnabled = !STATIC && PIN_STATUS_WHILE_WORKING;
104867
- 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");
104868
104987
  var activityCardStoreFs = {
104869
104988
  readFileSync: (p) => readFileSync60(p, "utf8"),
104870
104989
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -104872,7 +104991,7 @@ var activityCardStoreFs = {
104872
104991
  existsSync: (p) => existsSync57(p)
104873
104992
  };
104874
104993
  var activityCardPersistEnabled = !STATIC;
104875
- 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");
104876
104995
  var queuedCardStoreFs = {
104877
104996
  readFileSync: (p) => readFileSync60(p, "utf8"),
104878
104997
  writeFileSync: (p, d) => writeFileSync50(p, d),
@@ -105164,7 +105283,7 @@ async function unpinAllStatusPins() {
105164
105283
  }
105165
105284
  }
105166
105285
  var stalePinSweepEligible = false;
105167
- 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");
105168
105287
  var stalePinSweeper = createGatewayStalePinSweeper({
105169
105288
  telegram: { handle: () => lockedBot, call: robustApiCall },
105170
105289
  claims: () => statusPinClaims.values(),
@@ -105205,12 +105324,12 @@ var getPinnedProgressCardMessageId = null;
105205
105324
  var completeProgressCardTurn = null;
105206
105325
  var subagentWatcher = null;
105207
105326
  var workerActivityFeed = null;
105208
- 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");
105209
105328
  if (isGatewayMain)
105210
105329
  mkdirSync51(STATE_DIR, { recursive: true, mode: 448 });
105211
- var GATEWAY_PID_PATH = process.env.SWITCHROOM_GATEWAY_PID_FILE ?? join68(STATE_DIR, "gateway.pid.json");
105212
- var GATEWAY_SESSION_MARKER_PATH = process.env.SWITCHROOM_GATEWAY_SESSION_MARKER ?? join68(STATE_DIR, "gateway-session.json");
105213
- 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");
105214
105333
  var GATEWAY_STARTED_AT_MS = Date.now();
105215
105334
  var BOOT_CARD_ENABLED = process.env.SWITCHROOM_BOOT_CARD !== "false";
105216
105335
  var activeBootCard = null;
@@ -105239,7 +105358,7 @@ function ensureIssuesCard(chatId, threadId) {
105239
105358
  bot: botApi,
105240
105359
  log: (msg) => process.stderr.write(`telegram gateway: ${msg}
105241
105360
  `),
105242
- persistPath: join68(stateDir, "issues-card.json")
105361
+ persistPath: join69(stateDir, "issues-card.json")
105243
105362
  });
105244
105363
  activeIssuesWatcher = startIssuesWatcher({
105245
105364
  stateDir,
@@ -105417,7 +105536,11 @@ function runDeliveryConfirmSweep() {
105417
105536
  }
105418
105537
  var _deliveryConfirmSweep = isGatewayMain ? setInterval(runDeliveryConfirmSweep, DELIVERY_CONFIRM_SWEEP_MS) : undefined;
105419
105538
  _deliveryConfirmSweep?.unref?.();
105420
- 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) });
105421
105544
  if (isGatewayMain)
105422
105545
  startTimer2({
105423
105546
  editMessage: async (ctx) => {
@@ -105439,14 +105562,14 @@ if (isGatewayMain)
105439
105562
  var inboundSpool;
105440
105563
  if (isGatewayMain)
105441
105564
  inboundSpool = STATIC ? undefined : createInboundSpool({
105442
- path: join68(STATE_DIR, "inbound-spool.jsonl"),
105565
+ path: join69(STATE_DIR, "inbound-spool.jsonl"),
105443
105566
  fs: {
105444
105567
  appendFileSync: (p, d) => appendFileSync9(p, d),
105445
105568
  readFileSync: (p) => readFileSync60(p, "utf8"),
105446
105569
  writeFileSync: (p, d) => writeFileSync50(p, d),
105447
105570
  renameSync: (a, b) => renameSync27(a, b),
105448
105571
  existsSync: (p) => existsSync57(p),
105449
- statSizeSync: (p) => statSync22(p).size,
105572
+ statSizeSync: (p) => statSync23(p).size,
105450
105573
  fsyncFileSync: fsyncPathSync,
105451
105574
  fsyncDirSync: fsyncPathSync
105452
105575
  },
@@ -105509,7 +105632,7 @@ async function maybeRedeliverUndeliveredAnswer() {
105509
105632
  let transcriptText;
105510
105633
  try {
105511
105634
  const projectsDir = getProjectsDirForCwd();
105512
- const path3 = join68(projectsDir, `${sessionId}.jsonl`);
105635
+ const path3 = join69(projectsDir, `${sessionId}.jsonl`);
105513
105636
  if (!existsSync57(path3)) {
105514
105637
  process.stderr.write(`telegram gateway: crash-redelivery \u2014 transcript not found for turnKey=${turn.turn_key} session=${sessionId} (${path3}); skipping
105515
105638
  `);
@@ -105674,8 +105797,8 @@ var bridgeDeadWatchdog = createBridgeDeadWatchdog({
105674
105797
  isSessionAlive: () => findAgentProcessInContainer2()?.comm === "claude",
105675
105798
  isShuttingDown: () => shuttingDown,
105676
105799
  escalate: (reason) => triggerSelfRestart(process.env.SWITCHROOM_AGENT_NAME ?? "", reason, 1500),
105677
- crashLogPath: join68(STATE_DIR, "bridge-crash.log"),
105678
- 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"),
105679
105802
  log: (line) => process.stderr.write(`${line}
105680
105803
  `),
105681
105804
  priorStreak: bridgeDeadPriorStreak,
@@ -105781,8 +105904,8 @@ if (isGatewayMain)
105781
105904
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
105782
105905
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
105783
105906
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
105784
- configSnapshotPath: join68(resolvedAgentDirForCard, ".config-snapshot.json"),
105785
- bootCardStatePath: join68(resolvedAgentDirForCard, ".boot-card-msgid.json"),
105907
+ configSnapshotPath: join69(resolvedAgentDirForCard, ".config-snapshot.json"),
105908
+ bootCardStatePath: join69(resolvedAgentDirForCard, ".boot-card-msgid.json"),
105786
105909
  floodStatePath: FLOOD_STATE_PATH,
105787
105910
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
105788
105911
  }, ackMsgId).then((handle) => {
@@ -106450,7 +106573,7 @@ if (isGatewayMain)
106450
106573
  const receiverUid = receiverUidRaw ? Number(receiverUidRaw) : NaN;
106451
106574
  if (Number.isInteger(receiverUid))
106452
106575
  allowedUids.push(receiverUid);
106453
- const socketPath = join68(STATE_DIR, "webhook.sock");
106576
+ const socketPath = join69(STATE_DIR, "webhook.sock");
106454
106577
  const webhookInject = (agentName3, inbound) => {
106455
106578
  const msg = inbound;
106456
106579
  const delivered = ipcServer.sendToAgent(agentName3, msg);
@@ -106678,9 +106801,9 @@ function redactOutboundText(text5, site) {
106678
106801
  var VOICE_OUT_DEFAULT_CHUNK_CHARS = 600;
106679
106802
  var VOICE_OUT_HARD_CHUNK_CAP = 4096;
106680
106803
  var voiceOnDemandCache = new VoiceOnDemandCache({
106681
- persistPath: join68(STATE_DIR, "voice-ondemand.json")
106804
+ persistPath: join69(STATE_DIR, "voice-ondemand.json")
106682
106805
  });
106683
- var VOICE_CACHE_DIR = join68(STATE_DIR, "voice-cache");
106806
+ var VOICE_CACHE_DIR = join69(STATE_DIR, "voice-cache");
106684
106807
  var voicePreSynthQueue = new PreSynthQueue({
106685
106808
  runJob: async (job) => {
106686
106809
  const sidecarToken = await materializeSidecarToken2();
@@ -107082,7 +107205,7 @@ async function executeSendGif(rawArgs) {
107082
107205
  };
107083
107206
  }
107084
107207
  async function publishToTelegraph(text5, shortName, authorName) {
107085
- const accountPath = join68(STATE_DIR, "telegraph-account.json");
107208
+ const accountPath = join69(STATE_DIR, "telegraph-account.json");
107086
107209
  let account = null;
107087
107210
  try {
107088
107211
  if (existsSync57(accountPath)) {
@@ -108637,7 +108760,7 @@ function restartMarkerPath() {
108637
108760
  const agentDir = resolveAgentDirFromEnv();
108638
108761
  if (!agentDir)
108639
108762
  return null;
108640
- return join68(agentDir, "restart-pending.json");
108763
+ return join69(agentDir, "restart-pending.json");
108641
108764
  }
108642
108765
  function writeRestartMarker(marker) {
108643
108766
  const p = restartMarkerPath();
@@ -108829,7 +108952,7 @@ function _resetDockerReachableCache() {
108829
108952
  }
108830
108953
  function spawnSwitchroomDetached(args, onFailure) {
108831
108954
  const fullArgs = SWITCHROOM_CONFIG ? ["--config", SWITCHROOM_CONFIG, ...args] : args;
108832
- const logPath = join68(STATE_DIR, "detached-spawn.log");
108955
+ const logPath = join69(STATE_DIR, "detached-spawn.log");
108833
108956
  let outFd = null;
108834
108957
  try {
108835
108958
  mkdirSync51(STATE_DIR, { recursive: true });
@@ -109122,7 +109245,7 @@ ${preBlock(formatSwitchroomOutput(detail))}`, { html: true });
109122
109245
  }
109123
109246
  function readRecentDenialsForAgent(agentName3, windowMs, limit) {
109124
109247
  try {
109125
- const auditPath = join68(homedir19(), ".switchroom", "vault-audit.log");
109248
+ const auditPath = join69(homedir20(), ".switchroom", "vault-audit.log");
109126
109249
  if (!existsSync57(auditPath))
109127
109250
  return [];
109128
109251
  const raw = readFileSync60(auditPath, "utf8");
@@ -109176,7 +109299,7 @@ async function buildAgentMetadata(agentName3) {
109176
109299
  try {
109177
109300
  const agentDir = resolveAgentDirFromEnv();
109178
109301
  if (agentDir) {
109179
- const raw = readFileSync60(join68(agentDir, ".claude", ".claude.json"), "utf8");
109302
+ const raw = readFileSync60(join69(agentDir, ".claude", ".claude.json"), "utf8");
109180
109303
  claudeJson = JSON.parse(raw);
109181
109304
  }
109182
109305
  } catch {}
@@ -109305,7 +109428,7 @@ function buildModelDeps(restartCtx) {
109305
109428
  try {
109306
109429
  const agentDir = resolveAgentDirFromEnv();
109307
109430
  if (agentDir) {
109308
- const local = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
109431
+ const local = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
109309
109432
  if (local.ok)
109310
109433
  return formatQuotaLine2(local.data);
109311
109434
  }
@@ -109552,10 +109675,10 @@ function effortMenuReplyMarkup(reply) {
109552
109675
  function flushAgentHandoff(agentDir) {
109553
109676
  let removed = 0;
109554
109677
  for (const fname of [".handoff.md", ".handoff-topic"]) {
109555
- const p = join68(agentDir, fname);
109678
+ const p = join69(agentDir, fname);
109556
109679
  try {
109557
109680
  if (existsSync57(p)) {
109558
- unlinkSync30(p);
109681
+ unlinkSync31(p);
109559
109682
  removed++;
109560
109683
  }
109561
109684
  } catch (err) {
@@ -109610,7 +109733,7 @@ async function handleNewCommand(ctx) {
109610
109733
  writeRestartMarker({ chat_id: chatId, thread_id: threadId ?? null, ack_message_id: ackId, ts: Date.now() });
109611
109734
  if (agentDir != null) {
109612
109735
  try {
109613
- writeFileSync50(join68(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
109736
+ writeFileSync50(join69(agentDir, ".force-fresh-session"), `${kind} at ${new Date().toISOString()}
109614
109737
  `, "utf8");
109615
109738
  } catch (err) {
109616
109739
  process.stderr.write(`telegram gateway: failed to write force-fresh marker: ${err}
@@ -109727,7 +109850,7 @@ var lockoutOps = {
109727
109850
  writeFileSync: (p, data, opts) => writeFileSync50(p, data, opts),
109728
109851
  existsSync: (p) => existsSync57(p),
109729
109852
  mkdirSync: (p, opts) => mkdirSync51(p, opts),
109730
- joinPath: (...parts) => join68(...parts)
109853
+ joinPath: (...parts) => join69(...parts)
109731
109854
  };
109732
109855
  var FLEET_FALLBACK_DEDUP_MS = 30000;
109733
109856
  function isAuthBrokerSocketReachable() {
@@ -109987,7 +110110,7 @@ async function runCreditWatch() {
109987
110110
  if (!agentDir)
109988
110111
  return;
109989
110112
  const agentName3 = getMyAgentName();
109990
- const claudeConfigDir = join68(agentDir, ".claude");
110113
+ const claudeConfigDir = join69(agentDir, ".claude");
109991
110114
  const stateDir = STATE_DIR;
109992
110115
  const reason = readClaudeJsonOverage(claudeConfigDir);
109993
110116
  const prev = loadCreditState(stateDir);
@@ -111743,7 +111866,7 @@ Send \`/auth ${parsed.provider} cancel\` to abort.`, { html: true });
111743
111866
  await switchroomReply(ctx, "**/usage:** cannot resolve agent dir.", { html: true });
111744
111867
  return;
111745
111868
  }
111746
- const result = await fetchQuota2({ claudeConfigDir: join68(agentDir, ".claude") });
111869
+ const result = await fetchQuota2({ claudeConfigDir: join69(agentDir, ".claude") });
111747
111870
  if (!result.ok) {
111748
111871
  await switchroomReply(ctx, `**/usage:** ${escapeHtmlForTg2(result.reason)}`, { html: true });
111749
111872
  return;
@@ -112999,7 +113122,7 @@ async function startGateway() {
112999
113122
  return;
113000
113123
  }
113001
113124
  })();
113002
- const resolvedAgentDirForBootCard = agentDir ?? join68(homedir19(), ".switchroom", "agents", agentSlug);
113125
+ const resolvedAgentDirForBootCard = agentDir ?? join69(homedir20(), ".switchroom", "agents", agentSlug);
113003
113126
  const handle = await startBootCard(chatId, threadId, botApiForCard, {
113004
113127
  agentName: agentDisplayName,
113005
113128
  agentSlug,
@@ -113013,8 +113136,8 @@ async function startGateway() {
113013
113136
  probeQuotaViaBroker: (t) => probeQuotaForBootCard(agentSlug, t),
113014
113137
  tmuxSupervisor: process.env.SWITCHROOM_TMUX_SUPERVISOR === "1",
113015
113138
  dockerMode: process.env.SWITCHROOM_RUNTIME === "docker",
113016
- configSnapshotPath: join68(resolvedAgentDirForBootCard, ".config-snapshot.json"),
113017
- bootCardStatePath: join68(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
113139
+ configSnapshotPath: join69(resolvedAgentDirForBootCard, ".config-snapshot.json"),
113140
+ bootCardStatePath: join69(resolvedAgentDirForBootCard, ".boot-card-msgid.json"),
113018
113141
  floodStatePath: FLOOD_STATE_PATH,
113019
113142
  ...updateOutcomeLine ? { updateOutcomeLine } : {}
113020
113143
  }, ackMsgId);
@@ -113047,7 +113170,7 @@ async function startGateway() {
113047
113170
  if (smAgentDir) {
113048
113171
  const resolutionTimeoutMs = resolveSessionModelResolutionTimeoutMs(process.env.SWITCHROOM_SESSION_MODEL_RESOLUTION_TIMEOUT_MS);
113049
113172
  const resolved = await waitForSessionModelResolution({
113050
- barrierExists: () => existsSync57(join68(smAgentDir, ".session-model-resolved")),
113173
+ barrierExists: () => existsSync57(join69(smAgentDir, ".session-model-resolved")),
113051
113174
  timeoutMs: resolutionTimeoutMs
113052
113175
  });
113053
113176
  if (!resolved) {
@@ -113055,7 +113178,7 @@ async function startGateway() {
113055
113178
  process.stderr.write(`telegram gateway: gw /model relaunch UNRESOLVED agent=${getMyAgentName()} target=${target} (barrier timeout after ${resolutionTimeoutMs}ms)
113056
113179
  `);
113057
113180
  } else {
113058
- const activePath = join68(smAgentDir, ".active-session-model");
113181
+ const activePath = join69(smAgentDir, ".active-session-model");
113059
113182
  if (existsSync57(activePath)) {
113060
113183
  try {
113061
113184
  const launched = readFileSync60(activePath, "utf8").trim();
@@ -113091,12 +113214,12 @@ async function startGateway() {
113091
113214
  deliverModelSwitchBootNotice({
113092
113215
  ...modelBootCardDeps,
113093
113216
  confirmation,
113094
- hasSessionModelAlert: existsSync57(join68(smAgentDir, ".session-model-alert"))
113217
+ hasSessionModelAlert: existsSync57(join69(smAgentDir, ".session-model-alert"))
113095
113218
  });
113096
113219
  }
113097
113220
  } catch {}
113098
113221
  }
113099
- const activeEffortPath = join68(smAgentDir, ".active-session-effort");
113222
+ const activeEffortPath = join69(smAgentDir, ".active-session-effort");
113100
113223
  if (existsSync57(activeEffortPath)) {
113101
113224
  try {
113102
113225
  const launchedEffort = readFileSync60(activeEffortPath, "utf8").trim();
@@ -113104,7 +113227,7 @@ async function startGateway() {
113104
113227
  sessionEffortOverride = launchedEffort.length > 0 && launchedEffort !== configuredEffort ? launchedEffort : null;
113105
113228
  } catch {}
113106
113229
  }
113107
- const alertPath = join68(smAgentDir, ".session-model-alert");
113230
+ const alertPath = join69(smAgentDir, ".session-model-alert");
113108
113231
  if (existsSync57(alertPath)) {
113109
113232
  let alertText = null;
113110
113233
  try {
@@ -113113,7 +113236,7 @@ async function startGateway() {
113113
113236
  alertText = null;
113114
113237
  }
113115
113238
  try {
113116
- unlinkSync30(alertPath);
113239
+ unlinkSync31(alertPath);
113117
113240
  } catch {}
113118
113241
  if (alertText && alertText.length > 0) {
113119
113242
  const operators = loadAccess().allowFrom;