instar 1.3.676 → 1.3.677

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4CH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAwH7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA+EtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAy4CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CA8eN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA6mhBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA8CH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAwH7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AA+EtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA84CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CAmfN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAyrhBtE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
@@ -22,6 +22,8 @@ import { loadConfig, ensureStateDir, detectTmuxPath, detectGeminiPath } from '..
22
22
  import { handleProcessLevelError } from '../core/uncaughtExceptionPolicy.js';
23
23
  import { planInboundLossNotices } from '../core/inboundLossRouting.js';
24
24
  import { configureHostSpawnSemaphore } from '../core/hostSpawnSemaphore.js';
25
+ import { SpawningTopicsRegistry } from '../core/SpawningTopicsRegistry.js';
26
+ import { TopicReachabilityVerifier } from '../monitoring/TopicReachabilityVerifier.js';
25
27
  import { SingleInstanceLock, installReleaseHandlers } from '../core/SingleInstanceLock.js';
26
28
  import { resolveDevAgentGate, resolveStateSyncStores } from '../core/devAgentGate.js';
27
29
  import { shouldReleaseOnComplete, planClaimOnSpawn, ownershipNonce } from '../core/ownershipFollowsLiveWork.js';
@@ -440,6 +442,10 @@ let _fixDeps = null;
440
442
  // server's store at message-time — the server is constructed long after
441
443
  // routing is wired, and the store must be the server's OWN instance.
442
444
  let _agentServerRef = null;
445
+ // F7: late-bound ref to wireTelegramRouting's spawning-topics registry so the
446
+ // TopicReachabilityVerifier (constructed in the outer scope) can probe whether a topic
447
+ // is stuck-spawning. Assigned inside wireTelegramRouting; null until routing is wired.
448
+ let _spawningTopicsRegistryRef = null;
443
449
  // Module-level reference for session resume mapping.
444
450
  // Set once in startServer() and used by spawnSessionForTopic/respawnSessionForTopic.
445
451
  let _topicResumeMap = null;
@@ -1731,7 +1737,12 @@ getAttentionTopicId) {
1731
1737
  // Guard: tracks which topic IDs have a spawn in progress.
1732
1738
  // Prevents duplicate concurrent spawns for the same topic when messages
1733
1739
  // arrive faster than the async spawn completes.
1734
- const spawningTopics = new Set();
1740
+ // F7 Piece 1: token-tagged spawn-guard. `has` guards double-spawn (as the old Set
1741
+ // did); `add` returns a token and `clear` is token-guarded so a late `.finally` from
1742
+ // a superseded spawn cannot delete a newer entry (the ABA fix). A hung spawn keeps its
1743
+ // entry (no timeout/sweep) so the verifier can SURFACE it — never auto-cleared.
1744
+ const spawningTopics = new SpawningTopicsRegistry();
1745
+ _spawningTopicsRegistryRef = spawningTopics; // F7: expose to the outer-scope verifier
1735
1746
  telegram.onTopicMessage = async (msg) => {
1736
1747
  const topicId = msg.metadata?.messageThreadId ?? null;
1737
1748
  if (!topicId)
@@ -2032,7 +2043,7 @@ getAttentionTopicId) {
2032
2043
  console.log(`[telegram→session] Spawn already in progress for topic ${topicId} — skipping duplicate respawn`);
2033
2044
  return;
2034
2045
  }
2035
- spawningTopics.add(topicId);
2046
+ const _spawnTokA = spawningTopics.add(topicId);
2036
2047
  // Remove the resume UUID so respawnSessionForTopic doesn't try --resume
2037
2048
  if (_topicResumeMap) {
2038
2049
  _topicResumeMap.remove(topicId);
@@ -2043,7 +2054,7 @@ getAttentionTopicId) {
2043
2054
  telegram.sendToTopic(topicId, `❌ Fresh session restart failed. Try sending your message again.`).catch(() => { });
2044
2055
  })
2045
2056
  .finally(() => {
2046
- spawningTopics.delete(topicId);
2057
+ spawningTopics.clear(topicId, _spawnTokA);
2047
2058
  });
