instar 1.3.729 → 1.3.730
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.
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +174 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/core/ModelSwapService.d.ts +13 -0
- package/dist/core/ModelSwapService.d.ts.map +1 -1
- package/dist/core/ModelSwapService.js +11 -0
- package/dist/core/ModelSwapService.js.map +1 -1
- package/dist/core/ModelTierEscalation.d.ts +10 -0
- package/dist/core/ModelTierEscalation.d.ts.map +1 -1
- package/dist/core/ModelTierEscalation.js +4 -0
- package/dist/core/ModelTierEscalation.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +18 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/ProactiveSwapMonitor.d.ts +74 -20
- package/dist/core/ProactiveSwapMonitor.d.ts.map +1 -1
- package/dist/core/ProactiveSwapMonitor.js +349 -28
- package/dist/core/ProactiveSwapMonitor.js.map +1 -1
- package/dist/core/QuotaAwareScheduler.d.ts +70 -3
- package/dist/core/QuotaAwareScheduler.d.ts.map +1 -1
- package/dist/core/QuotaAwareScheduler.js +100 -16
- package/dist/core/QuotaAwareScheduler.js.map +1 -1
- package/dist/core/SessionManager.d.ts +28 -0
- package/dist/core/SessionManager.d.ts.map +1 -1
- package/dist/core/SessionManager.js +102 -0
- package/dist/core/SessionManager.js.map +1 -1
- package/dist/core/SessionRefresh.d.ts +116 -1
- package/dist/core/SessionRefresh.d.ts.map +1 -1
- package/dist/core/SessionRefresh.js +280 -17
- package/dist/core/SessionRefresh.js.map +1 -1
- package/dist/core/SwapAntiThrash.d.ts +329 -0
- package/dist/core/SwapAntiThrash.d.ts.map +1 -0
- package/dist/core/SwapAntiThrash.js +1025 -0
- package/dist/core/SwapAntiThrash.js.map +1 -0
- package/dist/core/SwapLedger.d.ts +168 -0
- package/dist/core/SwapLedger.d.ts.map +1 -0
- package/dist/core/SwapLedger.js +275 -0
- package/dist/core/SwapLedger.js.map +1 -0
- package/dist/core/SwapWorkGate.d.ts +100 -0
- package/dist/core/SwapWorkGate.d.ts.map +1 -0
- package/dist/core/SwapWorkGate.js +175 -0
- package/dist/core/SwapWorkGate.js.map +1 -0
- package/dist/core/devGatedFeatures.d.ts.map +1 -1
- package/dist/core/devGatedFeatures.js +6 -0
- package/dist/core/devGatedFeatures.js.map +1 -1
- package/dist/core/types.d.ts +63 -1
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/guardManifest.d.ts.map +1 -1
- package/dist/monitoring/guardManifest.js +40 -1
- package/dist/monitoring/guardManifest.js.map +1 -1
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +19 -0
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +36 -3
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +65 -65
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.730.md +112 -0
- package/upgrades/side-effects/swap-continuity-antithrash.md +118 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgDH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAU3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAyBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA6H7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAgFtD;;;;;;;;;;;;;;;;;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;AAu/CD,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,CAijBN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CA2skBtE;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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -26,6 +26,7 @@ import { SpawningTopicsRegistry } from '../core/SpawningTopicsRegistry.js';
|
|
|
26
26
|
import { TopicReachabilityVerifier } from '../monitoring/TopicReachabilityVerifier.js';
|
|
27
27
|
import { SingleInstanceLock, installReleaseHandlers } from '../core/SingleInstanceLock.js';
|
|
28
28
|
import { resolveDevAgentGate, resolveStateSyncStores } from '../core/devAgentGate.js';
|
|
29
|
+
import { resolveAntiThrashKnobs, readingValidity } from '../core/SwapAntiThrash.js';
|
|
29
30
|
import { shouldReleaseOnComplete, planClaimOnSpawn, ownershipNonce } from '../core/ownershipFollowsLiveWork.js';
|
|
30
31
|
import { PrHandLease } from '../core/PrHandLease.js';
|
|
31
32
|
import { claimSuspensionExcludesPin } from '../core/TopicClaimAnnotationStore.js';
|
|
@@ -760,6 +761,12 @@ let _quotaAwareScheduler = null;
|
|
|
760
761
|
// Subscription & Auth Standard P1.3 — pre-limit proactive swap monitor. Null
|
|
761
762
|
// until wired (requires the scheduler; gated behind subscriptionPool.proactiveSwap).
|
|
762
763
|
let _proactiveSwapMonitor = null;
|
|
764
|
+
// swap-continuity-antithrash §3/§4 — the anti-thrash spine. The ledger + engine
|
|
765
|
+
// load and hydrate UNCONDITIONALLY at boot (R4-L3: reactive rows append
|
|
766
|
+
// regardless of antiThrash.enabled, so the index is warm at a mid-run flag
|
|
767
|
+
// flip). Null only when construction itself failed (non-fatal).
|
|
768
|
+
let _swapAntiThrashEngine = null;
|
|
769
|
+
let _swapWorkGate = null;
|
|
763
770
|
async function spawnSessionForTopic(sessionManager, telegram, sessionName, topicId, latestMessage, topicMemory, userProfile, precomputedContext,
|
|
764
771
|
/** Subscription & Auth Standard P1.3 (additive): launch under this account's
|
|
765
772
|
* config home + record its id (the quota-aware account-swap mechanism).
|
|
@@ -14962,6 +14969,94 @@ export async function startServer(options) {
|
|
|
14962
14969
|
divergenceChecker = undefined;
|
|
14963
14970
|
}
|
|
14964
14971
|
}
|
|
14972
|
+
// ── Swap-continuity anti-thrash spine (swap-continuity-antithrash §3/§4) ──
|
|
14973
|
+
// SwapLedger + SwapAntiThrashEngine + SwapWorkGate. Constructed and
|
|
14974
|
+
// hydrated UNCONDITIONALLY (R4-L3): the ledger loads at every boot
|
|
14975
|
+
// regardless of antiThrash.enabled so the in-memory index (dwell /
|
|
14976
|
+
// reversal / breaker / frequency) is warm the moment the flag flips on
|
|
14977
|
+
// mid-run — the §7.1 half-enable trap closed for this key. All antiThrash
|
|
14978
|
+
// knobs are read LIVE per tick via the getter (the config object is
|
|
14979
|
+
// shared + mutated in place by config PATCHes).
|
|
14980
|
+
const antiThrashKnobsGetter = () => resolveAntiThrashKnobs(config.subscriptionPool?.proactiveSwap?.antiThrash, {
|
|
14981
|
+
thresholdPct: config.subscriptionPool?.proactiveSwap?.thresholdPct,
|
|
14982
|
+
tickMs: config.subscriptionPool?.proactiveSwap?.tickMs,
|
|
14983
|
+
});
|
|
14984
|
+
// swapContinuity (Piece 2, the work gate): the `enabled` KEY IS OMITTED
|
|
14985
|
+
// from shipped config on purpose — omission routes it through the
|
|
14986
|
+
// dev-agent gate (live on a dev agent, dark on the fleet; the #1001
|
|
14987
|
+
// anti-mechanism). `enabled` is RESTART-REQUIRED (§7.1 — the gate wiring
|
|
14988
|
+
// into SessionRefresh is constructor-injected); dryRun + numeric knobs
|
|
14989
|
+
// are read live per evaluation via the getters below.
|
|
14990
|
+
const swapContinuityEnabled = resolveDevAgentGate(config.subscriptionPool?.swapContinuity?.enabled, config);
|
|
14991
|
+
const swapContinuityKnobs = () => {
|
|
14992
|
+
const c = config.subscriptionPool?.swapContinuity;
|
|
14993
|
+
return {
|
|
14994
|
+
enabled: swapContinuityEnabled,
|
|
14995
|
+
dryRun: c?.dryRun ?? true,
|
|
14996
|
+
deferralCeilingMs: c?.deferralCeilingMs ?? 1_800_000,
|
|
14997
|
+
reactiveGraceMs: c?.reactiveGraceMs ?? 120_000,
|
|
14998
|
+
recheckMs: c?.recheckMs ?? 10_000,
|
|
14999
|
+
};
|
|
15000
|
+
};
|
|
15001
|
+
try {
|
|
15002
|
+
const { SwapLedger } = await import('../core/SwapLedger.js');
|
|
15003
|
+
const { SwapAntiThrashEngine, retentionBoundMs, crossKnobWarnings } = await import('../core/SwapAntiThrash.js');
|
|
15004
|
+
const { SwapWorkGate } = await import('../core/SwapWorkGate.js');
|
|
15005
|
+
const swapLedger = new SwapLedger({
|
|
15006
|
+
filePath: path.join(config.stateDir, 'state', 'swap-ledger.jsonl'),
|
|
15007
|
+
windowMs: () => retentionBoundMs(antiThrashKnobsGetter()),
|
|
15008
|
+
// Counts supplied at outage-resume time (the ledger-lost refusals the
|
|
15009
|
+
// writer itself could not record — §3.5/I5).
|
|
15010
|
+
outageRefusalCount: () => _swapAntiThrashEngine?.ledgerLostRefusalCount() ?? 0,
|
|
15011
|
+
machineId: _meshSelfId ?? 'local',
|
|
15012
|
+
logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
|
|
15013
|
+
});
|
|
15014
|
+
_swapAntiThrashEngine = new SwapAntiThrashEngine({
|
|
15015
|
+
ledger: swapLedger,
|
|
15016
|
+
getKnobs: antiThrashKnobsGetter,
|
|
15017
|
+
machineId: _meshSelfId ?? 'local',
|
|
15018
|
+
// P17: episode-deduped attention (the engine dedupes on episodeId;
|
|
15019
|
+
// the id passed here is the stable per-episode dedupe key).
|
|
15020
|
+
raiseAttention: (id, title, body) => {
|
|
15021
|
+
void telegram
|
|
15022
|
+
?.createAttentionItem({
|
|
15023
|
+
id,
|
|
15024
|
+
title,
|
|
15025
|
+
summary: body,
|
|
15026
|
+
category: 'subscription-pool',
|
|
15027
|
+
priority: 'HIGH',
|
|
15028
|
+
sourceContext: 'subscription-pool:swap-antithrash',
|
|
15029
|
+
})
|
|
15030
|
+
.catch(() => {
|
|
15031
|
+
/* @silent-fallback-ok: attention is best-effort; the ledger row is the durable trace */
|
|
15032
|
+
});
|
|
15033
|
+
},
|
|
15034
|
+
logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
|
|
15035
|
+
});
|
|
15036
|
+
_swapAntiThrashEngine.hydrate();
|
|
15037
|
+
// Cross-knob coherence warnings (§7, R4-L2 — warn-only, once at boot).
|
|
15038
|
+
// 15 min = the QuotaPoller's default cadence (pollIntervalMs ?? 15*60_000).
|
|
15039
|
+
for (const w of crossKnobWarnings(antiThrashKnobsGetter(), 15 * 60_000))
|
|
15040
|
+
console.warn(w);
|
|
15041
|
+
_swapWorkGate = new SwapWorkGate({
|
|
15042
|
+
checkSessionWorkState: (tmux) => sessionManager.checkSessionWorkState(tmux),
|
|
15043
|
+
getClaudeSessionId: (tmux) => state.listSessions({ status: 'running' }).find((s) => s.tmuxSession === tmux)?.claudeSessionId ?? null,
|
|
15044
|
+
hasActiveSubagents: (id) => subagentTracker.hasActiveSubagents(id),
|
|
15045
|
+
getActiveSubagents: (id) => subagentTracker.getActiveSubagents(id).map((r) => ({
|
|
15046
|
+
agentType: r.agentType,
|
|
15047
|
+
startedAt: r.startedAt,
|
|
15048
|
+
...(r.transcriptPath ? { transcriptPath: r.transcriptPath } : {}),
|
|
15049
|
+
})),
|
|
15050
|
+
});
|
|
15051
|
+
}
|
|
15052
|
+
catch (err) {
|
|
15053
|
+
// @silent-fallback-ok: cascade-isolation — a spine construction failure
|
|
15054
|
+
// must never 503 the server; behavior degrades to pre-brake semantics
|
|
15055
|
+
// (the legacy path) and re-attempts next boot.
|
|
15056
|
+
console.warn('[instar] swap-continuity anti-thrash spine init failed (non-fatal):', err);
|
|
15057
|
+
_swapAntiThrashEngine = null;
|
|
15058
|
+
_swapWorkGate = null;
|
|
15059
|
+
}
|
|
14965
15060
|
// SessionRefresh — agent-initiated session respawn. Requires a Telegram
|
|
14966
15061
|
// adapter (v1 scope: Telegram-bound sessions only). The respawner closure
|
|
14967
15062
|
// captures `topicMemory` by reference, so even if topicMemory is wired up
|
|
@@ -14979,6 +15074,43 @@ export async function startServer(options) {
|
|
|
14979
15074
|
state,
|
|
14980
15075
|
telegram: telegramRef,
|
|
14981
15076
|
topicResumeMap: _topicResumeMap,
|
|
15077
|
+
// swap-continuity-antithrash §4.2 — the in-flight work gate, bound at
|
|
15078
|
+
// THIS primitive (the funnel every session-killing mutation flows
|
|
15079
|
+
// through). Constructor-injected on purpose: `swapContinuity.enabled`
|
|
15080
|
+
// is restart-required (§7.1); dryRun + numeric knobs read live via
|
|
15081
|
+
// getKnobs. Null when the feature is dark on this install ⇒
|
|
15082
|
+
// byte-for-byte today's behavior (unconditional kill).
|
|
15083
|
+
workGateCtx: swapContinuityEnabled && _swapWorkGate
|
|
15084
|
+
? {
|
|
15085
|
+
probe: (tmux) => _swapWorkGate.probe(tmux),
|
|
15086
|
+
getKnobs: () => {
|
|
15087
|
+
const k = swapContinuityKnobs();
|
|
15088
|
+
return { enabled: k.enabled, dryRun: k.dryRun, reactiveGraceMs: k.reactiveGraceMs, recheckMs: k.recheckMs };
|
|
15089
|
+
},
|
|
15090
|
+
recordProceeded: (args) => _swapAntiThrashEngine?.recordProceeded(args),
|
|
15091
|
+
recordInteractiveRefusal: (args) => _swapAntiThrashEngine?.recordInteractiveRefusal(args),
|
|
15092
|
+
// §4.3(2) — Q4: v1 reads the in-memory exactly-once map (set on
|
|
15093
|
+
// inbound, cleared on reply); 'unknown' = the map/ledger is
|
|
15094
|
+
// unavailable (post-restart, or exactly-once ingress dark).
|
|
15095
|
+
// `messageLedger`/`currentInboundByTopic` are declared later in
|
|
15096
|
+
// this boot function and captured by reference — every call
|
|
15097
|
+
// happens post-boot, so the binding is initialized by then.
|
|
15098
|
+
resolveUnansweredInbound: (topicId) => {
|
|
15099
|
+
if (!messageLedger || !currentInboundByTopic)
|
|
15100
|
+
return 'unknown';
|
|
15101
|
+
const dedupeKey = currentInboundByTopic.get(String(topicId));
|
|
15102
|
+
if (!dedupeKey)
|
|
15103
|
+
return 'none';
|
|
15104
|
+
const entry = messageLedger.get(dedupeKey);
|
|
15105
|
+
if (!entry || entry.inputSnapshot === null)
|
|
15106
|
+
return 'unknown';
|
|
15107
|
+
const from = entry.senderEnvelope?.username ??
|
|
15108
|
+
entry.senderEnvelope?.firstName ??
|
|
15109
|
+
(entry.senderEnvelope?.userId !== undefined ? String(entry.senderEnvelope.userId) : 'unknown sender');
|
|
15110
|
+
return { body: entry.inputSnapshot, from, at: entry.receivedAt };
|
|
15111
|
+
},
|
|
15112
|
+
}
|
|
15113
|
+
: null,
|
|
14982
15114
|
// §10.5 Slack binding — SlackAdapter satisfies SlackRefreshBinding
|
|
14983
15115
|
// structurally (getChannelForSession / removeChannelResume /
|
|
14984
15116
|
// resolveChannelForSessionFromDisk). Null ⇒ Telegram-only, unchanged.
|
|
@@ -15042,6 +15174,30 @@ export async function startServer(options) {
|
|
|
15042
15174
|
_quotaAwareScheduler = new QuotaAwareScheduler({
|
|
15043
15175
|
listAccounts: () => subscriptionPool.list(),
|
|
15044
15176
|
softThresholdPct: config.subscriptionPool?.swapSoftThresholdPct,
|
|
15177
|
+
// swap-continuity-antithrash — the anti-thrash observation + execute-time
|
|
15178
|
+
// revalidation hooks. Reactive decision behavior is untouched (I6):
|
|
15179
|
+
// these only OBSERVE reactive swaps (dwell clock-start, hop alerts,
|
|
15180
|
+
// failed rows) and revalidate PROACTIVE explicit-target calls (§3.3).
|
|
15181
|
+
antiThrash: _swapAntiThrashEngine
|
|
15182
|
+
? {
|
|
15183
|
+
readingValid: (acct, nowMs) => readingValidity(acct, nowMs, antiThrashKnobsGetter().quotaFreshnessMs).valid,
|
|
15184
|
+
getKnobs: () => {
|
|
15185
|
+
const k = antiThrashKnobsGetter();
|
|
15186
|
+
return { thresholdPct: k.thresholdPct, targetHeadroomPct: k.targetHeadroomPct, minImprovementPct: k.minImprovementPct };
|
|
15187
|
+
},
|
|
15188
|
+
resolveCurrentAccountId: (sessionName) => state.listSessions({ status: 'running' }).find((s) => s.tmuxSession === sessionName)?.subscriptionAccountId ?? null,
|
|
15189
|
+
onReactiveExecuted: (a) => _swapAntiThrashEngine?.recordReactiveExecuted(a),
|
|
15190
|
+
onReactiveFailed: (a) => _swapAntiThrashEngine?.recordExecFailure({
|
|
15191
|
+
session: a.session,
|
|
15192
|
+
from: a.from,
|
|
15193
|
+
...(a.to ? { to: a.to } : {}),
|
|
15194
|
+
kind: 'reactive',
|
|
15195
|
+
errorClass: a.errorClass,
|
|
15196
|
+
nowMs: a.nowMs,
|
|
15197
|
+
}),
|
|
15198
|
+
onReactiveRateCapRefusal: (s, n) => _swapAntiThrashEngine?.noteReactiveRateCapRefusal(s, n),
|
|
15199
|
+
}
|
|
15200
|
+
: undefined,
|
|
15045
15201
|
refreshFn: async (o) => {
|
|
15046
15202
|
if (!_sessionRefresh)
|
|
15047
15203
|
return false;
|
|
@@ -15050,8 +15206,13 @@ export async function startServer(options) {
|
|
|
15050
15206
|
reason: o.reason,
|
|
15051
15207
|
configHome: o.configHome,
|
|
15052
15208
|
accountId: o.accountId,
|
|
15209
|
+
// I11: callerClass set ONLY server-internally — the scheduler is
|
|
15210
|
+
// the one caller that knows its lane (proactive funnel vs reactive).
|
|
15211
|
+
callerClass: o.callerClass,
|
|
15053
15212
|
});
|
|
15054
|
-
|
|
15213
|
+
// Object form: surfaces the structured refusal code so the
|
|
15214
|
+
// anti-thrash observation layer can classify failures (§3.1/§3.6).
|
|
15215
|
+
return r.ok ? { ok: true } : { ok: false, code: r.code };
|
|
15055
15216
|
},
|
|
15056
15217
|
onNoAlternate: (sessionName, exhaustedAccountId) => {
|
|
15057
15218
|
void telegramRef.createAttentionItem({
|
|
@@ -15133,6 +15294,18 @@ export async function startServer(options) {
|
|
|
15133
15294
|
maxSwapsPerCycle: proactiveCfg.maxSwapsPerCycle,
|
|
15134
15295
|
cooldownMs: proactiveCfg.cooldownMs,
|
|
15135
15296
|
tickMs: proactiveCfg.tickMs,
|
|
15297
|
+
// swap-continuity-antithrash §3 — the brake engine at the proactive
|
|
15298
|
+
// DECISION chokepoint. Knobs read LIVE per tick (§7.1). dryRun keeps
|
|
15299
|
+
// the legacy decision path byte-identical while the engine logs
|
|
15300
|
+
// would-refuse shadow rows (the rung-2 soak).
|
|
15301
|
+
antiThrash: _swapAntiThrashEngine
|
|
15302
|
+
? { engine: _swapAntiThrashEngine, getKnobs: antiThrashKnobsGetter }
|
|
15303
|
+
: undefined,
|
|
15304
|
+
// §4 — the in-flight work gate (proactive arm: defer, ceiling-drop).
|
|
15305
|
+
// The monitor owns the deferral lifecycle; the gate is stateless.
|
|
15306
|
+
workGate: _swapWorkGate
|
|
15307
|
+
? { probe: (s) => _swapWorkGate.probe(s), getContinuity: swapContinuityKnobs }
|
|
15308
|
+
: undefined,
|
|
15136
15309
|
logger: { log: (m) => console.log(m), warn: (m) => console.warn(m) },
|
|
15137
15310
|
});
|
|
15138
15311
|
_proactiveSwapMonitor.start();
|