instar 1.3.272 → 1.3.274

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 (33) hide show
  1. package/dist/commands/server.d.ts.map +1 -1
  2. package/dist/commands/server.js +70 -0
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/MachinePoolRegistry.d.ts +3 -0
  5. package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
  6. package/dist/core/MachinePoolRegistry.js +1 -0
  7. package/dist/core/MachinePoolRegistry.js.map +1 -1
  8. package/dist/core/PlacementExecutor.d.ts.map +1 -1
  9. package/dist/core/PlacementExecutor.js +28 -6
  10. package/dist/core/PlacementExecutor.js.map +1 -1
  11. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  12. package/dist/core/PostUpdateMigrator.js +26 -1
  13. package/dist/core/PostUpdateMigrator.js.map +1 -1
  14. package/dist/core/SessionManager.d.ts.map +1 -1
  15. package/dist/core/SessionManager.js +7 -3
  16. package/dist/core/SessionManager.js.map +1 -1
  17. package/dist/core/types.d.ts +21 -0
  18. package/dist/core/types.d.ts.map +1 -1
  19. package/dist/core/types.js.map +1 -1
  20. package/dist/monitoring/SessionReaper.d.ts +19 -0
  21. package/dist/monitoring/SessionReaper.d.ts.map +1 -1
  22. package/dist/monitoring/SessionReaper.js +57 -0
  23. package/dist/monitoring/SessionReaper.js.map +1 -1
  24. package/dist/scaffold/templates.d.ts.map +1 -1
  25. package/dist/scaffold/templates.js +2 -0
  26. package/dist/scaffold/templates.js.map +1 -1
  27. package/package.json +1 -1
  28. package/src/data/builtin-manifest.json +20 -20
  29. package/src/scaffold/templates.ts +2 -0
  30. package/upgrades/1.3.273.md +52 -0
  31. package/upgrades/1.3.274.md +47 -0
  32. package/upgrades/side-effects/post-transfer-session-closeout.md +116 -0
  33. package/upgrades/side-effects/quota-aware-placement.md +99 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA4SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAi/QtE;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;AA4SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAqxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAgjRtE;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"}
@@ -8640,6 +8640,27 @@ export async function startServer(options) {
8640
8640
  // child (CPU-flat) from a working one, so an idle MCP child no longer
8641
8641
  // holds an otherwise-reapable session hostage under host load.
8642
8642
  descendantCpuSeconds: (s) => sessionManager.descendantCpuSeconds(s),
8643
+ // Post-transfer closeout (2026-06-05): the OTHER machine that owns this
8644
+ // topic per the session-pool ownership registry, as a display identifier
8645
+ // (nickname when resolvable). Late-binds the pool objects — they are
8646
+ // constructed AFTER the reaper in this function; until the mesh block
8647
+ // wires them (and on single-machine installs forever) this returns null
8648
+ // and the rule is inert. try/catch also absorbs the TDZ window.
8649
+ topicOwnerElsewhere: (topicId) => {
8650
+ try {
8651
+ const reg = sessionOwnershipRegistry;
8652
+ const self = _meshSelfId;
8653
+ if (!reg || !self)
8654
+ return null;
8655
+ const owner = reg.ownerOf(String(topicId));
8656
+ if (!owner || owner === self)
8657
+ return null;
8658
+ return machinePoolRegistry?.getCapacity(owner)?.nickname ?? owner;
8659
+ }
8660
+ catch {
8661
+ return null; /* @silent-fallback-ok — pool not wired yet → rule inert */
8662
+ }
8663
+ },
8643
8664
  audit: reaperAuditSink(config.stateDir),
8644
8665
  },
8645
8666
  // developmentAgent gate (standard_development_agent_dark_feature_gate):
@@ -9039,12 +9060,39 @@ export async function startServer(options) {
9039
9060
  poolIdMgr.recordSelfHardware(poolSelfId, poolMod.captureHardware());
9040
9061
  }
9041
9062
  catch { /* best-effort hardware self-attest */ }
