instar 1.3.347 → 1.3.348

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.
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA6SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAulStE;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;AA6SH,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAuxDD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAmuStE;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"}
@@ -2343,6 +2343,15 @@ export async function startServer(options) {
2343
2343
  // the reaper/ownership wiring that emits into it. Writer runs even on a
2344
2344
  // single-machine agent (locally useful); replication is P1.3.
2345
2345
  let coherenceJournal;
2346
+ // The journal's OWN machine id (used by the journal-sync SERVE path to read
2347
+ // this machine's own stream files, which are keyed on it). Hoisted so the
2348
+ // mesh dispatcher (wired much later) can drive the shared applier's
2349
+ // buildServeBatch against the right stream.
2350
+ let cjOwnMachineId;
2351
+ // ONE shared JournalSyncApplier (P1.3 engine) — drives both the journal-sync
2352
+ // RECEIVE handler (always-registered; harmless when idle) and the
2353
+ // REPLICATION-GATED puller delta drive. Constructed only when the journal is.
2354
+ let journalSyncApplier;
2346
2355
  {
2347
2356
  const cjCfg = config.multiMachine?.coherenceJournal;
2348
2357
  const cjEnabled = cjCfg?.enabled ?? !!config.developmentAgent;
@@ -2353,6 +2362,7 @@ export async function startServer(options) {
2353
2362
  // hostname-derived fallback for single-machine agents (sanitized by
2354
2363
  // the journal's own percent-encode rule either way).
2355
2364
  const cjMachineId = coordinator.identity?.machineId ?? `m_host_${os.hostname()}`;
2365
+ cjOwnMachineId = cjMachineId;
2356
2366
  coherenceJournal = new cjMod.CoherenceJournal({
2357
2367
  stateDir: config.stateDir,
2358
2368
  machineId: cjMachineId,
@@ -2367,6 +2377,26 @@ export async function startServer(options) {
2367
2377
  // Lifecycle funnel (§3.3): every session status transition flows
2368
2378
  // through StateManager.saveSession; the diff-derived emit lives there.
2369
2379
  state.setCoherenceJournal(coherenceJournal);
2380
+ // ONE shared JournalSyncApplier (P1.3 engine) — the RECEIVE/own-stream
2381
+ // SERVE side of replication. Same guardWrite seam as the writer. The
2382
+ // mesh dispatcher registers an always-on journal-sync handler over it
2383
+ // (harmless when no peer sends), and the REPLICATION-GATED puller drive
2384
+ // shares this same instance. Construction here does NOT enable
2385
+ // replication SEND/drive — that gate is config.multiMachine
2386
+ // .coherenceJournal.replication.enabled === true, checked at the puller
2387
+ // wiring below.
2388
+ try {
2389
+ const applierMod = await import('../core/JournalSyncApplier.js');
2390
+ journalSyncApplier = new applierMod.JournalSyncApplier({
2391
+ stateDir: config.stateDir,
2392
+ guardWrite: (p) => state.guardJournalWrite(p),
2393
+ logger: (m) => console.log(pc.dim(` [journal-sync] ${m}`)),
2394
+ });
2395
+ }
2396
+ catch (e) { /* @silent-fallback-ok: journal observability must never endanger the observed operation (COHERENCE-JOURNAL-SPEC §3.1) */
2397
+ journalSyncApplier = undefined;
2398
+ console.log(pc.dim(` [journal-sync] applier not constructed: ${e instanceof Error ? e.message : String(e)}`));
2399
+ }
2370
2400
  // §3.3 autonomous-run journal scanner — observation-based start/stop
2371
2401
  // (no single .local.md write funnel exists; polling is the structural
2372
2402
  // choice). P19 brakes, declared: constant per-tick cost (bounded by
@@ -9807,7 +9837,41 @@ export async function startServer(options) {
9807
9837
  },
9808
9838
  handlers: {
9809
9839
  'capacity-report': () => machinePoolRegistry?.getCapacities() ?? [],
9810
- 'session-status': () => machinePoolRegistry?.getCapacity(meshSelfId) ?? { machineId: meshSelfId },
9840
+ 'session-status': () => {
9841
+ const base = machinePoolRegistry?.getCapacity(meshSelfId) ?? { machineId: meshSelfId };
9842
+ // COHERENCE-JOURNAL-SPEC §3.4 rule 5: advertise this machine's OWN
9843
+ // durably-flushed stream heads so a peer can compute deltas. {}
9844
+ // when the journal is disabled. Forward/backward compatible — old
9845
+ // peers ignore the extra field, old callers don't read it.
9846
+ const journalAdvert = coherenceJournal
9847
+ ? { [cjOwnMachineId ?? meshSelfId]: coherenceJournal.getOwnAdvert() }
9848
+ : {};
9849
+ return { ...base, journalAdvert };
9850
+ },
9851
+ // COHERENCE-JOURNAL-SPEC §3.4 — always-registered journal-sync verb
9852
+ // (harmless when no peer sends): serve our OWN stream on `request`
9853
+ // (first-hop only), durably apply an inbound `batch`. The RBAC gate
9854
+ // already proved a registered peer; the applier's first-hop sender
9855
+ // binding fences forged entries (entry.machine must === env.sender).
9856
+ 'journal-sync': (cmd, _sender, env) => {
9857
+ const c = cmd;
9858
+ if (!journalSyncApplier)
9859
+ return { ok: false, reason: 'journal disabled' };
9860
+ if (c.request) {
9861
+ // §3.4 rule 5 serve-batch byte cap (coherenceJournal.replication
9862
+ // .maxBatchBytes; the applier defaults it when absent).
9863
+ const maxBatchBytes = config.multiMachine?.coherenceJournal?.replication?.maxBatchBytes;
9864
+ const served = journalSyncApplier.buildServeBatch(c.request.kind, c.request.fromSeq, cjOwnMachineId ?? meshSelfId, maxBatchBytes);
9865
+ return { batch: [served] };
9866
+ }
9867
+ if (c.batch) {
9868
+ // Apply binds every entry to the AUTHENTICATED envelope sender —
9869
+ // never a payload field — so a forged sender is rejected + counted.
9870
+ const r = journalSyncApplier.apply(env.sender, c.batch);
9871
+ return { ok: true, result: r };
9872
+ }
9873
+ return { ok: true };
9874
+ },
9811
9875
  place: ownAction,
9812
9876
  claim: ownAction,
9813
9877
  transfer: ownAction,
@@ -10083,21 +10147,71 @@ export async function startServer(options) {
10083
10147
  // so it authenticates off the mutual identity established at pairing —
10084
10148
  // no router role, no epoch fence required.
10085
10149
  const presenceMod = await import('../core/PeerPresencePuller.js');
10150
+ // ── REPLICATION ACTIVATION GATE (CRITICAL SAFETY) ────────────────────
10151
+ // The journal-sync SEND/drive path is gated on EXPLICIT-true only:
10152
+ // config.multiMachine.coherenceJournal.replication.enabled === true.
10153
+ // This is DELIBERATELY NOT the `?? !!developmentAgent` dark-feature
10154
+ // gate — the engine + transport land dark even on the dev agent; a
10155
+ // human flips replication on for a monitored live proof. When false
10156
+ // (the default — ConfigDefaults leaves replication.enabled absent), the
10157
+ // delta deps below are left undefined and the puller behaves EXACTLY as
10158
+ // before: a presence-only poll, no journal delta ever requested/applied.
10159
+ const _replicationEnabled = config.multiMachine?.coherenceJournal
10160
+ ?.replication?.enabled === true;
10161
+ const _journalDeltaDeps = _replicationEnabled && journalSyncApplier
10162
+ ? {
10163
+ requestJournalDelta: async (machineId, url, kind, fromSeq) => {
10164
+ try {
10165
+ const res = await meshClient.send({ machineId, url }, { type: 'journal-sync', request: { machineId, kind, fromSeq } }, 0);
10166
+ if (res.ok && res.result && typeof res.result === 'object' && 'batch' in res.result) {
10167
+ const b = res.result.batch;
10168
+ if (Array.isArray(b) && b.length > 0) {
10169
+ return b[0];
10170
+ }
10171
+ }
10172
+ }
10173
+ catch { /* @silent-fallback-ok: a journal-sync delta fetch to a peer is best-effort + self-healing — an unreachable/rejected peer simply yields no delta this pass and the next presence tick re-requests; never endanger the presence pass (COHERENCE-JOURNAL-SPEC §3.1) */ }
10174
+ return null;
10175
+ },
10176
+ applyDelta: (senderMachineId, batch) => {
10177
+ journalSyncApplier?.apply(senderMachineId, batch);
10178
+ },
10179
+ localAdvertFor: (machineId) => journalSyncApplier?.getAdvertState()[machineId] ?? {},
10180
+ }
10181
+ : {};
10182
+ if (_replicationEnabled) {
10183
+ console.log(pc.yellow(' [journal-sync] REPLICATION SEND/drive ENABLED (replication.enabled===true) — live proof mode'));
10184
+ }
10185
+ // Unwrap the PEER's own-stream slice (keyed on its machine id) from the
10186
+ // nested `{ [ownMachineId]: { kind → {…} } }` session-status advert to the
10187
+ // flat `kind → {incarnation,lastSeq}` shape the puller drive compares
10188
+ // against what we hold for that peer. First-hop: a peer serves only its
10189
+ // OWN stream, so its advert has exactly its own key (fall back to the
10190
+ // first/only entry if the key differs).
10191
+ const _unwrapPeerJournalAdvert = (machineId, nested) => {
10192
+ if (!nested || typeof nested !== 'object')
10193
+ return undefined;
10194
+ return nested[machineId] ?? Object.values(nested)[0];
10195
+ };
10086
10196
  const peerPresencePuller = new presenceMod.PeerPresencePuller({
10087
10197
  selfMachineId: meshSelfId,
10088
10198
  listPeers: () => meshIdMgr
10089
10199
  .getActiveMachines()
10090
10200
  .filter((m) => !m.entry.revokedAt)
10091
10201
  .map((m) => ({ machineId: m.machineId, url: peerUrl(m.machineId) })),
10202
+ recordHeartbeat: (obs) => { machinePoolRegistry?.recordHeartbeat(obs); },
10203
+ log: (line) => console.log(pc.dim(` [peer-presence] ${line}`)),
10092
10204
  fetchPeerCapacity: async (machineId, url) => {
10093
10205
  const res = await meshClient.send({ machineId, url }, { type: 'session-status' }, 0);
10094
10206
  if (res.ok && res.result && typeof res.result === 'object') {
10095
- return res.result;
10207
+ const cap = res.result;
10208
+ const journalAdvert = _unwrapPeerJournalAdvert(machineId, cap.journalAdvert);
10209
+ return { selfReportedLastSeen: cap.selfReportedLastSeen, loadAvg: cap.loadAvg, journalAdvert };
10096
10210
  }
10097
10211
  return null;
10098
10212
  },
10099
- recordHeartbeat: (obs) => { machinePoolRegistry?.recordHeartbeat(obs); },
10100
- log: (line) => console.log(pc.dim(` [peer-presence] ${line}`)),
10213
+ // REPLICATION-GATED: present only when replication.enabled === true.
10214
+ ..._journalDeltaDeps,
10101
10215
  });
10102
10216
  void peerPresencePuller.pullOnce();
10103
10217
  const peerPresenceTimer = setInterval(() => { void peerPresencePuller.pullOnce(); }, 30_000);