2048
2059
  }
2049
2060
  else if (!isQuotaDeath) {
@@ -2054,7 +2065,7 @@ getAttentionTopicId) {
2054
2065
  console.log(`[telegram→session] Spawn already in progress for topic ${topicId} — skipping duplicate respawn`);
2055
2066
  return;
2056
2067
  }
2057
- spawningTopics.add(topicId);
2068
+ const _spawnTokB = spawningTopics.add(topicId);
2058
2069
  telegram.sendToTopic(topicId, `🔄 Session restarting — message queued.`).catch(() => { });
2059
2070
  respawnSessionForTopic(sessionManager, telegram, targetSession, topicId, text, topicMemory, resolvedUser ?? undefined)
2060
2071
  .catch(err => {
@@ -2066,7 +2077,7 @@ getAttentionTopicId) {
2066
2077
  telegram.sendToTopic(topicId, userMsg).catch(() => { });
2067
2078
  })
2068
2079
  .finally(() => {
2069
- spawningTopics.delete(topicId);
2080
+ spawningTopics.clear(topicId, _spawnTokB);
2070
2081
  });
2071
2082
  }
2072
2083
  }
@@ -2081,7 +2092,7 @@ getAttentionTopicId) {
2081
2092
  console.log(`[telegram→session] Spawn already in progress for topic ${topicId} — skipping duplicate`);
2082
2093
  return;
2083
2094
  }
2084
- spawningTopics.add(topicId);
2095
+ const _spawnTokC = spawningTopics.add(topicId);
2085
2096
  // Resolve topic name — try in-memory, then active probe, then fallback
2086
2097
  let spawnName = storedTopicName;
2087
2098
  if (!spawnName || /^topic-\d+$/.test(spawnName)) {
@@ -2104,7 +2115,7 @@ getAttentionTopicId) {
2104
2115
  : 'Having trouble starting a session right now. Try sending your message again in a moment.';
2105
2116
  telegram.sendToTopic(topicId, userMsg).catch(() => { });
2106
2117
  }).finally(() => {
2107
- spawningTopics.delete(topicId);
2118
+ spawningTopics.clear(topicId, _spawnTokC);
2108
2119
  });
2109
2120
  }
2110
2121
  };
@@ -2195,7 +2206,7 @@ getAttentionTopicId) {
2195
2206
  return { kind: 'handover-refused' };
2196
2207
  if (handover.stopRecheck())
2197
2208
  return { kind: 'stopped-before-inject' };
2198
- spawningTopics.add(topicId);
2209
+ const _spawnTokD = spawningTopics.add(topicId);
2199
2210
  try {
2200
2211
  if (targetSession) {
2201
2212
  // Dead session — respawn, AWAITED through the spawn+inject (the PIS
@@ -2214,7 +2225,7 @@ getAttentionTopicId) {
2214
2225
  return { kind: 'local-delivered', injectError: err instanceof Error ? err.message : String(err) };
2215
2226
  }
2216
2227
  finally {
2217
- spawningTopics.delete(topicId);
2228
+ spawningTopics.clear(topicId, _spawnTokD);
2218
2229
  }
2219
2230
  };
2220
2231
  }
@@ -7632,7 +7643,87 @@ export async function startServer(options) {
7632
7643
  }
7633
7644
  }
7634
7645
  }
