switchroom 0.19.31 → 0.19.33

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.
@@ -44732,7 +44732,7 @@ async function handleVoiceMessage(ctx, deps) {
44732
44732
  }
44733
44733
 
44734
44734
  // gateway/status-pin-store.ts
44735
- var SNAPSHOT_VERSIONS = new Set([1, 2, 3, 4]);
44735
+ var SNAPSHOT_VERSION = 4;
44736
44736
  function isPinRow(x) {
44737
44737
  if (x == null || typeof x !== "object")
44738
44738
  return false;
@@ -44757,12 +44757,12 @@ function loadStatusPins(path, fs2) {
44757
44757
  if (parsed == null || typeof parsed !== "object")
44758
44758
  return [];
44759
44759
  const env = parsed;
44760
- if (typeof env.v !== "number" || !SNAPSHOT_VERSIONS.has(env.v) || !Array.isArray(env.pins))
44760
+ if (!Number.isInteger(env.v) || env.v < 1 || !Array.isArray(env.pins))
44761
44761
  return [];
44762
44762
  return env.pins.filter(isPinRow);
44763
44763
  }
44764
44764
  function persistStatusPins(path, fs2, snapshot, log = (l) => process.stderr.write(l)) {
44765
- const env = { v: 4, pins: [...snapshot] };
44765
+ const env = { v: SNAPSHOT_VERSION, pins: [...snapshot] };
44766
44766
  const tmp = path + ".tmp";
44767
44767
  try {
44768
44768
  fs2.writeFileSync(tmp, JSON.stringify(env));
@@ -71213,6 +71213,9 @@ function projectTranscriptLine(line) {
71213
71213
  if (op === "dequeue") {
71214
71214
  return [{ kind: "dequeue" }];
71215
71215
  }
71216
+ if (op === "remove") {
71217
+ return [{ kind: "queue_remove", rawContent: obj.content ?? "" }];
71218
+ }
71216
71219
  return [];
71217
71220
  }
71218
71221
  if (type === "assistant") {
@@ -79814,6 +79817,195 @@ function decideFallbackTeardownNotice2(input) {
79814
79817
  }
79815
79818
 
79816
79819
  // gateway/stream-render.ts
79820
+ var PARKED_TURN_START_MAX = 16;
79821
+ var PARKED_TURN_START_TTL_MS = 30 * 60000;
79822
+ var parkedTurnStarts = [];
79823
+ function pruneParkedTurnStarts(now) {
79824
+ for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
79825
+ if (now - parkedTurnStarts[i].parkedAt > PARKED_TURN_START_TTL_MS) {
79826
+ const [dropped] = parkedTurnStarts.splice(i, 1);
79827
+ process.stderr.write(`telegram gateway: parked-turn-start expired chat=${dropped.chatId ?? "-"} ` + `msg=${dropped.messageId ?? "-"} age_ms=${now - dropped.parkedAt}
79828
+ `);
79829
+ }
79830
+ }
79831
+ }
79832
+ function parkTurnStart(env, now) {
79833
+ parkedTurnStarts.push({ ...env, parkedAt: now });
79834
+ while (parkedTurnStarts.length > PARKED_TURN_START_MAX) {
79835
+ const [dropped] = parkedTurnStarts.splice(0, 1);
79836
+ process.stderr.write(`telegram gateway: parked-turn-start evicted (cap ${PARKED_TURN_START_MAX}) ` + `chat=${dropped.chatId ?? "-"} msg=${dropped.messageId ?? "-"}
79837
+ `);
79838
+ }
79839
+ }
79840
+ function takeParkedTurnStart() {
79841
+ return parkedTurnStarts.pop() ?? null;
79842
+ }
79843
+ function discardParkedTurnStart(rawContent) {
79844
+ for (let i = parkedTurnStarts.length - 1;i >= 0; i--) {
79845
+ if (parkedTurnStarts[i].rawContent === rawContent) {
79846
+ parkedTurnStarts.splice(i, 1);
79847
+ return true;
79848
+ }
79849
+ }
79850
+ return false;
79851
+ }
79852
+ function beginTurn(deps, ev) {
79853
+ const {
79854
+ HANDBACK_PRETURN_ENABLED,
79855
+ STATE_DIR,
79856
+ clearActivitySummary,
79857
+ extractUserPromptPreview,
79858
+ getCurrentTurn,
79859
+ getPendingPtyPartial,
79860
+ handbackPreturnSignal,
79861
+ handlePtyPartial,
79862
+ isDmChatId,
79863
+ makeNarrativeGate,
79864
+ pendingCrossTurnGate,
79865
+ preambleSuppressor,
79866
+ promoteQueuedStatus,
79867
+ rememberRecentTurn,
79868
+ scheduleEarlyLivenessOpen,
79869
+ setCurrentTurn,
79870
+ setPendingPtyPartial,
79871
+ startTurnTypingLoop,
79872
+ statusKey,
79873
+ turnsDb,
79874
+ typingWrapper
79875
+ } = deps;
79876
+ typingWrapper.drainAll();
79877
+ if (ev.chatId) {
79878
+ const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
79879
+ clearPending2(statusKey(ev.chatId, enqThreadId), "handback");
79880
+ }
79881
+ if (ev.chatId) {
79882
+ const prior = getCurrentTurn();
79883
+ if (prior?.answerStream != null) {
79884
+ prior.answerStream.forceNewMessage();
79885
+ prior.answerStream.stop();
79886
+ prior.answerStream = null;
79887
+ }
79888
+ if (prior?.orphanedReplyTimeoutId != null) {
79889
+ clearTimeout(prior.orphanedReplyTimeoutId);
79890
+ prior.orphanedReplyTimeoutId = null;
79891
+ }
79892
+ prior?.narrativeGate?.teardown();
79893
+ if (prior != null && prior.endedAt == null) {
79894
+ clearActivitySummary(prior);
79895
+ process.stderr.write(`telegram gateway: ${formatTurnLifecycle("clear", "superseded", prior, Date.now())}
79896
+ `);
79897
+ }
79898
+ const startedAt = Date.now();
79899
+ const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
79900
+ const turnId = deriveTurnId(ev.chatId, enqThreadIdNum ?? null, ev.messageId) ?? `${chatKey(ev.chatId, enqThreadIdNum ?? null)}#synthetic-${startedAt}`;
79901
+ const xTurnGateKey = turnId;
79902
+ const consumedCrossTurnGate = pendingCrossTurnGate.get(xTurnGateKey);
79903
+ if (consumedCrossTurnGate != null)
79904
+ pendingCrossTurnGate.delete(xTurnGateKey);
79905
+ const next = {
79906
+ sessionChatId: ev.chatId,
79907
+ sessionThreadId: enqThreadIdNum,
79908
+ sourceMessageId: parseSourceMessageId(ev.messageId),
79909
+ startedAt,
79910
+ gatewayReceiveAt: startedAt,
79911
+ role: deriveTurnRole(ev.rawContent),
79912
+ ...consumedCrossTurnGate != null ? { crossTurnGate: consumedCrossTurnGate } : {},
79913
+ replyCalled: false,
79914
+ finalAnswerDelivered: false,
79915
+ finalAnswerSubstantive: false,
79916
+ finalAnswerEverDelivered: false,
79917
+ answerDelivered: false,
79918
+ flushedAnswerText: null,
79919
+ endedAt: null,
79920
+ firstPingAt: null,
79921
+ firstPingWasSubstantive: false,
79922
+ silentAnchorMessageId: null,
79923
+ silentAnchorText: "",
79924
+ capturedText: [],
79925
+ capturedBlockMeta: [],
79926
+ orphanedReplyTimeoutId: null,
79927
+ answerReadyFlushTimeoutId: null,
79928
+ liveness: new LivenessTracker(startedAt),
79929
+ turnId,
79930
+ registryKey: null,
79931
+ noReplyDrainTimer: null,
79932
+ lastAssistantMsgId: null,
79933
+ lastAssistantDone: false,
79934
+ toolCallCount: 0,
79935
+ labeledToolCount: 0,
79936
+ totalTokens: 0,
79937
+ seenUsageMessageIds: new Set,
79938
+ activityMessageId: null,
79939
+ activityInFlight: null,
79940
+ activityPendingRender: null,
79941
+ activityLastSentRender: null,
79942
+ activityEverOpened: false,
79943
+ activityDrainFailures: 0,
79944
+ mirrorLines: [],
79945
+ narrativeGate: undefined,
79946
+ lastReplyText: "",
79947
+ foregroundSubAgents: new Map,
79948
+ answerStream: null,
79949
+ isDm: isDmChatId(ev.chatId),
79950
+ emissionAuthority: new EmissionAuthority(statusKey(ev.chatId, enqThreadIdNum))
79951
+ };
79952
+ next.narrativeGate = makeNarrativeGate(next);
79953
+ if (HANDBACK_PRETURN_ENABLED) {
79954
+ const handbackAdoption = handbackPreturnSignal.tryAdopt(turnId);
79955
+ if (handbackAdoption != null) {
79956
+ if (handbackAdoption.activityMessageId != null) {
79957
+ next.activityMessageId = handbackAdoption.activityMessageId;
79958
+ next.activityEverOpened = true;
79959
+ }
79960
+ process.stderr.write(`telegram gateway: handback pre-turn adopted turnId=${turnId} ` + `key=${handbackAdoption.statusKey} card=${handbackAdoption.activityMessageId ?? "none"}
79961
+ `);
79962
+ }
79963
+ }
79964
+ startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
79965
+ setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
79966
+ scheduleEarlyLivenessOpen(next);
79967
+ process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
79968
+ `);
79969
+ rememberRecentTurn(next);
79970
+ promoteQueuedStatus(ev.chatId, enqThreadIdNum);
79971
+ shadowEmit({
79972
+ kind: "turnStart",
79973
+ key: statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : undefined),
79974
+ at: startedAt
79975
+ });
79976
+ preambleSuppressor.reset();
79977
+ clearSilentEndState2(statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null));
79978
+ if (turnsDb != null) {
79979
+ const evThreadIdNum = ev.threadId != null ? Number(ev.threadId) : null;
79980
+ const turnKey3 = chatKeyWithSuffix(ev.chatId, evThreadIdNum, String(startedAt));
79981
+ next.registryKey = turnKey3;
79982
+ const userPromptPreview = extractUserPromptPreview(ev.rawContent);
79983
+ try {
79984
+ recordTurnStart(turnsDb, {
79985
+ turnKey: turnKey3,
79986
+ chatId: String(ev.chatId),
79987
+ threadId: ev.threadId != null ? String(ev.threadId) : null,
79988
+ lastUserMsgId: ev.messageId != null ? String(ev.messageId) : null,
79989
+ userPromptPreview
79990
+ });
79991
+ } catch (err) {
79992
+ process.stderr.write(`telegram gateway: recordTurnStart failed turnKey=${turnKey3}: ${err.message}
79993
+ `);
79994
+ }
79995
+ writeTurnActiveMarker(STATE_DIR, {
79996
+ turnKey: turnKey3,
79997
+ chatId: String(ev.chatId),
79998
+ threadId: ev.threadId != null ? String(ev.threadId) : null,
79999
+ startedAt
80000
+ });
80001
+ }
80002
+ const pending = getPendingPtyPartial();
80003
+ if (pending != null) {
80004
+ setPendingPtyPartial(null);
80005
+ handlePtyPartial(pending);
80006
+ }
80007
+ }
80008
+ }
79817
80009
  function handleSessionEvent(deps, ev) {
79818
80010
  const {
79819
80011
  ANSWER_LANE,
@@ -79910,140 +80102,47 @@ function handleSessionEvent(deps, ev) {
79910
80102
  }
79911
80103
  switch (ev.kind) {
79912
80104
  case "enqueue": {
79913
- typingWrapper.drainAll();
79914
- if (ev.chatId) {
79915
- const enqThreadId = ev.threadId != null ? Number(ev.threadId) : undefined;
79916
- clearPending2(statusKey(ev.chatId, enqThreadId), "handback");
79917
- }
80105
+ const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
80106
+ const now = Date.now();
79918
80107
  if (ev.chatId) {
79919
- const prior = getCurrentTurn();
79920
- if (prior?.answerStream != null) {
79921
- prior.answerStream.forceNewMessage();
79922
- prior.answerStream.stop();
79923
- prior.answerStream = null;
79924
- }
79925
- if (prior?.orphanedReplyTimeoutId != null) {
79926
- clearTimeout(prior.orphanedReplyTimeoutId);
79927
- prior.orphanedReplyTimeoutId = null;
79928
- }
79929
- prior?.narrativeGate?.teardown();
79930
- const startedAt = Date.now();
79931
- const enqThreadIdNum = ev.threadId != null ? Number(ev.threadId) : undefined;
79932
- const turnId = deriveTurnId(ev.chatId, enqThreadIdNum ?? null, ev.messageId) ?? `${chatKey(ev.chatId, enqThreadIdNum ?? null)}#synthetic-${startedAt}`;
79933
- const xTurnGateKey = turnId;
79934
- const consumedCrossTurnGate = pendingCrossTurnGate.get(xTurnGateKey);
79935
- if (consumedCrossTurnGate != null)
79936
- pendingCrossTurnGate.delete(xTurnGateKey);
79937
- const next = {
79938
- sessionChatId: ev.chatId,
79939
- sessionThreadId: enqThreadIdNum,
79940
- sourceMessageId: parseSourceMessageId(ev.messageId),
79941
- startedAt,
79942
- gatewayReceiveAt: startedAt,
79943
- role: deriveTurnRole(ev.rawContent),
79944
- ...consumedCrossTurnGate != null ? { crossTurnGate: consumedCrossTurnGate } : {},
79945
- replyCalled: false,
79946
- finalAnswerDelivered: false,
79947
- finalAnswerSubstantive: false,
79948
- finalAnswerEverDelivered: false,
79949
- answerDelivered: false,
79950
- flushedAnswerText: null,
79951
- endedAt: null,
79952
- firstPingAt: null,
79953
- firstPingWasSubstantive: false,
79954
- silentAnchorMessageId: null,
79955
- silentAnchorText: "",
79956
- capturedText: [],
79957
- capturedBlockMeta: [],
79958
- orphanedReplyTimeoutId: null,
79959
- answerReadyFlushTimeoutId: null,
79960
- liveness: new LivenessTracker(startedAt),
79961
- turnId,
79962
- registryKey: null,
79963
- noReplyDrainTimer: null,
79964
- lastAssistantMsgId: null,
79965
- lastAssistantDone: false,
79966
- toolCallCount: 0,
79967
- labeledToolCount: 0,
79968
- totalTokens: 0,
79969
- seenUsageMessageIds: new Set,
79970
- activityMessageId: null,
79971
- activityInFlight: null,
79972
- activityPendingRender: null,
79973
- activityLastSentRender: null,
79974
- activityEverOpened: false,
79975
- activityDrainFailures: 0,
79976
- mirrorLines: [],
79977
- narrativeGate: undefined,
79978
- lastReplyText: "",
79979
- foregroundSubAgents: new Map,
79980
- answerStream: null,
79981
- isDm: isDmChatId(ev.chatId),
79982
- emissionAuthority: new EmissionAuthority(statusKey(ev.chatId, enqThreadIdNum))
79983
- };
79984
- next.narrativeGate = makeNarrativeGate(next);
79985
- if (HANDBACK_PRETURN_ENABLED) {
79986
- const handbackAdoption = handbackPreturnSignal.tryAdopt(turnId);
79987
- if (handbackAdoption != null) {
79988
- if (handbackAdoption.activityMessageId != null) {
79989
- next.activityMessageId = handbackAdoption.activityMessageId;
79990
- next.activityEverOpened = true;
79991
- }
79992
- process.stderr.write(`telegram gateway: handback pre-turn adopted turnId=${turnId} ` + `key=${handbackAdoption.statusKey} card=${handbackAdoption.activityMessageId ?? "none"}
79993
- `);
79994
- }
79995
- }
79996
- startTurnTypingLoop(ev.chatId, enqThreadIdNum ?? null);
79997
- setCurrentTurn(next, statusKey(ev.chatId, enqThreadIdNum));
79998
- scheduleEarlyLivenessOpen(next);
79999
- process.stderr.write(`telegram gateway: ${formatTurnLifecycle("set", "enqueue", next, startedAt)}
80000
- `);
80001
- rememberRecentTurn(next);
80002
- promoteQueuedStatus(ev.chatId, enqThreadIdNum);
80003
80108
  if (DELIVERY_CONFIRM_ENABLED) {
80004
80109
  ackDelivery(deliveryQueue, chatKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null), ev.messageId, ev.rawContent);
80005
80110
  }
80006
- shadowEmit({
80007
- kind: "turnStart",
80008
- key: statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : undefined),
80009
- at: startedAt
80010
- });
80011
- preambleSuppressor.reset();
80012
- clearSilentEndState2(statusKey(ev.chatId, ev.threadId != null ? Number(ev.threadId) : null));
80013
- if (turnsDb != null) {
80014
- const evThreadIdNum = ev.threadId != null ? Number(ev.threadId) : null;
80015
- const turnKey3 = chatKeyWithSuffix(ev.chatId, evThreadIdNum, String(startedAt));
80016
- next.registryKey = turnKey3;
80017
- const userPromptPreview = extractUserPromptPreview(ev.rawContent);
80018
- try {
80019
- recordTurnStart(turnsDb, {
80020
- turnKey: turnKey3,
80021
- chatId: String(ev.chatId),
80022
- threadId: ev.threadId != null ? String(ev.threadId) : null,
80023
- lastUserMsgId: ev.messageId != null ? String(ev.messageId) : null,
80024
- userPromptPreview
80025
- });
80026
- } catch (err) {
80027
- process.stderr.write(`telegram gateway: recordTurnStart failed turnKey=${turnKey3}: ${err.message}
80028
- `);
80029
- }
80030
- writeTurnActiveMarker(STATE_DIR, {
80031
- turnKey: turnKey3,
80032
- chatId: String(ev.chatId),
80033
- threadId: ev.threadId != null ? String(ev.threadId) : null,
80034
- startedAt
80035
- });
80036
- }
80037
- const pending = getPendingPtyPartial();
80038
- if (pending != null) {
80039
- setPendingPtyPartial(null);
80040
- handlePtyPartial(pending);
80041
- }
80042
80111
  }
80112
+ pruneParkedTurnStarts(now);
80113
+ if (!ev.chatId) {
80114
+ beginTurn(deps, ev);
80115
+ return;
80116
+ }
80117
+ const live = getCurrentTurn();
80118
+ const sessionBusy = live != null && live.endedAt == null;
80119
+ if (!sessionBusy && parkedTurnStarts.length === 0) {
80120
+ beginTurn(deps, ev);
80121
+ return;
80122
+ }
80123
+ parkTurnStart({
80124
+ chatId: ev.chatId,
80125
+ messageId: ev.messageId,
80126
+ threadId: ev.threadId,
80127
+ rawContent: ev.rawContent
80128
+ }, now);
80129
+ process.stderr.write(`telegram gateway: turn-start parked (session busy) chat=${ev.chatId} ` + `thread=${enqThreadIdNum ?? "-"} msg=${ev.messageId ?? "-"} ` + `parked=${parkedTurnStarts.length}
80130
+ `);
80131
+ return;
80132
+ }
80133
+ case "dequeue": {
80134
+ pruneParkedTurnStarts(Date.now());
80135
+ const parked = takeParkedTurnStart();
80136
+ if (parked == null)
80137
+ return;
80138
+ beginTurn(deps, parked);
80043
80139
  return;
80044
80140
  }
80045
- case "dequeue":
80141
+ case "queue_remove": {
80142
+ pruneParkedTurnStarts(Date.now());
80143
+ discardParkedTurnStart(ev.rawContent);
80046
80144
  return;
80145
+ }
80047
80146
  case "model": {
80048
80147
  const turn = getCurrentTurn();
80049
80148
  if (turn != null) {
@@ -84475,12 +84574,12 @@ init_paths();
84475
84574
  var _warnedReads2 = new Set;
84476
84575
 
84477
84576
  // ../src/setup/hindsight-pg-defaults.ts
84478
- var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 8 * 1024;
84577
+ var HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION = 16 * 1024;
84479
84578
  var HINDSIGHT_PG_APP_ANON_MIB = 2560;
84480
84579
  var HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB = 2048;
84481
84580
  var HINDSIGHT_PG_SHARED_BUFFERS_BUDGET_MIB = HINDSIGHT_PG_MEM_LIMIT_MIB_FOR_DERIVATION - HINDSIGHT_PG_APP_ANON_MIB - HINDSIGHT_PG_PAGE_CACHE_FLOOR_MIB;
84482
- var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 1536;
84483
- var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 4096;
84581
+ var HINDSIGHT_PG_DEFAULT_SHARED_BUFFERS_MIB = 3072;
84582
+ var HINDSIGHT_PG_DEFAULT_EFFECTIVE_CACHE_SIZE_MIB = 7168;
84484
84583
  function pgMib(mib) {
84485
84584
  return `${mib}MB`;
84486
84585
  }
@@ -84655,7 +84754,17 @@ var HINDSIGHT_HEALTHCHECK_CMD = `python3 -c '${HINDSIGHT_HEALTHCHECK_PY}'`;
84655
84754
  var DOCKER_PROBE_TIMEOUT_MS = 60 * 1000;
84656
84755
 
84657
84756
  // ../src/memory/hindsight.ts
84658
- var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and useful weeks from now: user preferences and standing rules, ongoing projects and recurring commitments, technical and architectural decisions with their rationale, and people/tool relationships. A preference revealed by a request is durable \u2014 record the preference (what the user likes, wants, or always does), not the request itself.
84757
+ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and useful weeks from now, once this session is forgotten.
84758
+ ` + `
84759
+ ` + `DURABILITY GATE. Emit a candidate ONLY if it is one of these five classes:
84760
+ ` + `- PREFERENCE \u2014 what the user likes, wants, or always does; a standing rule or correction.
84761
+ ` + `- DECISION \u2014 a settled choice that changes how future work is done, including a choice NOT to do something. A decision about the mechanics of the CURRENT task (which worker to dispatch, which branch to rebase, which PR to merge now, what to do next) is process narration, not a durable decision \u2014 drop it unless it establishes a standing rule or permanently changes a system.
84762
+ ` + `- FINDING \u2014 a root cause, a measurement, or verified behaviour of a system. Include the number.
84763
+ ` + `- OUTCOME \u2014 a completed result that changed the world: what shipped, what a thing turned out to be. Not the act of shipping it.
84764
+ ` + `- RELATIONSHIP \u2014 who a person is, what a project or tool is, and how they connect.
84765
+ ` + `If a candidate fits none of the five, it is not a memory. Drop it; do not reword it into one.
84766
+ ` + `
84767
+ ` + `A preference revealed by a request is durable \u2014 record the preference (what the user likes, wants, or always does), not the request itself.
84659
84768
  ` + `
84660
84769
  ` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
84661
84770
  ` + `candidate a file path, a command/process/agent/session id, a temp directory, or
@@ -84672,6 +84781,9 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
84672
84781
  ` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
84673
84782
  ` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
84674
84783
  ` + ` assistant used X to query Y", "ran a search", "sent the message").
84784
+ ` + `- The act of delegating, dispatching, spawning, launching, steering or merging
84785
+ ` + ` work \u2014 including when it succeeded. "X was dispatched and completed" is the
84786
+ ` + ` session describing itself. Record only what the work LEARNED or CHANGED.
84675
84787
  ` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
84676
84788
  ` + ` running) \u2014 retain the outcome only once the task completes or a decision is made.
84677
84789
  ` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
@@ -84694,8 +84806,12 @@ var DEFAULT_RETAIN_MISSION = `Extract durable facts that will still be true and
84694
84806
  ` + ` the fact is explicitly dated, in which case record it as a dated observation.
84695
84807
  ` + `- Greetings, acknowledgements, and routine operational chatter.
84696
84808
  ` + `
84809
+ ` + `Write each fact so it stands alone: name the thing, the number, and the date. A
84810
+ ` + `sentence that only makes sense while reading this transcript is not durable.
84811
+ ` + `
84697
84812
  ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
84698
- ` + "nothing durable remains, return an empty facts list.";
84813
+ ` + `nothing durable remains, return an empty facts list.
84814
+ `;
84699
84815
  var SUPERSEDED_RETAIN_MISSIONS = [
84700
84816
  "Extract technical decisions, architectural choices, user preferences, project context, and people/tool relationships. Ignore routine greetings and transient operational details.",
84701
84817
  "Extract user preferences, ongoing projects, recurring commitments, " + "important context, and durable facts that should help across future " + "conversations. Skip one-off chatter and temporary task noise.",
@@ -84742,7 +84858,96 @@ var SUPERSEDED_RETAIN_MISSIONS = [
84742
84858
  ` + `- Greetings, acknowledgements, and routine operational chatter.
84743
84859
  ` + `
84744
84860
  ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
84745
- ` + "nothing durable remains, return an empty facts list."
84861
+ ` + "nothing durable remains, return an empty facts list.",
84862
+ `Extract durable facts that will still be true and useful weeks from now: user preferences and standing rules, ongoing projects and recurring commitments, technical and architectural decisions with their rationale, and people/tool relationships. A preference revealed by a request is durable \u2014 record the preference (what the user likes, wants, or always does), not the request itself.
84863
+ ` + `
84864
+ ` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
84865
+ ` + `candidate a file path, a command/process/agent/session id, a temp directory, or
84866
+ ` + `the location where some output was written? If yes, drop it \u2014 it is transcript
84867
+ ` + `exhaust, not memory.
84868
+ ` + `
84869
+ ` + `NEVER extract:
84870
+ ` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
84871
+ ` + ` text resembles any of these: "File created successfully at /path/to/file",
84872
+ ` + ` "A background command with ID bctz4yskm is running, and its output will be
84873
+ ` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
84874
+ ` + ` and is running in the background", "User executed a Bash command to sleep for
84875
+ ` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
84876
+ ` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
84877
+ ` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
84878
+ ` + ` assistant used X to query Y", "ran a search", "sent the message").
84879
+ ` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
84880
+ ` + ` running) \u2014 retain the outcome only once the task completes or a decision is made.
84881
+ ` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
84882
+ ` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
84883
+ ` + ` reset assistant state").
84884
+ ` + `- Hindsight's own errors, retries, backlogs, or internal state \u2014 the memory
84885
+ ` + ` system's self-reports are not memories.
84886
+ ` + `- Restatements of the user's current request or the task in progress.
84887
+ ` + `- Volatile state written as a timeless assertion. A version, count, size,
84888
+ ` + ` backlog, status, or any "X is running Y" / "X is at Y" / "X is currently Y"
84889
+ ` + ` claim is true only at the instant it was said. Concretely, never produce a
84890
+ ` + ` fact whose text resembles any of these: "Switchroom fleet is running image
84891
+ ` + ` version v0.18.19", "The switchroom repo is at /path/to/fleet, version
84892
+ ` + ` v0.19.5", "Bank overlord has 43155 pending consolidations", "The build is
84893
+ ` + ` currently green". If the claim is worth keeping, put the date INSIDE the
84894
+ ` + ` fact text ("As of 2026-07-19 the fleet was running v0.18.19"); if you
84895
+ ` + ` cannot date it, drop it. An undated one is recalled forever as though it
84896
+ ` + ` were still true, which is worse than not remembering it at all.
84897
+ ` + `- Transient state (unread counts, build status, what is running right now) unless
84898
+ ` + ` the fact is explicitly dated, in which case record it as a dated observation.
84899
+ ` + `- Greetings, acknowledgements, and routine operational chatter.
84900
+ ` + `
84901
+ ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
84902
+ ` + "nothing durable remains, return an empty facts list.",
84903
+ `Extract durable facts that will still be true and useful weeks from now, once this session is forgotten.
84904
+ ` + `
84905
+ ` + `DURABILITY GATE. Emit a candidate ONLY if it is one of these five classes:
84906
+ ` + `- PREFERENCE \u2014 what the user likes, wants, or always does; a standing rule or correction.
84907
+ ` + `- DECISION \u2014 a settled choice that changes how future work is done, including a choice NOT to do something. A decision about the mechanics of the CURRENT task (which worker to dispatch, which branch to rebase, which PR to merge now, what to do next) is process narration, not a durable decision \u2014 drop it unless it establishes a standing rule or permanently changes a system.
84908
+ ` + `- FINDING \u2014 a root cause, a measurement, or verified behaviour of a system. Include the number.
84909
+ ` + `- OUTCOME \u2014 a completed result that changed the world: what shipped, what a thing turned out to be. Not the act of shipping it.
84910
+ ` + `- RELATIONSHIP \u2014 who a person is, what a project or tool is, and how they connect.
84911
+ ` + `If a candidate fits none of the five, it is not a memory. Drop it; do not reword it into one.
84912
+ ` + `
84913
+ ` + `A preference revealed by a request is durable \u2014 record the preference (what the user likes, wants, or always does), not the request itself.
84914
+ ` + `
84915
+ ` + `A TOOL RESULT IS NOT A FACT. Before extracting, ask: is the subject of this
84916
+ ` + `candidate a file path, a command/process/agent/session id, a temp directory, or
84917
+ ` + `the location where some output was written? If yes, drop it \u2014 it is transcript
84918
+ ` + `exhaust, not memory.
84919
+ ` + `
84920
+ ` + `NEVER extract:
84921
+ ` + `- Tool results verbatim or paraphrased. Concretely, never produce a fact whose
84922
+ ` + ` text resembles any of these: "File created successfully at /path/to/file",
84923
+ ` + ` "A background command with ID bctz4yskm is running, and its output will be
84924
+ ` + ` written to /tmp/...", "Async agent a745598ba84e71df1 was launched successfully
84925
+ ` + ` and is running in the background", "User executed a Bash command to sleep for
84926
+ ` + ` 200 seconds", "The assistant used grep to locate 'truncateSync' in src/foo.ts".
84927
+ ` + `- Anything mentioning a path under /tmp, a scratchpad directory, or a .tmp file.
84928
+ ` + `- Agent tool-use traces or narration of what the assistant did (e.g. "the
84929
+ ` + ` assistant used X to query Y", "ran a search", "sent the message").
84930
+ ` + `- The act of delegating, dispatching, spawning, launching, steering or merging
84931
+ ` + ` work \u2014 including when it succeeded. "X was dispatched and completed" is the
84932
+ ` + ` session describing itself. Record only what the work LEARNED or CHANGED.
84933
+ ` + `- In-flight workflow/process narration (a sub-task started, paused, or is still
84934
+ ` + ` running) \u2014 retain the outcome only once the task completes or a decision is made.
84935
+ ` + `- Operation, request, batch, agent, command or session IDs, UUIDs, hashes, or error codes.
84936
+ ` + `- Slash commands the user typed and their effects (e.g. "User issued /clear to
84937
+ ` + ` reset assistant state").
84938
+ ` + `- Hindsight's own errors, retries, backlogs, or internal state \u2014 the memory
84939
+ ` + ` system's self-reports are not memories.
84940
+ ` + `- Restatements of the user's current request or the task in progress.
84941
+ ` + `- Transient state (unread counts, build status, what is running right now) unless
84942
+ ` + ` the fact is explicitly dated, in which case record it as a dated observation.
84943
+ ` + `- Greetings, acknowledgements, and routine operational chatter.
84944
+ ` + `
84945
+ ` + `Write each fact so it stands alone: name the thing, the number, and the date. A
84946
+ ` + `sentence that only makes sense while reading this transcript is not durable.
84947
+ ` + `
84948
+ ` + `If a candidate fact matches an exclusion, drop it rather than rewording it. If
84949
+ ` + `nothing durable remains, return an empty facts list.
84950
+ `
84746
84951
  ];
84747
84952
  var DEFAULT_OBSERVATIONS_MISSION = `Synthesise durable, standing knowledge about the people, projects, and systems this agent works with: preferences and standing rules, roles and relationships, skills and recurring patterns, technical and operational decisions with their rationale, and the state of long-running work once it lands.
84748
84953
  ` + `
@@ -86424,7 +86629,7 @@ function shouldSweepChatAtBoot(chatId) {
86424
86629
 
86425
86630
  // gateway/stale-pin-sweep-store.ts
86426
86631
  var SWEEP_MAX_ATTEMPTS = 8;
86427
- var SWEEP_ENVELOPE_VERSIONS = new Set([1]);
86632
+ var SWEEP_ENVELOPE_VERSION = 1;
86428
86633
  function sweepTargetKey(chatId, threadId) {
86429
86634
  return `${chatId}:${threadId ?? "-"}`;
86430
86635
  }
@@ -86452,13 +86657,13 @@ function loadSweepCursors(path2, fs2) {
86452
86657
  if (parsed == null || typeof parsed !== "object")
86453
86658
  return [];
86454
86659
  const env = parsed;
86455
- if (typeof env.v !== "number" || !SWEEP_ENVELOPE_VERSIONS.has(env.v) || !Array.isArray(env.cursors)) {
86660
+ if (!Number.isInteger(env.v) || env.v < 1 || !Array.isArray(env.cursors)) {
86456
86661
  return [];
86457
86662
  }
86458
86663
  return env.cursors.filter(isCursorRow);
86459
86664
  }
86460
86665
  function persistSweepCursors(path2, fs2, cursors, log = (l) => process.stderr.write(l)) {
86461
- const env = { v: 1, cursors: [...cursors] };
86666
+ const env = { v: SWEEP_ENVELOPE_VERSION, cursors: [...cursors] };
86462
86667
  try {
86463
86668
  fs2.writeFileSync(path2, JSON.stringify(env));
86464
86669
  } catch (err) {
@@ -86879,11 +87084,13 @@ function createStalePinSweeper(deps) {
86879
87084
  const repinUnpinLoop = async (target, kind, cursor) => {
86880
87085
  const gate = gateFor(kind);
86881
87086
  let popped = 0;
87087
+ let issued = 0;
86882
87088
  let consecutiveFloods = 0;
86883
87089
  let lastRetryAfter = null;
86884
87090
  let noProgressAtSameRetryAfter = 0;
86885
87091
  let lastObservedTop = null;
86886
87092
  let unverifiedPop = false;
87093
+ const out = (r) => ({ ...r, issued });
86887
87094
  const creditPop = () => {
86888
87095
  popped++;
86889
87096
  cursor.popped++;
@@ -86892,29 +87099,29 @@ function createStalePinSweeper(deps) {
86892
87099
  };
86893
87100
  for (let i = 0;i < gate.maxPopsPerChatPerSweep; i++) {
86894
87101
  if (circuitOpen) {
86895
- return { status: "aborted-circuit-breaker", popped, detail: "breaker open" };
87102
+ return out({ status: "aborted-circuit-breaker", popped, detail: "breaker open" });
86896
87103
  }
86897
87104
  const observed = await readTopVerified(target.chatId);
86898
87105
  if (observed == null) {
86899
- return { status: "incomplete", popped, detail: "pin-stack read unverifiable" };
87106
+ return out({ status: "incomplete", popped, detail: "pin-stack read unverifiable" });
86900
87107
  }
86901
87108
  if (unverifiedPop) {
86902
87109
  if (observed.top != null && observed.top === lastObservedTop) {
86903
- return {
87110
+ return out({
86904
87111
  status: "incomplete",
86905
87112
  popped,
86906
87113
  detail: `no progress: message ${observed.top} still on top after a repin+unpin`
86907
- };
87114
+ });
86908
87115
  }
86909
87116
  creditPop();
86910
87117
  unverifiedPop = false;
86911
87118
  }
86912
87119
  if (observed.top == null) {
86913
- return { status: "drained", popped };
87120
+ return out({ status: "drained", popped });
86914
87121
  }
86915
87122
  lastObservedTop = observed.top;
86916
87123
  if (!await awaitWriteSlot(kind)) {
86917
- return { status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" };
87124
+ return out({ status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" });
86918
87125
  }
86919
87126
  try {
86920
87127
  await deps.pinSilent(target.chatId, observed.top);
@@ -86922,10 +87129,10 @@ function createStalePinSweeper(deps) {
86922
87129
  const d = classify(err);
86923
87130
  if (d.kind === "breaker") {
86924
87131
  circuitOpen = true;
86925
- return { status: "aborted-circuit-breaker", popped, detail: d.detail };
87132
+ return out({ status: "aborted-circuit-breaker", popped, detail: d.detail });
86926
87133
  }
86927
87134
  if (d.kind === "rights")
86928
- return { status: "skipped-no-rights", popped };
87135
+ return out({ status: "skipped-no-rights", popped });
86929
87136
  if (d.kind === "flood") {
86930
87137
  consecutiveFloods++;
86931
87138
  if (lastRetryAfter === d.seconds)
@@ -86934,19 +87141,19 @@ function createStalePinSweeper(deps) {
86934
87141
  noProgressAtSameRetryAfter = 1;
86935
87142
  lastRetryAfter = d.seconds;
86936
87143
  if (consecutiveFloods >= MAX_CONSECUTIVE_FLOOD_WAITS || noProgressAtSameRetryAfter >= MAX_IDENTICAL_RETRY_AFTER_NO_PROGRESS) {
86937
- return {
87144
+ return out({
86938
87145
  status: "deferred-flood",
86939
87146
  popped,
86940
87147
  detail: `flood: ${consecutiveFloods} consecutive 429s, retry_after=${d.seconds}s`
86941
- };
87148
+ });
86942
87149
  }
86943
87150
  await deps.sleep(d.seconds * 1000 * Math.pow(FLOOD_BACKOFF_FACTOR, consecutiveFloods - 1));
86944
87151
  continue;
86945
87152
  }
86946
- return { status: "error", popped, detail: d.detail };
87153
+ return out({ status: "error", popped, detail: d.detail });
86947
87154
  }
86948
87155
  if (!await awaitWriteSlot(kind)) {
86949
- return { status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" };
87156
+ return out({ status: "deferred-budget", popped, detail: "per-minute pin-op budget spent" });
86950
87157
  }
86951
87158
  try {
86952
87159
  await deps.unpin(target.chatId, observed.top);
@@ -86954,12 +87161,12 @@ function createStalePinSweeper(deps) {
86954
87161
  const d = classify(err);
86955
87162
  if (d.kind === "breaker") {
86956
87163
  circuitOpen = true;
86957
- return { status: "aborted-circuit-breaker", popped, detail: d.detail };
87164
+ return out({ status: "aborted-circuit-breaker", popped, detail: d.detail });
86958
87165
  }
86959
87166
  if (d.kind === "rights")
86960
- return { status: "skipped-no-rights", popped };
87167
+ return out({ status: "skipped-no-rights", popped });
86961
87168
  if (isNothingToUnpinError(err))
86962
- return { status: "drained", popped };
87169
+ return out({ status: "drained", popped });
86963
87170
  if (d.kind === "flood") {
86964
87171
  consecutiveFloods++;
86965
87172
  if (lastRetryAfter === d.seconds)
@@ -86968,30 +87175,31 @@ function createStalePinSweeper(deps) {
86968
87175
  noProgressAtSameRetryAfter = 1;
86969
87176
  lastRetryAfter = d.seconds;
86970
87177
  if (consecutiveFloods >= MAX_CONSECUTIVE_FLOOD_WAITS || noProgressAtSameRetryAfter >= MAX_IDENTICAL_RETRY_AFTER_NO_PROGRESS) {
86971
- return {
87178
+ return out({
86972
87179
  status: "deferred-flood",
86973
87180
  popped,
86974
87181
  detail: `flood: ${consecutiveFloods} consecutive 429s, retry_after=${d.seconds}s`
86975
- };
87182
+ });
86976
87183
  }
86977
87184
  await deps.sleep(d.seconds * 1000 * Math.pow(FLOOD_BACKOFF_FACTOR, consecutiveFloods - 1));
86978
87185
  continue;
86979
87186
  }
86980
- return { status: "error", popped, detail: d.detail };
87187
+ return out({ status: "error", popped, detail: d.detail });
86981
87188
  }
86982
87189
  consecutiveFloods = 0;
87190
+ issued++;
86983
87191
  unverifiedPop = true;
86984
87192
  }
86985
87193
  const finalRead = await readTopVerified(target.chatId);
86986
87194
  if (unverifiedPop && finalRead != null && finalRead.top !== lastObservedTop)
86987
87195
  creditPop();
86988
87196
  if (finalRead != null && finalRead.top == null)
86989
- return { status: "drained", popped };
86990
- return {
87197
+ return out({ status: "drained", popped });
87198
+ return out({
86991
87199
  status: "deferred-budget",
86992
87200
  popped,
86993
87201
  detail: `hit the ${gate.maxPopsPerChatPerSweep}-pop cap for this chat; yielding to next boot`
86994
- };
87202
+ });
86995
87203
  };
86996
87204
  const targetedUnpinDrain = async (target, kind, cursor, ids) => {
86997
87205
  const alreadyDone = new Set(cursor.doneIds ?? []);
@@ -87088,10 +87296,21 @@ function createStalePinSweeper(deps) {
87088
87296
  const forumTopicDrain = async (target, cursor) => {
87089
87297
  const threadId = target.threadId;
87090
87298
  if (threadId == null) {
87091
- return { status: "skipped-nothing-recorded", popped: 0, detail: "no message_thread_id" };
87299
+ return {
87300
+ status: "skipped-nothing-recorded",
87301
+ popped: 0,
87302
+ issued: 0,
87303
+ detail: "no message_thread_id"
87304
+ };
87092
87305
  }
87306
+ const before = await readTopVerified(target.chatId);
87093
87307
  if (!await awaitWriteSlot("forum-topic")) {
87094
- return { status: "deferred-budget", popped: 0, detail: "per-minute pin-op budget spent" };
87308
+ return {
87309
+ status: "deferred-budget",
87310
+ popped: 0,
87311
+ issued: 0,
87312
+ detail: "per-minute pin-op budget spent"
87313
+ };
87095
87314
  }
87096
87315
  try {
87097
87316
  await deps.unpinAllForumTopicMessages(target.chatId, threadId);
@@ -87099,39 +87318,59 @@ function createStalePinSweeper(deps) {
87099
87318
  const d = classify(err);
87100
87319
  if (d.kind === "breaker") {
87101
87320
  circuitOpen = true;
87102
- return { status: "aborted-circuit-breaker", popped: 0, detail: d.detail };
87321
+ return { status: "aborted-circuit-breaker", popped: 0, issued: 0, detail: d.detail };
87103
87322
  }
87104
87323
  if (d.kind === "rights")
87105
- return { status: "skipped-no-rights", popped: 0 };
87324
+ return { status: "skipped-no-rights", popped: 0, issued: 0 };
87106
87325
  if (d.kind === "flood") {
87107
87326
  return {
87108
87327
  status: "deferred-flood",
87109
87328
  popped: 0,
87329
+ issued: 0,
87110
87330
  detail: `unpinAllForumTopicMessages pegged at retry_after=${d.seconds}s; not looping`
87111
87331
  };
87112
87332
  }
87113
- return { status: "error", popped: 0, detail: d.detail };
87333
+ return { status: "error", popped: 0, issued: 0, detail: d.detail };
87114
87334
  }
87115
- cursor.popped++;
87116
- cursor.updatedAt = deps.now();
87117
- commit(cursor);
87118
87335
  const observed = await readTopVerified(target.chatId);
87119
- if (observed != null && observed.top == null)
87120
- return { status: "drained", popped: 1 };
87121
- return { status: "drained", popped: 1, detail: "topic drained; chat-wide stack may hold other topics" };
87336
+ const topMoved = before != null && observed != null && observed.top !== before.top ? 1 : 0;
87337
+ if (topMoved > 0) {
87338
+ cursor.popped += topMoved;
87339
+ cursor.updatedAt = deps.now();
87340
+ commit(cursor);
87341
+ }
87342
+ if (observed != null && observed.top == null) {
87343
+ return { status: "drained", popped: topMoved, issued: 1 };
87344
+ }
87345
+ return {
87346
+ status: "drained",
87347
+ popped: topMoved,
87348
+ issued: 1,
87349
+ detail: "topic drained; chat-wide stack may hold other topics"
87350
+ };
87122
87351
  };
87123
87352
  const sweepOnce = async (target) => {
87124
87353
  if (!deps.eligible())
87125
- return { status: "skipped-not-eligible", popped: 0 };
87354
+ return { status: "skipped-not-eligible", popped: 0, issued: 0 };
87126
87355
  if (circuitOpen) {
87127
- return { status: "aborted-circuit-breaker", popped: 0, detail: "breaker already open" };
87356
+ return {
87357
+ status: "aborted-circuit-breaker",
87358
+ popped: 0,
87359
+ issued: 0,
87360
+ detail: "breaker already open"
87361
+ };
87128
87362
  }
87129
87363
  const kind = classifyChatForSweep(target);
87130
87364
  const cursor = loadCursor(target, kind);
87131
87365
  if (cursor.done)
87132
- return { status: "already-drained", popped: 0 };
87366
+ return { status: "already-drained", popped: 0, issued: 0 };
87133
87367
  if (cursor.attempts >= SWEEP_MAX_ATTEMPTS) {
87134
- return { status: "forfeited", popped: 0, detail: `${cursor.attempts} attempts exhausted` };
87368
+ return {
87369
+ status: "forfeited",
87370
+ popped: 0,
87371
+ issued: 0,
87372
+ detail: `${cursor.attempts} attempts exhausted`
87373
+ };
87135
87374
  }
87136
87375
  if (kind !== "dm") {
87137
87376
  let allowed = false;
@@ -87145,7 +87384,7 @@ function createStalePinSweeper(deps) {
87145
87384
  cursor.lastStatus = "skipped-no-rights";
87146
87385
  cursor.updatedAt = deps.now();
87147
87386
  commit(cursor);
87148
- return { status: "skipped-no-rights", popped: 0 };
87387
+ return { status: "skipped-no-rights", popped: 0, issued: 0 };
87149
87388
  }
87150
87389
  }
87151
87390
  cursor.attempts++;
@@ -87174,7 +87413,7 @@ function createStalePinSweeper(deps) {
87174
87413
  } : await targetedUnpinDrain(target, kind, cursor, ids);
87175
87414
  }
87176
87415
  } catch (err) {
87177
- result = { status: "error", popped: 0, detail: err.message };
87416
+ result = { status: "error", popped: 0, issued: 0, detail: err.message };
87178
87417
  }
87179
87418
  if (result.status === "drained" || result.status === "skipped-nothing-recorded") {
87180
87419
  cursor.done = true;
@@ -87207,10 +87446,12 @@ function createStalePinSweeper(deps) {
87207
87446
  const running = inFlight.get(key);
87208
87447
  if (running != null)
87209
87448
  return running;
87210
- if (!deps.eligible())
87211
- return Promise.resolve({ status: "skipped-not-eligible", popped: 0 });
87212
- if (attempted.has(key))
87213
- return Promise.resolve({ status: "already-attempted", popped: 0 });
87449
+ if (!deps.eligible()) {
87450
+ return Promise.resolve({ status: "skipped-not-eligible", popped: 0, issued: 0 });
87451
+ }
87452
+ if (attempted.has(key)) {
87453
+ return Promise.resolve({ status: "already-attempted", popped: 0, issued: 0 });
87454
+ }
87214
87455
  attempted.add(key);
87215
87456
  const p = sweepOnce(target).finally(() => inFlight.delete(key));
87216
87457
  inFlight.set(key, p);
@@ -90115,7 +90356,7 @@ function createCapturedResumeDispatcher(ports) {
90115
90356
 
90116
90357
  // gateway/status-pin-store.ts
90117
90358
  var BOOT_UNPIN_MAX_ATTEMPTS = 5;
90118
- var SNAPSHOT_VERSIONS2 = new Set([1, 2, 3, 4]);
90359
+ var SNAPSHOT_VERSION2 = 4;
90119
90360
  function isPinRow2(x) {
90120
90361
  if (x == null || typeof x !== "object")
90121
90362
  return false;
@@ -90140,12 +90381,12 @@ function loadStatusPins2(path3, fs3) {
90140
90381
  if (parsed == null || typeof parsed !== "object")
90141
90382
  return [];
90142
90383
  const env = parsed;
90143
- if (typeof env.v !== "number" || !SNAPSHOT_VERSIONS2.has(env.v) || !Array.isArray(env.pins))
90384
+ if (!Number.isInteger(env.v) || env.v < 1 || !Array.isArray(env.pins))
90144
90385
  return [];
90145
90386
  return env.pins.filter(isPinRow2);
90146
90387
  }
90147
90388
  function persistStatusPins2(path3, fs3, snapshot, log = (l) => process.stderr.write(l)) {
90148
- const env = { v: 4, pins: [...snapshot] };
90389
+ const env = { v: SNAPSHOT_VERSION2, pins: [...snapshot] };
90149
90390
  const tmp = path3 + ".tmp";
90150
90391
  try {
90151
90392
  fs3.writeFileSync(tmp, JSON.stringify(env));
@@ -96446,6 +96687,12 @@ function parseAuditLine(line) {
96446
96687
  entry.m = o.m;
96447
96688
  if (typeof o.prior_pin === "string")
96448
96689
  entry.prior_pin = o.prior_pin;
96690
+ if (Array.isArray(o.prior_pin_observed) && o.prior_pin_observed.every((v) => typeof v === "string")) {
96691
+ entry.prior_pin_observed = o.prior_pin_observed;
96692
+ }
96693
+ if (typeof o.prior_pin_source === "string") {
96694
+ entry.prior_pin_source = o.prior_pin_source;
96695
+ }
96449
96696
  return entry;
96450
96697
  }
96451
96698
  function filterEntries(entries, filters) {
@@ -99070,10 +99317,10 @@ function startOutboxSweep(deps) {
99070
99317
  }
99071
99318
 
99072
99319
  // ../src/build-info.ts
99073
- var VERSION2 = "0.19.31";
99074
- var COMMIT_SHA = "d24ee3cc";
99075
- var COMMIT_DATE = "2026-07-28T23:06:42Z";
99076
- var LATEST_PR = 3961;
99320
+ var VERSION2 = "0.19.33";
99321
+ var COMMIT_SHA = "4ccbde6b";
99322
+ var COMMIT_DATE = "2026-07-29T02:39:39Z";
99323
+ var LATEST_PR = 3973;
99077
99324
  var COMMITS_AHEAD_OF_TAG = 0;
99078
99325
 
99079
99326
  // gateway/boot-version.ts