9063
+ // Quota-aware placement (2026-06-05): self-report whether a NEW
9064
+ // session on THIS machine could work right now. Blocked = a provider
9065
+ // block is in effect (blockedUntil in the future) or the 5-hour
9066
+ // window is exhausted (>= 95%, the same bar QuotaTracker.canRunJob
9067
+ // uses to block all spawns). Sourced from THIS machine's own
9068
+ // QuotaTracker — never another machine's file (the gemini
9069
+ // quota-conflation lesson). Absent/unreadable state = not blocked.
9070
+ const selfQuotaState = () => {
9071
+ try {
9072
+ const q = quotaTracker?.getState();
9073
+ if (!q)
9074
+ return undefined;
9075
+ const blockActive = !!q.blockedUntil && Date.parse(q.blockedUntil) > Date.now();
9076
+ const fiveHourExhausted = (q.fiveHourPercent ?? 0) >= 95;
9077
+ if (!blockActive && !fiveHourExhausted)
9078
+ return { blocked: false };
9079
+ return {
9080
+ blocked: true,
9081
+ blockedUntil: q.blockedUntil,
9082
+ reason: q.blockReason ?? (fiveHourExhausted ? `5-hour window at ${q.fiveHourPercent}%` : 'provider block'),
9083
+ };
9084
+ }
9085
+ catch {
9086
+ return undefined; /* unknown ≠ blocked */
9087
+ }
9088
+ };
9042
9089
  const refreshPool = () => {
9043
9090
  try {
9044
9091
  machinePoolRegistry.recordHeartbeat({
9045
9092
  machineId: poolSelfId,
9046
9093
  selfReportedLastSeen: new Date().toISOString(),
9047
9094
  loadAvg: osMod.loadavg()[0],
9095
+ quotaState: selfQuotaState(),
9048
9096
  });
9049
9097
  const hbApi = machineHeartbeat?.api;
9050
9098
  if (hbApi) {
@@ -9626,6 +9674,28 @@ export async function startServer(options) {
9626
9674
  }
9627
9675
  }
9628
9676
  catch { /* best-effort; route() re-places regardless once the owner is cleared */ }
9677
+ // ── Post-transfer closeout, immediate half (2026-06-05) ───────
9678
+ // The user's explicit move means this machine's topic session is
9679
+ // now a leftover — left running it does duplicate work alongside
9680
+ // the target machine's session. Close it NOW (origin 'operator':
9681
+ // this executes the user's direct command, arrived through the
9682
+ // authed Telegram pipeline; disposition 'recovery-bounce' keeps
9683
+ // the §P3 notifier silent — the user already got the "Moving…"
9684
+ // reply, and the conversation continues on the target). Protected
9685
+ // sessions are never auto-closed (skipped here AND vetoed in the
9686
+ // reaper sweeper that backstops non-explicit move paths).
9687
+ if (plan.action === 'transfer' && target !== meshSelfId) {
9688
+ try {
9689
+ const tmuxName = telegram?.getSessionForTopic(topicId);
9690
+ const rec = tmuxName ? sessionManager.listRunningSessions().find((s) => s.tmuxSession === tmuxName) : undefined;
9691
+ if (rec && !sessionManager.getProtectedSessions().includes(rec.tmuxSession)) {
9692
+ void sessionManager.terminateSession(rec.id, `topic ${topicId} moved to ${cmd.nickname} (user-commanded transfer) — closing the leftover local session`, { origin: 'operator', disposition: 'recovery-bounce' }).then((r) => {
9693
+ console.log(pc.dim(` [session-pool] post-transfer closeout: ${rec.tmuxSession} → ${r.terminated ? 'closed' : `skipped (${r.skipped})`}`));
9694
+ });
9695
+ }
9696
+ }
9697
+ catch { /* @silent-fallback-ok — the reaper's topic-moved sweeper backstops this */ }
9698
+ }
9629
9699
  await telegram?.sendToTopic(topicId, plan.action === 'noop'
9630
9700
  ? (plan.detail === 'already-on-target'
9631
9701
  ? `This conversation is already running on ${cmd.nickname} — nothing to move.`