instar 1.3.688 → 1.3.690

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 (34) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +106 -0
  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 +11 -0
  6. package/dist/config/ConfigDefaults.js.map +1 -1
  7. package/dist/core/SessionManager.d.ts.map +1 -1
  8. package/dist/core/SessionManager.js +8 -0
  9. package/dist/core/SessionManager.js.map +1 -1
  10. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  11. package/dist/core/devGatedFeatures.js +5 -0
  12. package/dist/core/devGatedFeatures.js.map +1 -1
  13. package/dist/core/leaseGatedSpawn.d.ts +232 -0
  14. package/dist/core/leaseGatedSpawn.d.ts.map +1 -0
  15. package/dist/core/leaseGatedSpawn.js +216 -0
  16. package/dist/core/leaseGatedSpawn.js.map +1 -0
  17. package/dist/core/nobodyPollingRecovery.d.ts +138 -0
  18. package/dist/core/nobodyPollingRecovery.d.ts.map +1 -0
  19. package/dist/core/nobodyPollingRecovery.js +147 -0
  20. package/dist/core/nobodyPollingRecovery.js.map +1 -0
  21. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  22. package/dist/server/CapabilityIndex.js +1 -0
  23. package/dist/server/CapabilityIndex.js.map +1 -1
  24. package/dist/server/routes.d.ts.map +1 -1
  25. package/dist/server/routes.js +95 -0
  26. package/dist/server/routes.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/data/builtin-manifest.json +47 -47
  29. package/upgrades/1.3.689.md +30 -0
  30. package/upgrades/1.3.690.md +28 -0
  31. package/upgrades/side-effects/mesh-self-heal-g2-core.md +32 -0
  32. package/upgrades/side-effects/mesh-self-heal-g2-observe.md +34 -0
  33. package/upgrades/side-effects/mesh-self-heal-g3.md +45 -0
  34. package/upgrades/side-effects/mesh-self-heal-spec.md +37 -0
@@ -8,9 +8,11 @@ import { Router } from 'express';
8
8
  import { execFileSync } from 'node:child_process';
9
9
  import { createHash, timingSafeEqual, randomUUID } from 'node:crypto';
10
10
  import { classifyActionClaim } from '../core/action-claim.js';
11
+ import { sharedG3SoakLedger, decideLeaseGatedSpawn } from '../core/leaseGatedSpawn.js';
11
12
  import { getHostSpawnSemaphore, configuredSpawnAcquireMs, configuredSpawnWaitersMax } from '../core/hostSpawnSemaphore.js';
12
13
  import { activeSpawnPollers } from '../core/SpawnCapIntelligenceProvider.js';
13
14
  import { poolPollerVerdict } from '../core/pollerCount.js';
15
+ import { decideNobodyPollingClaim, sharedG2NobodyPollingLedger } from '../core/nobodyPollingRecovery.js';
14
16
  import { canonicalPushKey } from '../core/PrHandLease.js';
15
17
  import { enrollPaneSessionName } from '../core/FrameworkLoginDriver.js';
16
18
  import fs from 'node:fs';
@@ -4561,6 +4563,21 @@ export function createRoutes(ctx) {
4561
4563
  }
4562
4564
  res.json(ctx.sessionReaper.snapshot());
4563
4565
  });
4566
+ // G3 lease-gated-spawn soak evidence (MESH-SELF-HEAL-SPEC §3.3). The operator's
4567
+ // PROMOTION-EVIDENCE surface — the answer to "is this dark feature actually
4568
+ // doing anything, and should I promote it?" (operator directive 2026-06-27:
4569
+ // observe-mode must produce an evaluable promotion recommendation, not rot
4570
+ // dark forever). `summary` carries the counterfactual `wouldHavePreventedDuplicate`;
4571
+ // `promotion` is the deterministic recommendation (promote | keep-soaking |
4572
+ // consider-removal | enforcing). Read-only; never gates.
4573
+ router.get('/mesh-selfheal/g3', (_req, res) => {
4574
+ res.json({
4575
+ feature: 'lease-gated-spawn',
4576
+ flag: 'multiMachine.sessionPool.ownershipCheckedSpawn',
4577
+ summary: sharedG3SoakLedger.summary(),
4578
+ promotion: sharedG3SoakLedger.promotionSignal(),
4579
+ });
4580
+ });
4564
4581
  // AgentWorktreeReaper (RESPONSIBLE-RESOURCE-USAGE — OS resource hygiene). The
4565
4582
  // pull-surface answer to "which stale worktrees can be reclaimed, and why is
4566
4583
  // each kept?": every `.worktrees/` worktree's verdict (active-lock /
@@ -11389,6 +11406,56 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
11389
11406
  machines: caps.map((c) => ({ machineId: c.machineId, nickname: c.nickname, online: c.online, pollingActive: c.pollingActive })),
11390
11407
  });
11391
11408
  });
