instar 1.3.651 → 1.3.652

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 +120 -1
  3. package/dist/commands/server.js.map +1 -1
  4. package/dist/core/ReapGuard.d.ts +13 -1
  5. package/dist/core/ReapGuard.d.ts.map +1 -1
  6. package/dist/core/ReapGuard.js +86 -30
  7. package/dist/core/ReapGuard.js.map +1 -1
  8. package/dist/core/SessionManager.d.ts +11 -0
  9. package/dist/core/SessionManager.d.ts.map +1 -1
  10. package/dist/core/SessionManager.js +30 -4
  11. package/dist/core/SessionManager.js.map +1 -1
  12. package/dist/core/devGatedFeatures.d.ts.map +1 -1
  13. package/dist/core/devGatedFeatures.js +6 -0
  14. package/dist/core/devGatedFeatures.js.map +1 -1
  15. package/dist/core/gapBCommitmentEvidence.d.ts +9 -0
  16. package/dist/core/gapBCommitmentEvidence.d.ts.map +1 -1
  17. package/dist/core/gapBCommitmentEvidence.js +20 -0
  18. package/dist/core/gapBCommitmentEvidence.js.map +1 -1
  19. package/dist/core/types.d.ts +11 -0
  20. package/dist/core/types.d.ts.map +1 -1
  21. package/dist/core/types.js.map +1 -1
  22. package/dist/monitoring/SessionReaper.d.ts +118 -7
  23. package/dist/monitoring/SessionReaper.d.ts.map +1 -1
  24. package/dist/monitoring/SessionReaper.js +302 -101
  25. package/dist/monitoring/SessionReaper.js.map +1 -1
  26. package/dist/monitoring/closeoutLivenessSnapshot.d.ts +143 -0
  27. package/dist/monitoring/closeoutLivenessSnapshot.d.ts.map +1 -0
  28. package/dist/monitoring/closeoutLivenessSnapshot.js +207 -0
  29. package/dist/monitoring/closeoutLivenessSnapshot.js.map +1 -0
  30. package/package.json +1 -1
  31. package/src/data/builtin-manifest.json +3 -3
  32. package/upgrades/1.3.652.md +18 -0
  33. package/upgrades/side-effects/post-transfer-closeout-correctness.md +43 -0
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0CH,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,CAi3ftE;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;AA0CH,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,CA89ftE;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"}
@@ -32,7 +32,7 @@ import { paneIdleWithEmptyInput } from '../core/ModelSwapService.js';
32
32
  import { escalatedModelIds, normalizeTierEscalationConfig } from '../core/ModelTierEscalation.js';
33
33
  import { activeAutonomousJobs, autonomousRunRemainingForTopic, listAutonomousJobs } from '../core/AutonomousSessions.js';
34
34
  import { AGE_LIMIT_ACTIVE_RUN_REASON, COMMITMENT_ACTIVE_RUN_REASON } from '../core/WorkEvidence.js';
35
- import { gapBEligibleForTopic, recentUserMessageFromHistory, resolveGapBInjectionGate, decideGapBInjection } from '../core/gapBCommitmentEvidence.js';
35
+ import { gapBEligibleForTopic, recentUserMessageFromHistory, recentUserMessageAtFromHistory, resolveGapBInjectionGate, decideGapBInjection } from '../core/gapBCommitmentEvidence.js';
36
36
  import { TopicProfileTransferCarrier, createTopicProfilePullHandler } from '../core/TopicProfileTransferCarrier.js';
37
37
  import { closeAllSqlite } from '../core/SqliteRegistry.js';
38
38
  import { SessionManager } from '../core/SessionManager.js';
@@ -14087,6 +14087,96 @@ export async function startServer(options) {
14087
14087
  config: { residueDenylist: ysCfg.residueDenylist, cacheTtlMs: ysCfg.dirtyCheckCacheTtlMs },
14088
14088
  });
14089
14089
  }
