switchroom 0.19.44 → 0.19.45

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.
@@ -41869,7 +41869,8 @@ function normalizeForDedup(text) {
41869
41869
  }
41870
41870
 
41871
41871
  // flushed-turn-supersede.ts
41872
- var DEFAULT_SUPERSEDE_TTL_MS = 60000;
41872
+ var SUPERSEDE_OPEN_WINDOW_CAP_MS = 30 * 60000;
41873
+ var SUPERSEDE_COMPLETED_GRACE_MS = 60000;
41873
41874
  var SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS = 32;
41874
41875
  function normalizeForMatch(text) {
41875
41876
  return text.replace(/\s+/g, " ").trim();
@@ -41888,10 +41889,12 @@ function flushedAnswerMatchesReply(flushedText, replyText) {
41888
41889
  return false;
41889
41890
  }
41890
41891
  function decideSupersede(record, args) {
41891
- const ttlMs = args.ttlMs ?? DEFAULT_SUPERSEDE_TTL_MS;
41892
+ const openCapMs = args.openCapMs ?? SUPERSEDE_OPEN_WINDOW_CAP_MS;
41893
+ const completedGraceMs = args.completedGraceMs ?? SUPERSEDE_COMPLETED_GRACE_MS;
41892
41894
  if (record == null)
41893
41895
  return { supersede: false, deleteMessageIds: [], reason: "no-record" };
41894
- if (args.now - record.ts > ttlMs) {
41896
+ const expired = record.completedAt == null ? args.now - record.ts > openCapMs : args.now - record.completedAt > completedGraceMs;
41897
+ if (expired) {
41895
41898
  return { supersede: false, deleteMessageIds: [], reason: "expired" };
41896
41899
  }
41897
41900
  const sameTurn = record.turnId != null ? record.turnId === args.liveTurnId : args.liveTurnId == null;
@@ -41915,9 +41918,11 @@ function turnKey(turnId) {
41915
41918
 
41916
41919
  class FlushedTurnSupersedeRegistry {
41917
41920
  entries = new Map;
41918
- ttlMs;
41921
+ openCapMs;
41922
+ completedGraceMs;
41919
41923
  constructor(opts = {}) {
41920
- this.ttlMs = opts.ttlMs ?? DEFAULT_SUPERSEDE_TTL_MS;
41924
+ this.openCapMs = opts.openCapMs ?? SUPERSEDE_OPEN_WINDOW_CAP_MS;
41925
+ this.completedGraceMs = opts.completedGraceMs ?? SUPERSEDE_COMPLETED_GRACE_MS;
41921
41926
  }
41922
41927
  record(chatId, threadId, rec, now) {
41923
41928
  if (rec.messageIds.length === 0)
@@ -41933,9 +41938,18 @@ class FlushedTurnSupersedeRegistry {
41933
41938
  turnId: rec.turnId,
41934
41939
  messageIds: [...rec.messageIds],
41935
41940
  text: rec.text,
41936
- ts: now
41941
+ ts: now,
41942
+ completedAt: rec.completedAt ?? null
41937
41943
  });
41938
41944
  }
41945
+ completeAll(now) {
41946
+ for (const laneMap of this.entries.values()) {
41947
+ for (const rec of laneMap.values()) {
41948
+ if (rec.completedAt == null)
41949
+ rec.completedAt = now;
41950
+ }
41951
+ }
41952
+ }
41939
41953
  peek(chatId, threadId, args) {
41940
41954
  const rec = this.entries.get(makeKey2(chatId, threadId))?.get(turnKey(args.liveTurnId));
41941
41955
  return decideSupersede(rec, {
@@ -41943,7 +41957,8 @@ class FlushedTurnSupersedeRegistry {
41943
41957
  replyText: args.replyText,
41944
41958
  positiveAttribution: args.positiveAttribution,
41945
41959
  now: args.now,
41946
- ttlMs: this.ttlMs
41960
+ openCapMs: this.openCapMs,
41961
+ completedGraceMs: this.completedGraceMs
41947
41962
  });
41948
41963
  }
41949
41964
  take(chatId, threadId, args) {
@@ -41966,7 +41981,8 @@ class FlushedTurnSupersedeRegistry {
41966
41981
  sweep(now) {
41967
41982
  for (const [lane, laneMap] of this.entries) {
41968
41983
  for (const [tk, rec] of laneMap) {
41969
- if (now - rec.ts > this.ttlMs)
41984
+ const expired = rec.completedAt == null ? now - rec.ts > this.openCapMs : now - rec.completedAt > this.completedGraceMs;
41985
+ if (expired)
41970
41986
  laneMap.delete(tk);
41971
41987
  }
41972
41988
  if (laneMap.size === 0)
@@ -41985,6 +42001,302 @@ function makeKey2(chatId, threadId) {
41985
42001
  return threadId == null ? chatId : `${chatId}|${threadId}`;
41986
42002
  }
41987
42003
 
42004
+ // flushed-turn-supersede.ts
42005
+ var SUPERSEDE_OPEN_WINDOW_CAP_MS2 = 30 * 60000;
42006
+ var SUPERSEDE_COMPLETED_GRACE_MS2 = 60000;
42007
+ var SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 = 32;
42008
+ function normalizeForMatch2(text) {
42009
+ return text.replace(/\s+/g, " ").trim();
42010
+ }
42011
+ function flushedAnswerMatchesReply2(flushedText, replyText) {
42012
+ const flushed = normalizeForMatch2(flushedText);
42013
+ const reply = normalizeForMatch2(replyText);
42014
+ if (flushed.length === 0 || reply.length === 0)
42015
+ return false;
42016
+ if (flushed === reply)
42017
+ return true;
42018
+ if (reply.length >= SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 && flushed.includes(reply))
42019
+ return true;
42020
+ if (flushed.length >= SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 && reply.includes(flushed))
42021
+ return true;
42022
+ return false;
42023
+ }
42024
+ function decideSupersede2(record, args) {
42025
+ const openCapMs = args.openCapMs ?? SUPERSEDE_OPEN_WINDOW_CAP_MS2;
42026
+ const completedGraceMs = args.completedGraceMs ?? SUPERSEDE_COMPLETED_GRACE_MS2;
42027
+ if (record == null)
42028
+ return { supersede: false, deleteMessageIds: [], reason: "no-record" };
42029
+ const expired = record.completedAt == null ? args.now - record.ts > openCapMs : args.now - record.completedAt > completedGraceMs;
42030
+ if (expired) {
42031
+ return { supersede: false, deleteMessageIds: [], reason: "expired" };
42032
+ }
42033
+ const sameTurn = record.turnId != null ? record.turnId === args.liveTurnId : args.liveTurnId == null;
42034
+ if (!sameTurn) {
42035
+ return { supersede: false, deleteMessageIds: [], reason: "different-turn" };
42036
+ }
42037
+ if (!args.positiveAttribution && args.replyText != null && !flushedAnswerMatchesReply2(record.text, args.replyText)) {
42038
+ return { supersede: false, deleteMessageIds: [], reason: "new-content", recordText: record.text };
42039
+ }
42040
+ return {
42041
+ supersede: true,
42042
+ deleteMessageIds: [...record.messageIds],
42043
+ reason: "supersede",
42044
+ recordText: record.text
42045
+ };
42046
+ }
42047
+ function decideSupersedeCorrection(input) {
42048
+ const eligible = input.flushMessageIds.length === 1 && input.chunkCount === 1 && !input.hasFiles && !input.suppressText && !input.hasOpenPreview;
42049
+ if (eligible) {
42050
+ return { mode: "edit-in-place", editMessageId: input.flushMessageIds[0], deleteMessageIds: [] };
42051
+ }
42052
+ return { mode: "delete-resend", deleteMessageIds: [...input.flushMessageIds] };
42053
+ }
42054
+ var NULL_TURN_KEY2 = "<<null-turn>>";
42055
+ function turnKey2(turnId) {
42056
+ return turnId == null ? NULL_TURN_KEY2 : turnId;
42057
+ }
42058
+
42059
+ class FlushedTurnSupersedeRegistry2 {
42060
+ entries = new Map;
42061
+ openCapMs;
42062
+ completedGraceMs;
42063
+ constructor(opts = {}) {
42064
+ this.openCapMs = opts.openCapMs ?? SUPERSEDE_OPEN_WINDOW_CAP_MS2;
42065
+ this.completedGraceMs = opts.completedGraceMs ?? SUPERSEDE_COMPLETED_GRACE_MS2;
42066
+ }
42067
+ record(chatId, threadId, rec, now) {
42068
+ if (rec.messageIds.length === 0)
42069
+ return;
42070
+ this.sweep(now);
42071
+ const lane = makeKey3(chatId, threadId);
42072
+ let laneMap = this.entries.get(lane);
42073
+ if (laneMap == null) {
42074
+ laneMap = new Map;
42075
+ this.entries.set(lane, laneMap);
42076
+ }
42077
+ laneMap.set(turnKey2(rec.turnId), {
42078
+ turnId: rec.turnId,
42079
+ messageIds: [...rec.messageIds],
42080
+ text: rec.text,
42081
+ ts: now,
42082
+ completedAt: rec.completedAt ?? null
42083
+ });
42084
+ }
42085
+ completeAll(now) {
42086
+ for (const laneMap of this.entries.values()) {
42087
+ for (const rec of laneMap.values()) {
42088
+ if (rec.completedAt == null)
42089
+ rec.completedAt = now;
42090
+ }
42091
+ }
42092
+ }
42093
+ peek(chatId, threadId, args) {
42094
+ const rec = this.entries.get(makeKey3(chatId, threadId))?.get(turnKey2(args.liveTurnId));
42095
+ return decideSupersede2(rec, {
42096
+ liveTurnId: args.liveTurnId,
42097
+ replyText: args.replyText,
42098
+ positiveAttribution: args.positiveAttribution,
42099
+ now: args.now,
42100
+ openCapMs: this.openCapMs,
42101
+ completedGraceMs: this.completedGraceMs
42102
+ });
42103
+ }
42104
+ take(chatId, threadId, args) {
42105
+ const lane = makeKey3(chatId, threadId);
42106
+ const decision = this.peek(chatId, threadId, args);
42107
+ if (decision.supersede) {
42108
+ const laneMap = this.entries.get(lane);
42109
+ laneMap?.delete(turnKey2(args.liveTurnId));
42110
+ if (laneMap != null && laneMap.size === 0)
42111
+ this.entries.delete(lane);
42112
+ }
42113
+ return decision;
42114
+ }
42115
+ forget(chatId, threadId) {
42116
+ this.entries.delete(makeKey3(chatId, threadId));
42117
+ }
42118
+ clear() {
42119
+ this.entries.clear();
42120
+ }
42121
+ sweep(now) {
42122
+ for (const [lane, laneMap] of this.entries) {
42123
+ for (const [tk, rec] of laneMap) {
42124
+ const expired = rec.completedAt == null ? now - rec.ts > this.openCapMs : now - rec.completedAt > this.completedGraceMs;
42125
+ if (expired)
42126
+ laneMap.delete(tk);
42127
+ }
42128
+ if (laneMap.size === 0)
42129
+ this.entries.delete(lane);
42130
+ }
42131
+ }
42132
+ size(now) {
42133
+ this.sweep(now);
42134
+ let total = 0;
42135
+ for (const laneMap of this.entries.values())
42136
+ total += laneMap.size;
42137
+ return total;
42138
+ }
42139
+ }
42140
+ function makeKey3(chatId, threadId) {
42141
+ return threadId == null ? chatId : `${chatId}|${threadId}`;
42142
+ }
42143
+
42144
+ class FlushCompletionTracker {
42145
+ pending = [];
42146
+ open(turn) {
42147
+ turn.realEndObservedAt = null;
42148
+ if (!this.pending.includes(turn))
42149
+ this.pending.push(turn);
42150
+ }
42151
+ closeAll(now) {
42152
+ let closed = 0;
42153
+ for (const turn of this.pending) {
42154
+ if (turn.realEndObservedAt == null) {
42155
+ turn.realEndObservedAt = now;
42156
+ closed++;
42157
+ }
42158
+ }
42159
+ this.pending = [];
42160
+ return closed;
42161
+ }
42162
+ pendingCount() {
42163
+ return this.pending.length;
42164
+ }
42165
+ }
42166
+
42167
+ // reply-owner-resolve.ts
42168
+ function latestEndedAccepted(candidates) {
42169
+ if (candidates.latestEndedTurnId == null)
42170
+ return false;
42171
+ const age = candidates.latestEndedAgeMs;
42172
+ const ttl = candidates.latestEndedTtlMs;
42173
+ if (age == null || ttl == null)
42174
+ return false;
42175
+ const realEndAge = candidates.latestEndedRealEndAgeMs;
42176
+ if (realEndAge === undefined || realEndAge === null) {
42177
+ return age <= ttl;
42178
+ }
42179
+ const grace = candidates.latestEndedCompletedGraceMs ?? SUPERSEDE_COMPLETED_GRACE_MS2;
42180
+ return realEndAge <= grace;
42181
+ }
42182
+ function resolveReplyOwnerTier(candidates) {
42183
+ if (candidates.liveTurnId != null)
42184
+ return "live";
42185
+ if (candidates.originTurnId != null)
42186
+ return "origin";
42187
+ if (candidates.quotedTurnId != null)
42188
+ return "quoted";
42189
+ if (latestEndedAccepted(candidates))
42190
+ return "latest-ended";
42191
+ return "none";
42192
+ }
42193
+ function resolveReplyOwnerTurnId(candidates) {
42194
+ switch (resolveReplyOwnerTier(candidates)) {
42195
+ case "live":
42196
+ return candidates.liveTurnId;
42197
+ case "origin":
42198
+ return candidates.originTurnId;
42199
+ case "quoted":
42200
+ return candidates.quotedTurnId;
42201
+ case "latest-ended":
42202
+ return candidates.latestEndedTurnId;
42203
+ case "none":
42204
+ return null;
42205
+ }
42206
+ }
42207
+ function decideContentGateBypass(input) {
42208
+ if (input.tier === "live")
42209
+ return true;
42210
+ if (input.tier === "none")
42211
+ return false;
42212
+ if (input.handbackCouldOwnReply)
42213
+ return false;
42214
+ if (input.subagentCouldOwnReply !== false)
42215
+ return false;
42216
+ if (input.candidates == null)
42217
+ return false;
42218
+ if (!latestEndedAccepted(input.candidates))
42219
+ return false;
42220
+ return input.resolvedTurnId != null && input.resolvedTurnId === input.candidates.latestEndedTurnId;
42221
+ }
42222
+ function decideAnswerLatchSuppression(input) {
42223
+ if (input.superseded)
42224
+ return false;
42225
+ if (!input.replySubstantive)
42226
+ return false;
42227
+ if (!input.isLateReply)
42228
+ return false;
42229
+ if (input.replyMatchesFlushedAnswer === false)
42230
+ return false;
42231
+ return input.ownerAnswerDelivered === "flush";
42232
+ }
42233
+
42234
+ // gateway/reply-owner-wiring.ts
42235
+ var SUPERSEDE_OPEN_CAP_MS = (() => {
42236
+ const raw = Number(process.env.SWITCHROOM_SUPERSEDE_OPEN_CAP_MS);
42237
+ return Number.isFinite(raw) && raw > 0 ? raw : SUPERSEDE_OPEN_WINDOW_CAP_MS2;
42238
+ })();
42239
+ var SUPERSEDE_GRACE_MS = (() => {
42240
+ const raw = Number(process.env.SWITCHROOM_SUPERSEDE_GRACE_MS);
42241
+ return Number.isFinite(raw) && raw > 0 ? raw : SUPERSEDE_COMPLETED_GRACE_MS2;
42242
+ })();
42243
+ function resolveReplyOwnerTurnWith(lookups, liveTurn, chatId, args) {
42244
+ const origin = lookups.findTurnByOriginId(args.origin_turn_id);
42245
+ const quoted = lookups.findTurnByQuotedMessageId(chatId, args.reply_to);
42246
+ const latestEnded = lookups.findLatestTurnForChat(chatId, { endedOnly: true });
42247
+ const byId = new Map;
42248
+ for (const t of [latestEnded, quoted, origin, liveTurn]) {
42249
+ if (t != null)
42250
+ byId.set(t.turnId, t);
42251
+ }
42252
+ const latestEndedAgeMs = latestEnded?.endedAt != null ? lookups.now() - latestEnded.endedAt : null;
42253
+ const latestEndedRealEndAgeMs = latestEnded == null ? null : latestEnded.realEndObservedAt != null ? lookups.now() - latestEnded.realEndObservedAt : null;
42254
+ const candidates = {
42255
+ liveTurnId: liveTurn?.turnId ?? null,
42256
+ originTurnId: origin?.turnId ?? null,
42257
+ quotedTurnId: quoted?.turnId ?? null,
42258
+ latestEndedTurnId: latestEnded?.turnId ?? null,
42259
+ latestEndedAgeMs,
42260
+ latestEndedTtlMs: SUPERSEDE_OPEN_CAP_MS,
42261
+ latestEndedRealEndAgeMs,
42262
+ latestEndedCompletedGraceMs: SUPERSEDE_GRACE_MS
42263
+ };
42264
+ const tier = resolveReplyOwnerTier(candidates);
42265
+ const winnerId = resolveReplyOwnerTurnId(candidates);
42266
+ return { turn: winnerId != null ? byId.get(winnerId) ?? null : null, tier, candidates };
42267
+ }
42268
+
42269
+ // gateway/subagent-reply-authority.ts
42270
+ var SUB_AGENT_KIND_PREFIX = "sub_agent_";
42271
+
42272
+ class SubagentReplyAuthority {
42273
+ live = new Set;
42274
+ noteSessionEvent(ev) {
42275
+ if (ev == null)
42276
+ return;
42277
+ const kind = ev.kind;
42278
+ if (typeof kind !== "string" || !kind.startsWith(SUB_AGENT_KIND_PREFIX))
42279
+ return;
42280
+ const agentId = ev.agentId;
42281
+ if (typeof agentId !== "string" || agentId === "")
42282
+ return;
42283
+ if (kind === `${SUB_AGENT_KIND_PREFIX}turn_end`)
42284
+ this.live.delete(agentId);
42285
+ else
42286
+ this.live.add(agentId);
42287
+ }
42288
+ subagentCouldOwnReply() {
42289
+ return this.live.size > 0;
42290
+ }
42291
+ reset() {
42292
+ this.live.clear();
42293
+ }
42294
+ liveCount() {
42295
+ return this.live.size;
42296
+ }
42297
+ }
42298
+ var subagentReplyAuthority = new SubagentReplyAuthority;
42299
+
41988
42300
  // gateway/inbound-coalesce.ts
41989
42301
  function createInboundCoalescer(opts) {
41990
42302
  const buffer = new Map;
@@ -53954,11 +54266,11 @@ function createHandbackPreturnSignal(deps) {
53954
54266
  }
53955
54267
  return false;
53956
54268
  },
53957
- isPreTurnRecord(turnKey2) {
53958
- return turnKey2.startsWith(PRETURN_TURNKEY_PREFIX);
54269
+ isPreTurnRecord(turnKey3) {
54270
+ return turnKey3.startsWith(PRETURN_TURNKEY_PREFIX);
53959
54271
  },
53960
- handleReaped(turnKey2) {
53961
- const statusKey = bySyntheticKey.get(turnKey2);
54272
+ handleReaped(turnKey3) {
54273
+ const statusKey = bySyntheticKey.get(turnKey3);
53962
54274
  if (statusKey == null)
53963
54275
  return;
53964
54276
  const entry = byKey.get(statusKey);
@@ -71288,16 +71600,16 @@ function writeSilentEndState(args, deps) {
71288
71600
  `);
71289
71601
  }
71290
71602
  }
71291
- function clearSilentEndState(turnKey2, deps) {
71603
+ function clearSilentEndState(turnKey3, deps) {
71292
71604
  const statePath = resolveStatePath2(deps);
71293
71605
  if (!existsSync16(statePath))
71294
71606
  return;
71295
71607
  try {
71296
71608
  const prev = JSON.parse(readFileSync18(statePath, "utf8"));
71297
- if (prev.turnKey != null && prev.turnKey !== turnKey2)
71609
+ if (prev.turnKey != null && prev.turnKey !== turnKey3)
71298
71610
  return;
71299
71611
  unlinkSync12(statePath);
71300
- emitLog(deps, `silent-end: cleared state file turnKey=${turnKey2}
71612
+ emitLog(deps, `silent-end: cleared state file turnKey=${turnKey3}
71301
71613
  `);
71302
71614
  } catch {}
71303
71615
  }
@@ -75372,12 +75684,12 @@ class PendingUserNoticeGate {
75372
75684
  this.pending = this.pending.filter((p) => !(p.agent === notice.agent && p.key === notice.key));
75373
75685
  this.pending.push(notice);
75374
75686
  }
75375
- resolveTurnEnd(turnKey2, turnDeliveredReply, now = Date.now()) {
75687
+ resolveTurnEnd(turnKey3, turnDeliveredReply, now = Date.now()) {
75376
75688
  this.prune(now);
75377
75689
  const resolved = [];
75378
75690
  const remaining = [];
75379
75691
  for (const p of this.pending) {
75380
- if (p.key === turnKey2 || p.key === undefined)
75692
+ if (p.key === turnKey3 || p.key === undefined)
75381
75693
  resolved.push(p);
75382
75694
  else
75383
75695
  remaining.push(p);
@@ -78005,166 +78317,76 @@ ${input.newReplyText}`;
78005
78317
  };
78006
78318
  }
78007
78319
 
78008
- // flushed-turn-supersede.ts
78009
- var DEFAULT_SUPERSEDE_TTL_MS2 = 60000;
78010
- var SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 = 32;
78011
- function normalizeForMatch2(text4) {
78012
- return text4.replace(/\s+/g, " ").trim();
78013
- }
78014
- function flushedAnswerMatchesReply2(flushedText, replyText) {
78015
- const flushed = normalizeForMatch2(flushedText);
78016
- const reply = normalizeForMatch2(replyText);
78017
- if (flushed.length === 0 || reply.length === 0)
78320
+ // gateway/subagent-handback-marker.ts
78321
+ var INBOUND_SOURCE_CLASSIFICATION = {
78322
+ subagent_handback: { decoupledCompletion: true },
78323
+ cron: { decoupledCompletion: false },
78324
+ reaction: { decoupledCompletion: false },
78325
+ subagent_progress: { decoupledCompletion: false },
78326
+ resume_interrupted: { decoupledCompletion: false },
78327
+ resume_deferred: { decoupledCompletion: false },
78328
+ resume_watchdog_timeout: { decoupledCompletion: false },
78329
+ vault_grant_approved: { decoupledCompletion: false },
78330
+ vault_grant_denied: { decoupledCompletion: false },
78331
+ vault_grant_timeout: { decoupledCompletion: false },
78332
+ vault_save_completed: { decoupledCompletion: false },
78333
+ vault_save_discarded: { decoupledCompletion: false },
78334
+ vault_save_failed: { decoupledCompletion: false },
78335
+ vault_save_timeout: { decoupledCompletion: false },
78336
+ secret_provided: { decoupledCompletion: false },
78337
+ secret_declined: { decoupledCompletion: false },
78338
+ secret_provide_failed: { decoupledCompletion: false },
78339
+ secret_request_timeout: { decoupledCompletion: false },
78340
+ mental_model_propose_timeout: { decoupledCompletion: false },
78341
+ bridge_dead_restart: { decoupledCompletion: false },
78342
+ obligation_represent: { decoupledCompletion: false },
78343
+ missed_approval_retry: { decoupledCompletion: false },
78344
+ skill_proposal_apply: { decoupledCompletion: false },
78345
+ warmup: { decoupledCompletion: false },
78346
+ mental_model_proposal_applied: { decoupledCompletion: false },
78347
+ mental_model_proposal_denied: { decoupledCompletion: false },
78348
+ mental_model_proposal_failed: { decoupledCompletion: false },
78349
+ webhook: { decoupledCompletion: false },
78350
+ linear: { decoupledCompletion: false }
78351
+ };
78352
+ function stampsHandbackMarker(source) {
78353
+ if (source == null)
78018
78354
  return false;
78019
- if (flushed === reply)
78020
- return true;
78021
- if (reply.length >= SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 && flushed.includes(reply))
78022
- return true;
78023
- if (flushed.length >= SUPERSEDE_MATCH_MIN_CONTAINMENT_CHARS2 && reply.includes(flushed))
78355
+ const known = INBOUND_SOURCE_CLASSIFICATION[source];
78356
+ if (known == null)
78024
78357
  return true;
78025
- return false;
78026
- }
78027
- function decideSupersede2(record, args) {
78028
- const ttlMs = args.ttlMs ?? DEFAULT_SUPERSEDE_TTL_MS2;
78029
- if (record == null)
78030
- return { supersede: false, deleteMessageIds: [], reason: "no-record" };
78031
- if (args.now - record.ts > ttlMs) {
78032
- return { supersede: false, deleteMessageIds: [], reason: "expired" };
78033
- }
78034
- const sameTurn = record.turnId != null ? record.turnId === args.liveTurnId : args.liveTurnId == null;
78035
- if (!sameTurn) {
78036
- return { supersede: false, deleteMessageIds: [], reason: "different-turn" };
78037
- }
78038
- if (!args.positiveAttribution && args.replyText != null && !flushedAnswerMatchesReply2(record.text, args.replyText)) {
78039
- return { supersede: false, deleteMessageIds: [], reason: "new-content", recordText: record.text };
78040
- }
78041
- return {
78042
- supersede: true,
78043
- deleteMessageIds: [...record.messageIds],
78044
- reason: "supersede",
78045
- recordText: record.text
78046
- };
78047
- }
78048
- function decideSupersedeCorrection(input) {
78049
- const eligible = input.flushMessageIds.length === 1 && input.chunkCount === 1 && !input.hasFiles && !input.suppressText && !input.hasOpenPreview;
78050
- if (eligible) {
78051
- return { mode: "edit-in-place", editMessageId: input.flushMessageIds[0], deleteMessageIds: [] };
78052
- }
78053
- return { mode: "delete-resend", deleteMessageIds: [...input.flushMessageIds] };
78054
- }
78055
- var NULL_TURN_KEY2 = "<<null-turn>>";
78056
- function turnKey2(turnId) {
78057
- return turnId == null ? NULL_TURN_KEY2 : turnId;
78358
+ return known.decoupledCompletion;
78058
78359
  }
78360
+ var HANDBACK_RECENCY_WINDOW_MS = 60000;
78361
+ var MAIN_THREAD_KEY = "<main>";
78059
78362
 
78060
- class FlushedTurnSupersedeRegistry2 {
78061
- entries = new Map;
78062
- ttlMs;
78063
- constructor(opts = {}) {
78064
- this.ttlMs = opts.ttlMs ?? DEFAULT_SUPERSEDE_TTL_MS2;
78065
- }
78066
- record(chatId, threadId, rec, now) {
78067
- if (rec.messageIds.length === 0)
78068
- return;
78069
- this.sweep(now);
78070
- const lane = makeKey3(chatId, threadId);
78071
- let laneMap = this.entries.get(lane);
78072
- if (laneMap == null) {
78073
- laneMap = new Map;
78074
- this.entries.set(lane, laneMap);
78075
- }
78076
- laneMap.set(turnKey2(rec.turnId), {
78077
- turnId: rec.turnId,
78078
- messageIds: [...rec.messageIds],
78079
- text: rec.text,
78080
- ts: now
78081
- });
78082
- }
78083
- peek(chatId, threadId, args) {
78084
- const rec = this.entries.get(makeKey3(chatId, threadId))?.get(turnKey2(args.liveTurnId));
78085
- return decideSupersede2(rec, {
78086
- liveTurnId: args.liveTurnId,
78087
- replyText: args.replyText,
78088
- positiveAttribution: args.positiveAttribution,
78089
- now: args.now,
78090
- ttlMs: this.ttlMs
78091
- });
78363
+ class SubagentHandbackMarker {
78364
+ byChat = new Map;
78365
+ threadKey(threadId) {
78366
+ return threadId == null ? MAIN_THREAD_KEY : String(threadId);
78092
78367
  }
78093
- take(chatId, threadId, args) {
78094
- const lane = makeKey3(chatId, threadId);
78095
- const decision = this.peek(chatId, threadId, args);
78096
- if (decision.supersede) {
78097
- const laneMap = this.entries.get(lane);
78098
- laneMap?.delete(turnKey2(args.liveTurnId));
78099
- if (laneMap != null && laneMap.size === 0)
78100
- this.entries.delete(lane);
78368
+ record(chatId, threadId, now) {
78369
+ let inner = this.byChat.get(chatId);
78370
+ if (inner == null) {
78371
+ inner = new Map;
78372
+ this.byChat.set(chatId, inner);
78101
78373
  }
78102
- return decision;
78103
- }
78104
- forget(chatId, threadId) {
78105
- this.entries.delete(makeKey3(chatId, threadId));
78106
- }
78107
- clear() {
78108
- this.entries.clear();
78374
+ inner.set(this.threadKey(threadId), now);
78109
78375
  }
78110
- sweep(now) {
78111
- for (const [lane, laneMap] of this.entries) {
78112
- for (const [tk, rec] of laneMap) {
78113
- if (now - rec.ts > this.ttlMs)
78114
- laneMap.delete(tk);
78115
- }
78116
- if (laneMap.size === 0)
78117
- this.entries.delete(lane);
78118
- }
78376
+ lastAt(chatId, threadId) {
78377
+ return this.byChat.get(chatId)?.get(this.threadKey(threadId)) ?? null;
78119
78378
  }
78120
- size(now) {
78121
- this.sweep(now);
78122
- let total = 0;
78123
- for (const laneMap of this.entries.values())
78124
- total += laneMap.size;
78125
- return total;
78379
+ lastAtInChat(chatId) {
78380
+ const inner = this.byChat.get(chatId);
78381
+ if (inner == null || inner.size === 0)
78382
+ return null;
78383
+ let max = -Infinity;
78384
+ for (const ts of inner.values())
78385
+ if (ts > max)
78386
+ max = ts;
78387
+ return max === -Infinity ? null : max;
78126
78388
  }
78127
78389
  }
78128
- function makeKey3(chatId, threadId) {
78129
- return threadId == null ? chatId : `${chatId}|${threadId}`;
78130
- }
78131
-
78132
- // reply-owner-resolve.ts
78133
- function latestEndedAccepted(candidates) {
78134
- if (candidates.latestEndedTurnId == null)
78135
- return false;
78136
- const age = candidates.latestEndedAgeMs;
78137
- const ttl = candidates.latestEndedTtlMs;
78138
- if (age === null)
78139
- return false;
78140
- if (age === undefined || ttl == null)
78141
- return true;
78142
- return age <= ttl;
78143
- }
78144
- function decideContentGateBypass(input) {
78145
- if (input.tier === "live")
78146
- return true;
78147
- if (input.tier === "none")
78148
- return false;
78149
- if (input.handbackCouldOwnReply)
78150
- return false;
78151
- if (input.candidates == null)
78152
- return false;
78153
- if (!latestEndedAccepted(input.candidates))
78154
- return false;
78155
- return input.resolvedTurnId != null && input.resolvedTurnId === input.candidates.latestEndedTurnId;
78156
- }
78157
- function decideAnswerLatchSuppression(input) {
78158
- if (input.superseded)
78159
- return false;
78160
- if (!input.replySubstantive)
78161
- return false;
78162
- if (!input.isLateReply)
78163
- return false;
78164
- if (input.replyMatchesFlushedAnswer === false)
78165
- return false;
78166
- return input.ownerAnswerDelivered === "flush";
78167
- }
78168
78390
 
78169
78391
  // telegraph.ts
78170
78392
  function deriveTelegraphTitle2(text4) {
@@ -78739,6 +78961,7 @@ async function sendReply(deps, req) {
78739
78961
  resolveThreadId,
78740
78962
  getLatestInboundMessageId: getLatestInboundMessageId2,
78741
78963
  getLastSubagentHandbackAt,
78964
+ subagentReplyAuthority: subagentReplyAuthority2,
78742
78965
  recordOutbound: recordOutbound2,
78743
78966
  emissionAuthorityFor,
78744
78967
  clearActivitySummary,
@@ -78813,7 +79036,10 @@ async function sendReply(deps, req) {
78813
79036
  }
78814
79037
  }
78815
79038
  let supersedeFlushIds = [];
78816
- {
79039
+ if (req.callerIsForeignSession === true) {
79040
+ process.stderr.write(`telegram gateway: reply: foreign-session caller \u2014 supersede/latch skipped (#4172) ` + `chatId=${chat_id}
79041
+ `);
79042
+ } else {
78817
79043
  const replyThreadId = args.message_thread_id != null ? Number(args.message_thread_id) : undefined;
78818
79044
  const {
78819
79045
  turn: ownerTurn,
@@ -78825,12 +79051,14 @@ async function sendReply(deps, req) {
78825
79051
  const gateThreadId = ownerTurn?.sessionThreadId ?? replyThreadId;
78826
79052
  const handbackAt = getLastSubagentHandbackAt(chat_id);
78827
79053
  const now = Date.now();
78828
- const handbackCouldOwnReply = handbackAt != null && ownerEndedAt != null && handbackAt > ownerEndedAt && now - handbackAt <= DEFAULT_SUPERSEDE_TTL_MS2;
79054
+ const handbackCouldOwnReply = handbackAt != null && ownerEndedAt != null && handbackAt > ownerEndedAt && now - handbackAt <= HANDBACK_RECENCY_WINDOW_MS;
79055
+ const subagentCouldOwnReply = subagentReplyAuthority2.subagentCouldOwnReply();
78829
79056
  const replyIsOwnAnswer = decideContentGateBypass({
78830
79057
  tier: ownerTier,
78831
79058
  resolvedTurnId,
78832
79059
  candidates: ownerCandidates,
78833
- handbackCouldOwnReply
79060
+ handbackCouldOwnReply,
79061
+ subagentCouldOwnReply
78834
79062
  });
78835
79063
  const decision = flushedTurnSupersede.take(chat_id, gateThreadId, { liveTurnId: resolvedTurnId, replyText: text4, positiveAttribution: replyIsOwnAnswer, now });
78836
79064
  if (decision.supersede) {
@@ -78856,7 +79084,7 @@ async function sendReply(deps, req) {
78856
79084
  replyMatchesFlushedAnswer
78857
79085
  });
78858
79086
  if (decision.reason === "new-content") {
78859
- process.stderr.write(`telegram gateway: reply: flush supersede declined \u2014 new content (#3429) ` + `chatId=${chat_id} ownerTurnId=${JSON.stringify(resolvedTurnId)} ` + `tier=${ownerTier} latestEnded=${JSON.stringify(ownerCandidates.latestEndedTurnId)} ` + `handbackInWindow=${handbackCouldOwnReply}; sending fresh
79087
+ process.stderr.write(`telegram gateway: reply: flush supersede declined \u2014 new content (#3429) ` + `chatId=${chat_id} ownerTurnId=${JSON.stringify(resolvedTurnId)} ` + `tier=${ownerTier} latestEnded=${JSON.stringify(ownerCandidates.latestEndedTurnId)} ` + `handbackInWindow=${handbackCouldOwnReply} subagentLive=${subagentCouldOwnReply}; sending fresh
78860
79088
  `);
78861
79089
  }
78862
79090
  if (suppressByLatch) {
@@ -80110,6 +80338,37 @@ function emitRuntimeMetric2(event) {
80110
80338
  captureEvent(event.kind, { ...event, ts: wrapped.ts });
80111
80339
  }
80112
80340
 
80341
+ // gateway/subagent-reply-authority.ts
80342
+ var SUB_AGENT_KIND_PREFIX2 = "sub_agent_";
80343
+
80344
+ class SubagentReplyAuthority2 {
80345
+ live = new Set;
80346
+ noteSessionEvent(ev) {
80347
+ if (ev == null)
80348
+ return;
80349
+ const kind = ev.kind;
80350
+ if (typeof kind !== "string" || !kind.startsWith(SUB_AGENT_KIND_PREFIX2))
80351
+ return;
80352
+ const agentId = ev.agentId;
80353
+ if (typeof agentId !== "string" || agentId === "")
80354
+ return;
80355
+ if (kind === `${SUB_AGENT_KIND_PREFIX2}turn_end`)
80356
+ this.live.delete(agentId);
80357
+ else
80358
+ this.live.add(agentId);
80359
+ }
80360
+ subagentCouldOwnReply() {
80361
+ return this.live.size > 0;
80362
+ }
80363
+ reset() {
80364
+ this.live.clear();
80365
+ }
80366
+ liveCount() {
80367
+ return this.live.size;
80368
+ }
80369
+ }
80370
+ var subagentReplyAuthority2 = new SubagentReplyAuthority2;
80371
+
80113
80372
  // gateway/feed-open-gate.ts
80114
80373
  function shouldEarlyOpenLiveness(input) {
80115
80374
  if (!input.enabled)
@@ -80838,6 +81097,12 @@ function discardParkedTurnStart(rawContent) {
80838
81097
  }
80839
81098
  return null;
80840
81099
  }
81100
+ var flushCompletionTracker = new FlushCompletionTracker;
81101
+ function closeFlushCompletionWindows(registry, now) {
81102
+ const closed = flushCompletionTracker.closeAll(now);
81103
+ registry?.completeAll?.(now);
81104
+ return closed;
81105
+ }
80841
81106
  function drainParkedTurnStartsForChat(deps, chatId, threadId) {
80842
81107
  const wantThread = envThreadIdNum(threadId);
80843
81108
  const drained = [];
@@ -80970,6 +81235,7 @@ function beginTurn(deps, ev) {
80970
81235
  answerDelivered: false,
80971
81236
  flushedAnswerText: null,
80972
81237
  endedAt: null,
81238
+ realEndObservedAt: null,
80973
81239
  firstPingAt: null,
80974
81240
  firstPingWasSubstantive: false,
80975
81241
  silentAnchorMessageId: null,
@@ -81025,6 +81291,7 @@ function beginTurn(deps, ev) {
81025
81291
  `);
81026
81292
  }
81027
81293
  startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
81294
+ closeFlushCompletionWindows(deps.flushedTurnSupersede, Date.now());
81028
81295
  setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
81029
81296
  scheduleEarlyLivenessOpen(next);
81030
81297
  process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
@@ -81163,6 +81430,7 @@ function handleSessionEvent(deps, ev) {
81163
81430
  const durationMs = ev.kind === "turn_end" ? ev.durationMs : undefined;
81164
81431
  idleTracker.noteEvent(ev.kind, Date.now(), durationMs);
81165
81432
  }
81433
+ subagentReplyAuthority2.noteSessionEvent(ev);
81166
81434
  switch (ev.kind) {
81167
81435
  case "enqueue": {
81168
81436
  const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
@@ -81475,6 +81743,7 @@ function handleSessionEvent(deps, ev) {
81475
81743
  return;
81476
81744
  }
81477
81745
  }
81746
+ closeFlushCompletionWindows(flushedTurnSupersede, Date.now());
81478
81747
  const turnEndBackstopTurn = getCurrentTurn();
81479
81748
  const turnEndBackstopKey = turnEndBackstopTurn != null ? statusKey(turnEndBackstopTurn.sessionChatId, turnEndBackstopTurn.sessionThreadId) : null;
81480
81749
  withTurnEndGateBackstop(turnEndBackstopKey, turnEndBackstopTurn, () => {
@@ -81648,6 +81917,9 @@ function handleSessionEvent(deps, ev) {
81648
81917
  const backstopCardMessageId = cardTakeover.wasEmitted && cardTakeover.turnKey != null ? getPinnedProgressCardMessageId?.(cardTakeover.turnKey) ?? null : null;
81649
81918
  const backstopCardTurnKey = cardTakeover.turnKey;
81650
81919
  const backstopTurnEndedAt = endCurrentTurnAtomic(turn, { deferRecord: true, deferObligationClose: true });
81920
+ if (ev.durationMs === -1) {
81921
+ flushCompletionTracker.open(turn);
81922
+ }
81651
81923
  preambleSuppressor.dropNow();
81652
81924
  {
81653
81925
  const tKey = statusKey(chatId, threadId);
@@ -81725,7 +81997,12 @@ function handleSessionEvent(deps, ev) {
81725
81997
  turn.landedUnconfirmed = delivery.landedUnconfirmed;
81726
81998
  outboundDedup.record(backstopChatId, backstopThreadId, capturedText, Date.now(), getCurrentTurn()?.registryKey ?? null);
81727
81999
  if (sentIds.length > 0) {
81728
- flushedTurnSupersede.record(backstopChatId, backstopThreadId, { turnId: turn.turnId, messageIds: sentIds, text: capturedText }, Date.now());
82000
+ flushedTurnSupersede.record(backstopChatId, backstopThreadId, {
82001
+ turnId: turn.turnId,
82002
+ messageIds: sentIds,
82003
+ text: capturedText,
82004
+ completedAt: turn.realEndObservedAt
82005
+ }, Date.now());
81729
82006
  }
81730
82007
  if (!delivered) {
81731
82008
  if (backstopCtrl)
@@ -82698,51 +82975,13 @@ function isTurnFlushSafetyEnabled(env = process.env) {
82698
82975
  return true;
82699
82976
  }
82700
82977
 
82701
- // reply-owner-resolve.ts
82702
- function latestEndedAccepted2(candidates) {
82703
- if (candidates.latestEndedTurnId == null)
82704
- return false;
82705
- const age = candidates.latestEndedAgeMs;
82706
- const ttl = candidates.latestEndedTtlMs;
82707
- if (age === null)
82708
- return false;
82709
- if (age === undefined || ttl == null)
82710
- return true;
82711
- return age <= ttl;
82712
- }
82713
- function resolveReplyOwnerTier(candidates) {
82714
- if (candidates.liveTurnId != null)
82715
- return "live";
82716
- if (candidates.originTurnId != null)
82717
- return "origin";
82718
- if (candidates.quotedTurnId != null)
82719
- return "quoted";
82720
- if (latestEndedAccepted2(candidates))
82721
- return "latest-ended";
82722
- return "none";
82723
- }
82724
- function resolveReplyOwnerTurnId(candidates) {
82725
- switch (resolveReplyOwnerTier(candidates)) {
82726
- case "live":
82727
- return candidates.liveTurnId;
82728
- case "origin":
82729
- return candidates.originTurnId;
82730
- case "quoted":
82731
- return candidates.quotedTurnId;
82732
- case "latest-ended":
82733
- return candidates.latestEndedTurnId;
82734
- case "none":
82735
- return null;
82736
- }
82737
- }
82738
-
82739
82978
  // gateway/subagent-handback-marker.ts
82740
- var MAIN_THREAD_KEY = "<main>";
82979
+ var MAIN_THREAD_KEY2 = "<main>";
82741
82980
 
82742
- class SubagentHandbackMarker {
82981
+ class SubagentHandbackMarker2 {
82743
82982
  byChat = new Map;
82744
82983
  threadKey(threadId) {
82745
- return threadId == null ? MAIN_THREAD_KEY : String(threadId);
82984
+ return threadId == null ? MAIN_THREAD_KEY2 : String(threadId);
82746
82985
  }
82747
82986
  record(chatId, threadId, now) {
82748
82987
  let inner = this.byChat.get(chatId);
@@ -91182,76 +91421,6 @@ function buildDiffPreviewCard(input) {
91182
91421
  return { text: text4, reply_markup: kb };
91183
91422
  }
91184
91423
 
91185
- // gateway/subagent-handback-marker.ts
91186
- var INBOUND_SOURCE_CLASSIFICATION = {
91187
- subagent_handback: { decoupledCompletion: true },
91188
- cron: { decoupledCompletion: false },
91189
- reaction: { decoupledCompletion: false },
91190
- subagent_progress: { decoupledCompletion: false },
91191
- resume_interrupted: { decoupledCompletion: false },
91192
- resume_deferred: { decoupledCompletion: false },
91193
- resume_watchdog_timeout: { decoupledCompletion: false },
91194
- vault_grant_approved: { decoupledCompletion: false },
91195
- vault_grant_denied: { decoupledCompletion: false },
91196
- vault_grant_timeout: { decoupledCompletion: false },
91197
- vault_save_completed: { decoupledCompletion: false },
91198
- vault_save_discarded: { decoupledCompletion: false },
91199
- vault_save_failed: { decoupledCompletion: false },
91200
- vault_save_timeout: { decoupledCompletion: false },
91201
- secret_provided: { decoupledCompletion: false },
91202
- secret_declined: { decoupledCompletion: false },
91203
- secret_provide_failed: { decoupledCompletion: false },
91204
- secret_request_timeout: { decoupledCompletion: false },
91205
- mental_model_propose_timeout: { decoupledCompletion: false },
91206
- bridge_dead_restart: { decoupledCompletion: false },
91207
- obligation_represent: { decoupledCompletion: false },
91208
- missed_approval_retry: { decoupledCompletion: false },
91209
- skill_proposal_apply: { decoupledCompletion: false },
91210
- warmup: { decoupledCompletion: false },
91211
- mental_model_proposal_applied: { decoupledCompletion: false },
91212
- mental_model_proposal_denied: { decoupledCompletion: false },
91213
- mental_model_proposal_failed: { decoupledCompletion: false },
91214
- webhook: { decoupledCompletion: false },
91215
- linear: { decoupledCompletion: false }
91216
- };
91217
- function stampsHandbackMarker(source) {
91218
- if (source == null)
91219
- return false;
91220
- const known = INBOUND_SOURCE_CLASSIFICATION[source];
91221
- if (known == null)
91222
- return true;
91223
- return known.decoupledCompletion;
91224
- }
91225
- var MAIN_THREAD_KEY2 = "<main>";
91226
-
91227
- class SubagentHandbackMarker2 {
91228
- byChat = new Map;
91229
- threadKey(threadId) {
91230
- return threadId == null ? MAIN_THREAD_KEY2 : String(threadId);
91231
- }
91232
- record(chatId, threadId, now) {
91233
- let inner = this.byChat.get(chatId);
91234
- if (inner == null) {
91235
- inner = new Map;
91236
- this.byChat.set(chatId, inner);
91237
- }
91238
- inner.set(this.threadKey(threadId), now);
91239
- }
91240
- lastAt(chatId, threadId) {
91241
- return this.byChat.get(chatId)?.get(this.threadKey(threadId)) ?? null;
91242
- }
91243
- lastAtInChat(chatId) {
91244
- const inner = this.byChat.get(chatId);
91245
- if (inner == null || inner.size === 0)
91246
- return null;
91247
- let max = -Infinity;
91248
- for (const ts of inner.values())
91249
- if (ts > max)
91250
- max = ts;
91251
- return max === -Infinity ? null : max;
91252
- }
91253
- }
91254
-
91255
91424
  // gateway/pending-inbound-buffer.ts
91256
91425
  var DEFAULT_PENDING_INBOUND_CAP = 32;
91257
91426
  function redeliverBufferedInbound(buffer, agent, send, spool, onDelivered) {
@@ -91405,6 +91574,11 @@ function cronIdentity(agent) {
91405
91574
  function isCronIdentity(name) {
91406
91575
  return typeof name === "string" && name.endsWith(CRON_IDENTITY_SUFFIX);
91407
91576
  }
91577
+ function replyCallerIsForeignSession(name) {
91578
+ if (name == null || name === "")
91579
+ return true;
91580
+ return isCronIdentity(name);
91581
+ }
91408
91582
  function isCronInjectFire(meta) {
91409
91583
  return meta?.source === "cron" || meta?.session === "cron";
91410
91584
  }
@@ -93204,6 +93378,7 @@ function createTurnEndFunnel(deps) {
93204
93378
  endCurrentTurnForKey(turn, key);
93205
93379
  const turnEndedAt = Date.now();
93206
93380
  turn.endedAt = turnEndedAt;
93381
+ turn.realEndObservedAt = turnEndedAt;
93207
93382
  process.stderr.write(`telegram gateway: ${formatTurnLifecycle("clear", "turn_end", turn, turnEndedAt)}
93208
93383
  `);
93209
93384
  if (opts?.deferRecord !== true) {
@@ -100866,10 +101041,10 @@ function startOutboxSweep(deps) {
100866
101041
  }
100867
101042
 
100868
101043
  // ../src/build-info.ts
100869
- var VERSION2 = "0.19.44";
100870
- var COMMIT_SHA = "82d3b0b2";
100871
- var COMMIT_DATE = "2026-08-01T23:02:10Z";
100872
- var LATEST_PR = 4186;
101044
+ var VERSION2 = "0.19.45";
101045
+ var COMMIT_SHA = "2ad33d5d";
101046
+ var COMMIT_DATE = "2026-08-01T23:47:32Z";
101047
+ var LATEST_PR = 4192;
100873
101048
  var COMMITS_AHEAD_OF_TAG = 0;
100874
101049
 
100875
101050
  // gateway/boot-version.ts
@@ -103412,7 +103587,10 @@ var deferredDoneReactions = new DeferredDoneReactions({
103412
103587
  purge: (key) => purgeReactionTracking(key)
103413
103588
  });
103414
103589
  var outboundDedup = new OutboundDedupCache;
103415
- var flushedTurnSupersede = new FlushedTurnSupersedeRegistry;
103590
+ var flushedTurnSupersede = new FlushedTurnSupersedeRegistry({
103591
+ openCapMs: SUPERSEDE_OPEN_CAP_MS,
103592
+ completedGraceMs: SUPERSEDE_GRACE_MS
103593
+ });
103416
103594
  var backstopDeliveryLedger = new BackstopDeliveryLedger2;
103417
103595
  var BACKSTOP_DELIVERY_MAX_ATTEMPTS = (() => {
103418
103596
  const raw = Number(process.env.SWITCHROOM_BACKSTOP_DELIVERY_MAX_ATTEMPTS);
@@ -103776,7 +103954,7 @@ function cardDrainGate(turn, ea, run4) {
103776
103954
  var RECENT_TURNS_MAX = 32;
103777
103955
  var recentTurnsById = new Map;
103778
103956
  var recentTurnIdBySourceMessageId = new Map;
103779
- var subagentHandbackMarker = new SubagentHandbackMarker;
103957
+ var subagentHandbackMarker = new SubagentHandbackMarker2;
103780
103958
  var getLastSubagentHandbackAt = (chatId) => subagentHandbackMarker.lastAtInChat(chatId);
103781
103959
  function rememberRecentTurn(turn) {
103782
103960
  recentTurnsById.set(turn.turnId, turn);
@@ -103830,26 +104008,7 @@ function findLatestTurnForChat(chatId, opts) {
103830
104008
  return latestTurnForChat(recentTurnsById.values(), chatId, opts);
103831
104009
  }
103832
104010
  function resolveReplyOwnerTurn(liveTurn, chatId, args) {
103833
- const origin = findTurnByOriginId(args.origin_turn_id);
103834
- const quoted = findTurnByQuotedMessageId(chatId, args.reply_to);
103835
- const latestEnded = findLatestTurnForChat(chatId, { endedOnly: true });
103836
- const byId = new Map;
103837
- for (const t of [latestEnded, quoted, origin, liveTurn]) {
103838
- if (t != null)
103839
- byId.set(t.turnId, t);
103840
- }
103841
- const latestEndedAgeMs = latestEnded?.endedAt != null ? Date.now() - latestEnded.endedAt : null;
103842
- const candidates = {
103843
- liveTurnId: liveTurn?.turnId ?? null,
103844
- originTurnId: origin?.turnId ?? null,
103845
- quotedTurnId: quoted?.turnId ?? null,
103846
- latestEndedTurnId: latestEnded?.turnId ?? null,
103847
- latestEndedAgeMs,
103848
- latestEndedTtlMs: DEFAULT_SUPERSEDE_TTL_MS
103849
- };
103850
- const tier = resolveReplyOwnerTier(candidates);
103851
- const winnerId = resolveReplyOwnerTurnId(candidates);
103852
- return { turn: winnerId != null ? byId.get(winnerId) ?? null : null, tier, candidates };
104011
+ return resolveReplyOwnerTurnWith({ findTurnByOriginId, findTurnByQuotedMessageId, findLatestTurnForChat, now: Date.now }, liveTurn, chatId, args);
103853
104012
  }
103854
104013
  function resolveAnswerThreadWithLog(chatId, explicitThreadId, originTurn, originVia, liveTurn, surface) {
103855
104014
  const recovered = LATE_REPLY_TOPIC_RECOVERY_ENABLED && explicitThreadId == null && originTurn == null && liveTurn == null ? findLatestTurnForChat(chatId, { endedOnly: false }) : null;
@@ -106905,12 +107064,20 @@ if (isGatewayMain)
106905
107064
  log: (msg) => process.stderr.write(`${msg}
106906
107065
  `)
106907
107066
  });
107067
+ if (client3.agentName != null && !isCronIdentity(client3.agentName)) {
107068
+ subagentReplyAuthority.reset();
107069
+ const closedWindows = closeFlushCompletionWindows(flushedTurnSupersede, Date.now());
107070
+ if (closedWindows > 0) {
107071
+ process.stderr.write(`telegram gateway: bridge disconnect closed ${closedWindows} open flush-completion window(s) (#4173)
107072
+ `);
107073
+ }
107074
+ }
106908
107075
  },
106909
107076
  async onToolCall(client3, msg) {
106910
107077
  process.stderr.write(`telegram gateway: ipc: tool_call tool=${msg.tool} agent=${client3.agentName ?? "-"} clientId=${client3.id ?? "-"} callId=${msg.id}
106911
107078
  `);
106912
107079
  try {
106913
- const result = await executeToolCall(msg.tool, msg.args);
107080
+ const result = await executeToolCall(msg.tool, msg.args, client3.agentName);
106914
107081
  return { type: "tool_call_result", id: msg.id, success: true, result };
106915
107082
  } catch (err) {
106916
107083
  return {
@@ -107588,13 +107755,13 @@ var ALLOWED_TOOLS = new Set([
107588
107755
  "linear_create_issue",
107589
107756
  "linear_agent_setup"
107590
107757
  ]);
107591
- async function executeToolCall(tool, args) {
107758
+ async function executeToolCall(tool, args, callerAgentName = null) {
107592
107759
  if (!ALLOWED_TOOLS.has(tool)) {
107593
107760
  throw new Error(`tool not allowed: ${tool}`);
107594
107761
  }
107595
107762
  switch (tool) {
107596
107763
  case "reply":
107597
- return executeReply(args);
107764
+ return executeReply(args, callerAgentName);
107598
107765
  case "progress_update":
107599
107766
  return executeProgressUpdate(args);
107600
107767
  case "react":
@@ -107875,8 +108042,12 @@ async function synthesizeVoiceOut(plan) {
107875
108042
  return null;
107876
108043
  }
107877
108044
  }
107878
- async function executeReply(args) {
107879
- return sendReply(gatewaySendReplyDeps(), { args, turn: currentTurn });
108045
+ async function executeReply(args, callerAgentName = null) {
108046
+ return sendReply(gatewaySendReplyDeps(), {
108047
+ args,
108048
+ turn: currentTurn,
108049
+ callerIsForeignSession: replyCallerIsForeignSession(callerAgentName)
108050
+ });
107880
108051
  }
107881
108052
  function gatewaySendReplyDeps() {
107882
108053
  return {
@@ -107915,6 +108086,7 @@ function gatewaySendReplyDeps() {
107915
108086
  resolveThreadId,
107916
108087
  getLatestInboundMessageId,
107917
108088
  getLastSubagentHandbackAt,
108089
+ subagentReplyAuthority,
107918
108090
  recordOutbound,
107919
108091
  emissionAuthorityFor,
107920
108092
  clearActivitySummary,