instar 1.3.675 → 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.
Files changed (37) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +107 -9
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/config/ConfigDefaults.d.ts.map +1 -1
  5. package/dist/config/ConfigDefaults.js +6 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/MessagingToneGate.d.ts +9 -0
  8. package/dist/core/MessagingToneGate.d.ts.map +1 -1
  9. package/dist/core/MessagingToneGate.js +11 -1
  10. package/dist/core/MessagingToneGate.js.map +1 -1
  11. package/dist/core/SpawnCapIntelligenceProvider.d.ts +23 -0
  12. package/dist/core/SpawnCapIntelligenceProvider.d.ts.map +1 -1
  13. package/dist/core/SpawnCapIntelligenceProvider.js +77 -8
  14. package/dist/core/SpawnCapIntelligenceProvider.js.map +1 -1
  15. package/dist/core/SpawningTopicsRegistry.d.ts +65 -0
  16. package/dist/core/SpawningTopicsRegistry.d.ts.map +1 -0
  17. package/dist/core/SpawningTopicsRegistry.js +75 -0
  18. package/dist/core/SpawningTopicsRegistry.js.map +1 -0
  19. package/dist/core/hostSpawnSemaphore.d.ts +63 -1
  20. package/dist/core/hostSpawnSemaphore.d.ts.map +1 -1
  21. package/dist/core/hostSpawnSemaphore.js +69 -5
  22. package/dist/core/hostSpawnSemaphore.js.map +1 -1
  23. package/dist/core/types.d.ts +23 -0
  24. package/dist/core/types.d.ts.map +1 -1
  25. package/dist/core/types.js.map +1 -1
  26. package/dist/monitoring/TopicReachabilityVerifier.d.ts +92 -0
  27. package/dist/monitoring/TopicReachabilityVerifier.d.ts.map +1 -0
  28. package/dist/monitoring/TopicReachabilityVerifier.js +173 -0
  29. package/dist/monitoring/TopicReachabilityVerifier.js.map +1 -0
  30. package/dist/server/routes.d.ts.map +1 -1
  31. package/dist/server/routes.js +4 -0
  32. package/dist/server/routes.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/data/builtin-manifest.json +46 -46
  35. package/upgrades/1.3.677.md +78 -0
  36. package/upgrades/side-effects/spawn-cap-interactive-priority.md +76 -0
  37. package/upgrades/side-effects/verify-after-reachability.md +65 -0
@@ -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,CAsmhBtE;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
  }
@@ -2751,10 +2762,17 @@ export async function startServer(options) {
2751
2762
  // so every `claude -p`/`codex exec` spawn this process makes is bounded. A
2752
2763
  // safety FLOOR — read with a plain `??` default (env > config > 8), never the
2753
2764
  // dev-agent gate; it ships ON for every agent, never dark.
2765
+ // F5 interactive-priority reservation: `enabled` is OMITTED from ConfigDefaults so
2766
+ // it rides the dev-agent gate (live-on-dev / dark-fleet). The reservation only
2767
+ // SUBDIVIDES within the safety cap above — it never raises it. When off, acquire is
2768
+ // byte-identical to the all-or-nothing cap.
2769
+ const _ipCfg = config.intelligence?.spawnCap?.interactivePriority;
2770
+ const _ipEnabled = resolveDevAgentGate(_ipCfg?.enabled, config);
2754
2771
  configureHostSpawnSemaphore({
2755
2772
  maxConcurrent: config.intelligence?.spawnCap?.maxConcurrent,
2756
2773
  acquireMs: config.intelligence?.spawnCap?.acquireMs,
2757
2774
  waitersMax: config.intelligence?.spawnCap?.waitersMax,
2775
+ interactivePriority: { enabled: _ipEnabled, ri: _ipCfg?.ri ?? 2, rb: _ipCfg?.rb ?? 2 },
2758
2776
  });
2759
2777
  // LiveConfig: dynamic config re-reading for long-running server process.
2760
2778
  // Solves the "Written But Not Re-Read" class of bugs — sessions modify
@@ -7625,7 +7643,87 @@ export async function startServer(options) {
7625
7643
  }
7626
7644
  }
7627
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
+ }
7628
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
+ }
7629
7727
  // ── GAP-B Part B (spec: autonomous-registration-guarantee.md) — the
7630
7728
  // commitment-evidence BACKSTOP for an UNregistered-but-working autonomous
7631
7729
  // run. Computed ONCE here (cheap, fail-open) so its verdict feeds BOTH the