14090
+ // ── Post-transfer closeout correctness (F1) ───────────────────────────────
14091
+ // closeoutLivenessGate: when on, the post-transfer closeout no longer
14092
+ // terminates the live local session on a stale/unverified ownership record —
14093
+ // it verifies the owning machine ACTUALLY has a live session for the topic via
14094
+ // a machine-local liveness snapshot before shedding the leftover. Ships dark
14095
+ // fleet-wide, LIVE on a dev agent via resolveDevAgentGate. When OFF the snapshot
14096
+ // is never built and the new deps are left absent (closeout byte-identical).
14097
+ // Spec: docs/specs/post-transfer-closeout-correctness.md.
14098
+ const _closeoutLivenessGate = resolveDevAgentGate(config.monitoring?.sessionReaper?.closeoutLivenessGate, config);
14099
+ let _closeoutSnapshot;
14100
+ // The combined ATOMIC owner read the gated closeout consumes — ONE registry
14101
+ // read returning the STABLE machineId (liveness key) AND the display label
14102
+ // (audit text), guaranteeing they describe the same owner from one instant.
14103
+ const _topicOwnerElsewhereInfo = (topicId) => {
14104
+ try {
14105
+ const reg = sessionOwnershipRegistry;
14106
+ const self = _meshSelfId;
14107
+ if (!reg || !self)
14108
+ return null;
14109
+ const machineId = reg.ownerOf(String(topicId));
14110
+ if (!machineId || machineId === self)
14111
+ return null;
14112
+ const displayName = machinePoolRegistry?.getCapacity(machineId)?.nickname ?? machineId;
14113
+ return { machineId, displayName };
14114
+ }
14115
+ catch {
14116
+ return null; /* @silent-fallback-ok — pool not wired yet → rule inert (withhold) */
14117
+ }
14118
+ };
14119
+ if (_closeoutLivenessGate) {
14120
+ const { CloseoutLivenessSnapshot } = await import('../monitoring/closeoutLivenessSnapshot.js');
14121
+ const _reaperTick = config.monitoring?.sessionReaper?.tickIntervalSec ?? 120;
14122
+ _closeoutSnapshot = new CloseoutLivenessSnapshot({
14123
+ resolvePeerUrls: () => _resolvePeerUrls?.() ?? [],
14124
+ // Reuse the proven per-peer GET /sessions fetch primitive (5s timeout,
14125
+ // same shape as the pool route's fan-out). Rejects on any failure so the
14126
+ // entry ages to stale → 'unknown' (fail-closed).
14127
+ fetchPeerSessions: async (peer) => {
14128
+ const r = await fetch(`${peer.url}/sessions`, {
14129
+ headers: { Authorization: `Bearer ${config.authToken}` },
14130
+ signal: AbortSignal.timeout(5000),
14131
+ });
14132
+ if (!r.ok)
14133
+ throw new Error(`HTTP ${r.status}`);
14134
+ return (await r.json());
14135
+ },
14136
+ // Owner set = distinct machineId of every owned-elsewhere topic that has
14137
+ // a live LOCAL leftover here (the exact set the closeout could act on).
14138
+ ownerSet: () => {
14139
+ const owners = new Set();
14140
+ try {
14141
+ for (const s of sessionManager.listRunningSessions()) {
14142
+ const t = _resolveTopic(s.tmuxSession);
14143
+ if (t == null)
14144
+ continue;
14145
+ const info = _topicOwnerElsewhereInfo(t);
14146
+ if (info)
14147
+ owners.add(info.machineId);
14148
+ }
14149
+ }
14150
+ catch { /* @silent-fallback-ok — degrade to whatever was already pending */ }
14151
+ return [...owners];
14152
+ },
14153
+ now: () => Date.now(),
14154
+ raiseAttention: (item) => {
14155
+ try {
14156
+ if (!telegram)
14157
+ return;
14158
+ void telegram.createAttentionItem({
14159
+ id: item.id, title: item.title, summary: item.summary, description: item.description,
14160
+ category: 'sessions', priority: 'NORMAL',
14161
+ sourceContext: 'session-reaper:closeout-snapshot-breaker',
14162
+ }).catch(() => { });
14163
+ }
14164
+ catch { /* @silent-fallback-ok — telegram not wired (TDZ) → audit-only */ }
14165
+ },
14166
+ audit: reaperAuditSink(config.stateDir),
14167
+ }, { tickIntervalSec: _reaperTick });
14168
+ // Level-triggered on the reaper cadence with ±10% jitter (anti-herd). Async
14169
+ // refresh; the closeout always reads the PREVIOUS snapshot, never a fetch it
14170
+ // triggers this tick. A thrown pass is swallowed (previous snapshot ages to
14171
+ // stale → 'unknown'); the loop is never wedged.
14172
+ const _jitter = 1 + (Math.random() * 0.2 - 0.1);
14173
+ const _snapshotTimer = setInterval(() => {
14174
+ void _closeoutSnapshot.refresh().catch(() => { });
14175
+ }, Math.round(_reaperTick * 1000 * _jitter));
14176
+ if (typeof _snapshotTimer.unref === 'function')
14177
+ _snapshotTimer.unref();
14178
+ console.log(pc.green(' SessionReaper closeoutLivenessGate ON (post-transfer closeout liveness-gated — live)'));
14179
+ }
14090
14180
  const sessionReaper = new SessionReaper({
14091
14181
  ...reapGuardDeps,
14092
14182
  dirtyCheck: _yieldSafetyDirtyCheck,
@@ -14133,6 +14223,8 @@ export async function startServer(options) {
14133
14223
  },
14134
14224
  terminate: (id, reason, opts) => sessionManager.terminateSession(id, reason, {
14135
14225
  bypassActiveProcessKeep: opts?.bypassActiveProcessKeep,
14226
+ bypassRecentUserMessageForConfirmedMove: opts?.bypassRecentUserMessageForConfirmedMove,
14227
+ workEvidence: opts?.workEvidence,
14136
14228
  }),
14137
14229
  markReaping: (id) => sessionManager.markReaping(id),
14138
14230
  clearReaping: (id) => sessionManager.clearReaping(id),
@@ -14161,6 +14253,28 @@ export async function startServer(options) {
14161
14253
  return null; /* @silent-fallback-ok — pool not wired yet → rule inert */
14162
14254
  }
14163
14255
  },
14256
+ // Post-transfer closeout correctness (F1): the ATOMIC combined owner read
14257
+ // the gated closeout uses (stable machineId + display label from ONE
14258
+ // registry read). Always constructed (a pure read helper); the closeout
14259
+ // only consults it on the gated path. Returns null when the pool is dark.
14260
+ topicOwnerElsewhereInfo: _topicOwnerElsewhereInfo,
14261
+ // Post-transfer closeout correctness (F1): the liveness verdict, read from
14262
+ // the machine-local snapshot (NOT a live per-tick fetch). Injected ONLY
14263
+ // when the gate resolved on (the snapshot exists); absent ⇒ gate inert.
14264
+ remoteOwnerHasLiveSession: _closeoutSnapshot?.remoteOwnerHasLiveSession,
14265
+ // Post-transfer closeout correctness (F1, Part E): the LOCAL-receipt ts of
14266
+ // the bound topic's newest user message — same history source the
14267
+ // recent-user-message KEEP-guard reads. Backs the freshest-interaction veto.
14268
+ recentUserMessageAt: (topicId) => {
14269
+ try {
14270
+ if (!telegram)
14271
+ return null;
14272
+ return recentUserMessageAtFromHistory(telegram.getTopicHistory(topicId, 50));
14273
+ }
14274
+ catch {
14275
+ return null; /* @silent-fallback-ok — no history → withhold the bypass (safe) */
14276
+ }
14277
+ },
14164
14278
  // WS1.3: pin-conflict do-not-act — a pin naming THIS machine while the
14165
14279
  // owner is elsewhere means the reconciler is bringing the topic back;
14166
14280
  // the closeout holds instead of attacking the session the pin wants here.
@@ -14210,6 +14324,11 @@ export async function startServer(options) {
14210
14324
  // Observe-only busy-orphan detection rides the same dev-gate (dark fleet,
14211
14325
  // live on dev agents). Zero risk — it never changes a keep/kill verdict.
14212
14326
  busyOrphanDetection: resolveDevAgentGate(rcfg.busyOrphanDetection, config),
14327
+ // Post-transfer closeout correctness (F1): the liveness gate rides the
14328
+ // SAME dev-gate (dark fleet, live on dev agents). Resolved once here so
14329
+ // the reaper's gated/legacy branch matches the snapshot construction
14330
+ // above (both read resolveDevAgentGate(...closeoutLivenessGate)).
14331
+ closeoutLivenessGate: _closeoutLivenessGate,
14213
14332
  };
14214
14333
  })());
14215
14334
  sessionReaper.start();