instar 1.3.502 → 1.3.504
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 +117 -0
- package/dist/commands/server.js.map +1 -1
- package/dist/config/ConfigDefaults.d.ts.map +1 -1
- package/dist/config/ConfigDefaults.js +7 -0
- package/dist/config/ConfigDefaults.js.map +1 -1
- package/dist/core/CoherenceJournal.d.ts +1 -1
- package/dist/core/CoherenceJournal.d.ts.map +1 -1
- package/dist/core/CoherenceJournal.js +5 -1
- package/dist/core/CoherenceJournal.js.map +1 -1
- package/dist/core/MachinePoolRegistry.d.ts +4 -0
- package/dist/core/MachinePoolRegistry.d.ts.map +1 -1
- package/dist/core/MachinePoolRegistry.js +4 -0
- package/dist/core/MachinePoolRegistry.js.map +1 -1
- package/dist/core/OwnershipReconciler.d.ts +102 -0
- package/dist/core/OwnershipReconciler.d.ts.map +1 -0
- package/dist/core/OwnershipReconciler.js +185 -0
- package/dist/core/OwnershipReconciler.js.map +1 -0
- package/dist/core/SessionOwnership.d.ts +16 -1
- package/dist/core/SessionOwnership.d.ts.map +1 -1
- package/dist/core/SessionOwnership.js +14 -0
- package/dist/core/SessionOwnership.js.map +1 -1
- package/dist/core/SessionRouter.d.ts +11 -0
- package/dist/core/SessionRouter.d.ts.map +1 -1
- package/dist/core/SessionRouter.js +10 -0
- package/dist/core/SessionRouter.js.map +1 -1
- package/dist/core/types.d.ts +13 -0
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js.map +1 -1
- package/dist/monitoring/SessionReaper.d.ts +5 -0
- package/dist/monitoring/SessionReaper.d.ts.map +1 -1
- package/dist/monitoring/SessionReaper.js +22 -2
- package/dist/monitoring/SessionReaper.js.map +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +12 -1
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/src/data/builtin-manifest.json +46 -46
- package/upgrades/1.3.503.md +61 -0
- package/upgrades/1.3.504.md +51 -0
- package/upgrades/side-effects/multi-machine-seamlessness-ws11.md +105 -0
- package/upgrades/side-effects/multi-machine-seamlessness-ws13.md +122 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAyG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;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;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAgCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAuBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAyG7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;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;AA4zCD,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,CAynYtE;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
|
@@ -384,6 +384,9 @@ let _inboundQueueStop = null;
|
|
|
384
384
|
* (forward/spawn on another machine → must NOT also dispatch locally) from a
|
|
385
385
|
* self placement. Set once in startServer()'s mesh block. */
|
|
386
386
|
let _meshSelfId = null;
|
|
387
|
+
/** WS1.1: read-only ownership lookup for the drain's spawn-boundary re-check
|
|
388
|
+
* (the registry is constructed later in startServer's pool block). */
|
|
389
|
+
let _ownershipReadForDrain = null;
|
|
387
390
|
/** Resolve the current router (Telegram-owning lease holder)'s base URL, or null if
|
|
388
391
|
* this machine IS the router / none is known. Used by the owner-side resume to fetch
|
|
389
392
|
* a moved topic's prior history from the router (bug #2). Set in the mesh block where
|
|
@@ -2014,6 +2017,22 @@ getAttentionTopicId) {
|
|
|
2014
2017
|
return { kind: 'local-delivered', injectError: err instanceof Error ? err.message : String(err) };
|
|
2015
2018
|
}
|
|
2016
2019
|
}
|
|
2020
|
+
// WS1.1 (MULTI-MACHINE-SEAMLESSNESS-SPEC): ownership re-check at the SPAWN
|
|
2021
|
+
// boundary. route() consulted ownership above, but ownership can move in
|
|
2022
|
+
// the window between that verdict and this spawn (entry queued under owner
|
|
2023
|
+
// A, transferred to B mid-queue, drained on A) — a non-owner spawn is the
|
|
2024
|
+
// double-session bug (audit F20). Bounce to un-routable: the entry
|
|
2025
|
+
// re-queues and the next drain pass re-routes against FRESH ownership
|
|
2026
|
+
// (forward to the real owner / queue). Direct-inject into an EXISTING
|
|
2027
|
+
// local session above is not gated — a live local session for the topic is
|
|
2028
|
+
// itself the strongest local-serving signal, and the reconciler/closeout
|
|
2029
|
+
// own its lifecycle.
|
|
2030
|
+
if (_ownershipReadForDrain && _meshSelfId) {
|
|
2031
|
+
const ownRec = _ownershipReadForDrain(dmsg.sessionKey);
|
|
2032
|
+
if (ownRec && ownRec.status === 'active' && ownRec.ownerMachineId !== _meshSelfId) {
|
|
2033
|
+
return { kind: 'un-routable', reason: 'ownership-moved-before-spawn' };
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2017
2036
|
// Respawn / auto-spawn path. The spawn-in-progress guard maps to
|
|
2018
2037
|
// un-routable (§3.1) — never a silent skip.
|
|
2019
2038
|
if (spawningTopics.has(topicId)) {
|
|
@@ -11381,6 +11400,19 @@ export async function startServer(options) {
|
|
|
11381
11400
|
return null; /* @silent-fallback-ok — pool not wired yet → rule inert */
|
|
11382
11401
|
}
|
|
11383
11402
|
},
|
|
11403
|
+
// WS1.3: pin-conflict do-not-act — a pin naming THIS machine while the
|
|
11404
|
+
// owner is elsewhere means the reconciler is bringing the topic back;
|
|
11405
|
+
// the closeout holds instead of attacking the session the pin wants here.
|
|
11406
|
+
topicPinnedHere: (topicId) => {
|
|
11407
|
+
try {
|
|
11408
|
+
const self = _meshSelfId;
|
|
11409
|
+
const pin = _topicPinStore?.get(String(topicId));
|
|
11410
|
+
return !!self && !!pin && pin.pinned && pin.preferredMachine === self;
|
|
11411
|
+
}
|
|
11412
|
+
catch {
|
|
11413
|
+
return false; /* @silent-fallback-ok — no pin signal → hold not applied, closeout behaves as before */
|
|
11414
|
+
}
|
|
11415
|
+
},
|
|
11384
11416
|
audit: reaperAuditSink(config.stateDir),
|
|
11385
11417
|
},
|
|
11386
11418
|
// developmentAgent gate (standard_development_agent_dark_feature_gate):
|
|
@@ -11879,6 +11911,10 @@ export async function startServer(options) {
|
|
|
11879
11911
|
loadAvg: osMod.loadavg()[0],
|
|
11880
11912
|
quotaState: selfQuotaState(),
|
|
11881
11913
|
guardPosture: selfGuardPosture(),
|
|
11914
|
+
// WS1.1 capability advertisement (spec invariant 5): a bounded
|
|
11915
|
+
// fixed-size summary, never an inventory. Reported live each
|
|
11916
|
+
// heartbeat so a queue going dark withdraws the capability.
|
|
11917
|
+
seamlessnessFlags: { ws11DeliverReceive: !!_inboundQueue },
|
|
11882
11918
|
// Durable Inbound Message Queue §5.1: depth + oldest + tenure +
|
|
11883
11919
|
// bounded top-K — the survivor's loss-SUSPECTED item, capped
|
|
11884
11920
|
// re-placement arm, and supersede-dedupe key all read these.
|
|
@@ -11971,6 +12007,10 @@ export async function startServer(options) {
|
|
|
11971
12007
|
logger: (m) => console.log(pc.dim(` ${m}`)),
|
|
11972
12008
|
});
|
|
11973
12009
|
const ownReg = sessionOwnershipRegistry;
|
|
12010
|
+
// WS1.1: expose a read-only ownership lookup to the drain's
|
|
12011
|
+
// spawn-boundary re-check (module-scope; the drain closure is defined
|
|
12012
|
+
// before this block runs).
|
|
12013
|
+
_ownershipReadForDrain = (sk) => ownReg.read(sk);
|
|
11974
12014
|
// ── WS3 one-voice: bind the election's late pool deps ──
|
|
11975
12015
|
// From here on the SpeakerElection sees the real pool: online machine
|
|
11976
12016
|
// ids from the capacity registry and live topic ownership from the
|
|
@@ -11998,6 +12038,72 @@ export async function startServer(options) {
|
|
|
11998
12038
|
}
|
|
11999
12039
|
},
|
|
12000
12040
|
};
|
|
12041
|
+
// ── WS1.3 ownership reconciler (MULTI-MACHINE-SEAMLESSNESS-SPEC) ──
|
|
12042
|
+
// Bounded pin/owner convergence on a tick, replacing "wait for an
|
|
12043
|
+
// inbound message that delivery may never route" (the 2026-06-12 stuck
|
|
12044
|
+
// transfer-back). Dark + dry-run defaults; strict single-machine no-op
|
|
12045
|
+
// inside the module. Phase C: quorum logic is N-machine from day one.
|
|
12046
|
+
if (_topicPinStore && _meshSelfId) {
|
|
12047
|
+
const ws13Cfg = () => (config.multiMachine?.seamlessness ?? {});
|
|
12048
|
+
const { OwnershipReconciler } = await import('../core/OwnershipReconciler.js');
|
|
12049
|
+
const reconciler = new OwnershipReconciler({
|
|
12050
|
+
enabled: () => ws13Cfg().ws13Reconcile === true,
|
|
12051
|
+
dryRun: () => ws13Cfg().ws13DryRun !== false,
|
|
12052
|
+
selfMachineId: _meshSelfId,
|
|
12053
|
+
pinStore: _topicPinStore,
|
|
12054
|
+
ownership: ownReg,
|
|
12055
|
+
machines: () => {
|
|
12056
|
+
try {
|
|
12057
|
+
return (machinePoolRegistry?.getCapacities() ?? []).map((c) => ({
|
|
12058
|
+
machineId: c.machineId,
|
|
12059
|
+
online: !!c.online,
|
|
12060
|
+
lastSeenMs: c.selfReportedLastSeen ? Date.parse(c.selfReportedLastSeen) || 0 : 0,
|
|
12061
|
+
}));
|
|
12062
|
+
}
|
|
12063
|
+
catch {
|
|
12064
|
+
return []; /* @silent-fallback-ok — no pool view → module's single-machine strict no-op */
|
|
12065
|
+
}
|
|
12066
|
+
},
|
|
12067
|
+
isTopicBusy: (sessionKey) => {
|
|
12068
|
+
try {
|
|
12069
|
+
// Conservative safe-point signal: an inbound for this topic is
|
|
12070
|
+
// mid-processing. The bounded deadline in the module keeps a
|
|
12071
|
+
// false-busy from deferring forever.
|
|
12072
|
+
return currentInboundByTopic?.has(sessionKey) ?? false;
|
|
12073
|
+
}
|
|
12074
|
+
catch {
|
|
12075
|
+
return false; /* @silent-fallback-ok — unknown busy state defers to the module's bounded deadline */
|
|
12076
|
+
}
|
|
12077
|
+
},
|
|
12078
|
+
emitPlacement: (sessionKey, r, reason) => {
|
|
12079
|
+
try {
|
|
12080
|
+
const topicNum = Number(sessionKey);
|
|
12081
|
+
if (Number.isFinite(topicNum)) {
|
|
12082
|
+
state.getCoherenceJournal()?.emitPlacement(topicNum, {
|
|
12083
|
+
owner: r.record.ownerMachineId ?? '',
|
|
12084
|
+
epoch: r.record.ownershipEpoch,
|
|
12085
|
+
reason: 'reconcile',
|
|
12086
|
+
});
|
|
12087
|
+
}
|
|
12088
|
+
}
|
|
12089
|
+
catch { /* §3.3 pairing is observability — never endangers the CAS */ }
|
|
12090
|
+
},
|
|
12091
|
+
logger: (m) => console.log(pc.dim(` ${m}`)),
|
|
12092
|
+
});
|
|
12093
|
+
const tickMs = Math.max(5000, ws13Cfg().ws13TickMs ?? 30000);
|
|
12094
|
+
const ws13Timer = setInterval(() => {
|
|
12095
|
+
try {
|
|
12096
|
+
const rep = reconciler.tick();
|
|
12097
|
+
if (!rep.skipped && (rep.transfers || rep.claims || rep.forceClaims || rep.adoptions)) {
|
|
12098
|
+
console.log(pc.dim(` [OwnershipReconciler] tick: t=${rep.transfers} c=${rep.claims} f=${rep.forceClaims} a=${rep.adoptions}${rep.dryRun ? ' (dry-run)' : ''}`));
|
|
12099
|
+
}
|
|
12100
|
+
}
|
|
12101
|
+
catch (err) {
|
|
12102
|
+
console.error('[OwnershipReconciler] tick failed:', err instanceof Error ? err.message : String(err));
|
|
12103
|
+
}
|
|
12104
|
+
}, tickMs);
|
|
12105
|
+
ws13Timer.unref?.();
|
|
12106
|
+
}
|
|
12001
12107
|
// Coherence journal §3.3: the emit is a thin wrapper at every CAS call
|
|
12002
12108
|
// site — `reason` is caller knowledge (cas() is a storage primitive and
|
|
12003
12109
|
// cannot know WHY). A mesh-applied action records the coarse reason;
|
|
@@ -13068,6 +13174,17 @@ export async function startServer(options) {
|
|
|
13068
13174
|
// sending its sessions down the existing failover re-place path
|
|
13069
13175
|
// without each message re-paying the retry tax.
|
|
13070
13176
|
isMachineAlive: (m) => m === meshSelfId || ((machinePoolRegistry?.getCapacity(m)?.online ?? false) && !ownerSuspectBreaker.isSuspect(m)),
|
|
13177
|
+
// WS1.1 skew gate: read the owner's advertised receive capability
|
|
13178
|
+
// from its last heartbeat. A peer with NO flags field is an older
|
|
13179
|
+
// version → false (the conservative side: queue, don't forward into
|
|
13180
|
+
// a 501→failover steal). A peer the registry doesn't know → null
|
|
13181
|
+
// (unknown; the alive-check upstream already filters those).
|
|
13182
|
+
ownerSupportsForward: (m) => {
|
|
13183
|
+
const cap = machinePoolRegistry?.getCapacity(m);
|
|
13184
|
+
if (!cap)
|
|
13185
|
+
return null;
|
|
13186
|
+
return cap.seamlessnessFlags?.ws11DeliverReceive === true;
|
|
13187
|
+
},
|
|
13071
13188
|
markOwnerSuspect: (m) => ownerSuspectBreaker.markSuspect(m),
|
|
13072
13189
|
onOwnerResponsive: (m) => ownerSuspectBreaker.recordSuccess(m),
|
|
13073
13190
|
casClaimOwnership: (sk, machineId) => {
|