7646
+ // ── F7 Verify-After Topic Reachability (PURE SIGNAL, dev-gated dark/fleet) ──
7647
+ // After a destructive mutation it verifies the topic is still inbound-reachable and
7648
+ // SURFACES a genuine orphan (one NORMAL attention item). It mutates NOTHING. The
7649
+ // probe is conservative + fail-safe: a live session ⇒ reachable; a topic stuck-
7650
+ // spawning past stuckSpawnMs (the registry seam) ⇒ orphan; anything else ⇒ reachable
7651
+ // (the next inbound auto-spawns — the self-heal), so it never false-orphans an idle kill.
7652
+ const _trvCfg = config.monitoring?.topicReachabilityVerifier;
7653
+ const _trvEnabled = resolveDevAgentGate(_trvCfg?.enabled, config);
7654
+ const _stuckSpawnMs = _trvCfg?.stuckSpawnMs ?? 180_000;
7655
+ const topicReachabilityVerifier = _trvEnabled
7656
+ ? new TopicReachabilityVerifier({
7657
+ now: () => Date.now(),
7658
+ graceMs: _trvCfg?.graceMs ?? 30_000,
7659
+ probe: (topic) => {
7660
+ try {
7661
+ const session = telegram?.getSessionForTopic(topic);
7662
+ if (session && sessionManager.isSessionAlive(session))
7663
+ return { reachable: true };
7664
+ const stuckMs = _spawningTopicsRegistryRef?.stuckSinceMs(topic);
7665
+ if (stuckMs !== undefined && stuckMs >= _stuckSpawnMs)
7666
+ return { reachable: false, reason: 'stuck-spawn' };
7667
+ return { reachable: true }; // next inbound auto-spawns (self-heal)
7668
+ }
7669
+ catch {
7670
+ return { reachable: true }; // fail-safe: never false-orphan on a probe error
7671
+ }
7672
+ },
7673
+ surface: (item) => {
7674
+ void telegram
7675
+ ?.createAttentionItem({
7676
+ id: item.key,
7677
+ title: item.rolledUp ? 'Some conversations may be unreachable' : 'A conversation may be unreachable',
7678
+ summary: item.reason,
7679
+ category: 'reachability',
7680
+ priority: 'NORMAL',
7681
+ sourceContext: 'topic-reachability',
7682
+ })
7683
+ .catch(() => { });
7684
+ },
7685
+ pressureCritical: () => {
7686
+ try {
7687
+ return sessionManager.pressureTier?.() === 'critical';
7688
+ }
7689
+ catch {
7690
+ return false;
7691
+ }
7692
+ },
7693
+ emergencyStopActive: () => false, // wired to the operator-halt signal in a follow-up
7694
+ })
7695
+ : null;
7696
+ if (topicReachabilityVerifier) {
7697
+ const _trvTick = setInterval(() => {
7698
+ try {
7699
+ topicReachabilityVerifier.tick();
7700
+ }
7701
+ catch {
7702
+ /* @silent-fallback-ok: a verifier tick error must never crash the server. */
7703
+ }
7704
+ }, 15_000);
7705
+ _trvTick.unref?.();
7706
+ guardRegistry.register('monitoring.topicReachabilityVerifier.enabled', () => ({
7707
+ enabled: true,
7708
+ ...topicReachabilityVerifier.status(),
7709
+ }));
7710
+ }
7635
7711
  sessionManager.on('sessionReaped', (e) => {
7712
+ // ── F7: schedule a post-grace reachability verify for the affected topic. Both a
7713
+ // terminal kill AND a recovery-bounce schedule one (a recovery whose respawn
7714
+ // wedges is exactly the orphan to catch); the verifier's grace lets a normal
7715
+ // self-heal land first, so a healthy bounce never surfaces. Pure signal. ──
7716
+ if (topicReachabilityVerifier) {
7717
+ try {
7718
+ const rawTrv = telegram?.getTopicForSession(e.session.tmuxSession);
7719
+ const trvTopic = rawTrv == null ? null : Number.isFinite(Number(rawTrv)) ? Number(rawTrv) : null;
7720
+ if (trvTopic != null)
7721
+ topicReachabilityVerifier.recordMutation(trvTopic);
7722
+ }
7723
+ catch {
7724
+ /* @silent-fallback-ok: a verifier trigger error must never affect the reap. */
7725
+ }
7726
+ }
7636
7727
  // ── GAP-B Part B (spec: autonomous-registration-guarantee.md) — the
7637
7728
  // commitment-evidence BACKSTOP for an UNregistered-but-working autonomous
7638
7729
  // run. Computed ONCE here (cheap, fail-open) so its verdict feeds BOTH the