11409
+ // G2 nobody-polling silence debounce — module-scoped streak across reads, so a
11410
+ // transient handoff-gap silence never reads as confirmed (Adv-F9).
11411
+ const G2_NOBODY_POLLING_CONFIRM_OBSERVATIONS = 3;
11412
+ let _g2SilenceStreak = 0;
11413
+ // GET /mesh-selfheal/g2 — MESH-SELF-HEAL-SPEC §3.2 G2 OBSERVE surface (increment
11414
+ // 2: detector + single-claimant election, REPORT-only). Computes the B5 verdict
11415
+ // over the live pool, debounces a `silence` across reads (nobodyPollingConfirmObservations),
11416
+ // runs the pure G2 decision (who should claim poll-ownership), records the
11417
+ // counterfactual to the shared soak ledger, and returns it. OBSERVE-only: it
11418
+ // does NOT actuate (no fenced-CAS acquire, no poll-lever write) — the enforce
11419
+ // actuation is the next increment. Read-driven: each poll advances the soak.
11420
+ router.get('/mesh-selfheal/g2', (_req, res) => {
11421
+ const caps = ctx.machinePoolRegistry?.getCapacities() ?? [];
11422
+ const sync = ctx.coordinator ? ctx.coordinator.getSyncStatus() : null;
11423
+ const selfMachineId = ctx.coordinator?.identity?.machineId ?? null;
11424
+ const verdict = poolPollerVerdict(caps.map((c) => ({ machineId: c.machineId, online: c.online, pollingActive: c.pollingActive })), false);
11425
+ // Debounce a silence across reads (Adv-F9: a normal handoff gap is transient
11426
+ // and must not trip a claim). Module-scoped streak counter.
11427
+ if (verdict.verdict === 'silence')
11428
+ _g2SilenceStreak += 1;
11429
+ else
11430
+ _g2SilenceStreak = 0;
11431
+ const silenceConfirmed = _g2SilenceStreak >= G2_NOBODY_POLLING_CONFIRM_OBSERVATIONS;
11432
+ // fit (observe approximation): heartbeat-fresh = a candidate that COULD take
11433
+ // over polling. In a `silence` NOBODY is polling, so fitness is NOT tied to
11434
+ // pollingActive (that would exclude exactly the idle-but-healthy machines that
11435
+ // should claim). The enforce increment refines this with the machine-local
11436
+ // pollSucceededMonoMs re-verify (post-CAS) + self-exclusion advertisement.
11437
+ const machines = caps.map((c) => ({ machineId: c.machineId, fit: !!c.online }));
11438
+ const decision = decideNobodyPollingClaim({
11439
+ selfMachineId: selfMachineId ?? '(unknown)',
11440
+ pollerVerdict: verdict.verdict,
11441
+ silenceConfirmed,
11442
+ preferredAwakeMachineId: sync?.preferredAwakeMachineId ?? null,
11443
+ machines,
11444
+ // Peer-evidence-of-global-outage plumbing is the enforce increment; observe
11445
+ // never claims a global outage (false → a confirmed silence proceeds to elect).
11446
+ globalOutageEvidence: false,
11447
+ });
11448
+ sharedG2NobodyPollingLedger.recordClaim(decision, new Date().toISOString());
11449
+ res.json({
11450
+ enabled: !!ctx.machinePoolRegistry,
11451
+ verdict,
11452
+ silenceStreak: _g2SilenceStreak,
11453
+ silenceConfirmed,
11454
+ selfMachineId,
11455
+ decision,
11456
+ ledger: sharedG2NobodyPollingLedger.summary(),
11457
+ });
11458
+ });
11392
11459
  // GET /pool/poll-cache — WS4.4(f) global pool-cache observability
11393
11460
  // (MULTI-MACHINE-SEAMLESSNESS-SPEC §WS4.4 clause (f)). Read-only: reports
11394
11461
  // whether the shared per-peer poll cache is wired, its TTL + load-shed
@@ -14645,6 +14712,34 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
14645
14712
  if (resumeSessionId) {
14646
14713
  console.log(`[telegram-forward] Found resume UUID for topic ${topicId}: ${resumeSessionId} (source: TopicResumeMap — trusted)`);
14647
14714
  }
14715
+ // G3 — record the lease-gated-spawn decision for this SECONDARY spawn
14716
+ // entry (the /internal/telegram-forward auto-spawn). This route is
14717
+ // same-machine-only (injectionDropped respawn @server.ts + the lifeline
14718
+ // poll) — cross-machine delivery bypasses it via MeshRpc→durable queue —
14719
+ // so there is NO forward seam here and gating can never ping-pong.
14720
+ // forwardAvailable:false makes this RECORD-ONLY: it always spawns
14721
+ // (never strands a local re-entry), but feeds the shared soak ledger so
14722
+ // the promotion evidence covers BOTH spawn entries, not just the cold
14723
+ // path. Dark+dryRun by default ⇒ strict no-op on behavior.
14724
+ try {
14725
+ const o = ctx.config?.multiMachine?.sessionPool?.ownershipCheckedSpawn ?? {};
14726
+ const g3 = decideLeaseGatedSpawn({
14727
+ holdsLease: ctx.coordinator ? ctx.coordinator.holdsLease() : true,
14728
+ flagEnabled: o.enabled === true,
14729
+ dryRun: o.dryRun !== false,
14730
+ singleMachine: ctx.coordinator === null,
14731
+ forwardAvailable: false,
14732
+ });
14733
+ sharedG3SoakLedger.record(g3, new Date().toISOString());
14734
+ if (g3.action !== 'spawn') {
14735
+ console.log(`[g3-spawn-gate] (telegram-forward) topic ${topicId}: action=${g3.action} reason=${g3.reason} (record-only — local re-entry, no forward seam)`);
14736
+ }
14737
+ }
14738
+ catch (err) {
14739
+ // @silent-fallback-ok — the soak record is observability only; a record
14740
+ // failure must never block the spawn (the safe direction).
14741
+ console.warn(`[g3-spawn-gate] (telegram-forward) record failed (non-fatal): ${err instanceof Error ? err.message : String(err)}`);
14742
+ }
14648
14743
  ctx.sessionManager.spawnInteractiveSession(bootstrapMessage, topicName, { telegramTopicId: topicId, resumeSessionId }).then((newSessionName) => {
14649
14744
  // Clear resume entry after successful spawn
14650
14745
  if (resumeSessionId) {