tesseract-agent 0.11.0 → 0.11.1

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.
@@ -109820,6 +109820,36 @@ async function getPeerIdentityPub(opts) {
109820
109820
  import { createSocket as createSocket2 } from "node:dgram";
109821
109821
  import { createRequire as createRequire4 } from "node:module";
109822
109822
 
109823
+ // ../../packages/host-core/dist/mesh/bundle-gossip.js
109824
+ var BundleOfferLedger = class {
109825
+ #offered = /* @__PURE__ */ new Set();
109826
+ /**
109827
+ * Should we offer epoch `mine` to this peer, and record that we did?
109828
+ *
109829
+ * Returns false when we are not ahead (including equal — an offer would tell them
109830
+ * nothing), and false for a peer already offered THIS epoch. Keyed by epoch as well as
109831
+ * peer, so a newer bundle is offered afresh to someone we have already talked to.
109832
+ */
109833
+ claim(peerHex, peerEpoch, mine) {
109834
+ if (!(mine > peerEpoch))
109835
+ return false;
109836
+ const key2 = `${peerHex}:${mine}`;
109837
+ if (this.#offered.has(key2))
109838
+ return false;
109839
+ this.#offered.add(key2);
109840
+ return true;
109841
+ }
109842
+ /**
109843
+ * Forget everything, because we just adopted a newer bundle and are now the node that is
109844
+ * ahead. Without this the ledger would still be keyed to the epoch we no longer hold —
109845
+ * harmless for correctness (the key carries the epoch) but it would grow without bound on
109846
+ * a long-lived node, one entry per peer per epoch forever.
109847
+ */
109848
+ reset() {
109849
+ this.#offered.clear();
109850
+ }
109851
+ };
109852
+
109823
109853
  // ../../packages/host-core/dist/mesh/config.js
109824
109854
  init_credentials();
109825
109855
  import { chmodSync as chmodSync5, existsSync as existsSync33, mkdirSync as mkdirSync36, readdirSync as readdirSync12, readFileSync as readFileSync47, renameSync as renameSync11, rmSync as rmSync7, writeFileSync as writeFileSync35 } from "node:fs";
@@ -110289,6 +110319,12 @@ var MeshNode = class {
110289
110319
  #timer = null;
110290
110320
  #unsubscribe = null;
110291
110321
  #identity = null;
110322
+ /** The policy bundle we hold, as bytes, so we can OFFER it to a peer that is behind.
110323
+ * Kept beside the engine because the engine exposes an epoch but not the bytes. */
110324
+ #bundle = null;
110325
+ /** Who has already been offered which epoch — see `bundle-gossip.ts` for why a ledger
110326
+ * is needed at all (the relay broadcasts to the whole mesh, not to one peer). */
110327
+ #bundleOffered = new BundleOfferLedger();
110292
110328
  #port = null;
110293
110329
  #lastError = null;
110294
110330
  /**
@@ -110405,8 +110441,10 @@ var MeshNode = class {
110405
110441
  }
110406
110442
  try {
110407
110443
  this.#engine = new meshCore.MeshEngine(Buffer.from(id.genesis), Buffer.from(id.staticPrivate), Buffer.from(id.cert), 0, Buffer.alloc(16));
110408
- if (id.bundle)
110444
+ if (id.bundle) {
110409
110445
  this.#engine.setBundle(Buffer.from(id.bundle), Date.now());
110446
+ this.#bundle = id.bundle;
110447
+ }
110410
110448
  } catch (e) {
110411
110449
  this.#engine = null;
110412
110450
  this.#lastError = e instanceof Error ? e.message : String(e);
@@ -110653,6 +110691,8 @@ var MeshNode = class {
110653
110691
  engine.setBundle(bytes2, Date.now());
110654
110692
  this.#stopIfRevoked(engine);
110655
110693
  cacheBundle(new Uint8Array(bytes2), meshDirNameFromWireId(engine.meshId()) ?? void 0);
110694
+ this.#bundle = new Uint8Array(bytes2);
110695
+ this.#bundleOffered.reset();
110656
110696
  break;
110657
110697
  }
110658
110698
  }
@@ -110661,6 +110701,48 @@ var MeshNode = class {
110661
110701
  this.#lastError = e instanceof Error ? e.message : String(e);
110662
110702
  }
110663
110703
  }
110704
+ /**
110705
+ * Offer our policy bundle to a peer whose epoch is older than ours.
110706
+ *
110707
+ * ## The half of gossip that was never written
110708
+ *
110709
+ * `MeshPeerHint.epoch` is documented in the protocol as "the epoch the node advertises,
110710
+ * SO A LAGGING PEER KNOWS TO PULL", and the relay has always carried it. Nothing read
110711
+ * it, and — the part that made this total — nothing in the product ever SENT a
110712
+ * `mesh:bundle`. The broker forwarded a message with no sender anywhere.
110713
+ *
110714
+ * So a node's roster was frozen at the instant it joined. `joinWithToken` caches the
110715
+ * bundle the server returns, which is why a machine works at all; but every machine that
110716
+ * joins LATER is invisible to it forever, because the only thing that would have told it
110717
+ * is the gossip that never happened. On a four-machine network built in join order, the
110718
+ * first machine knew nobody, the last knew everyone, and every one of them reported
110719
+ * itself healthy. That is what this fixes.
110720
+ *
110721
+ * PUSH, not pull, because push needs no new message type and the protocol already hands
110722
+ * us exactly the fact we need — the peer's epoch. For any pair, whoever is ahead offers,
110723
+ * so the direction takes care of itself and a new joiner (who always has the newest
110724
+ * bundle, straight from the server) feeds it to everyone already there.
110725
+ *
110726
+ * Safe by construction: the bundle is CA-signed, so `setBundle` on the receiving side
110727
+ * verifies it against genesis and refuses anything that does not chain. Offering is
110728
+ * therefore never a policy change — the worst a wrong offer costs a peer is a parse.
110729
+ */
110730
+ #offerBundleTo(peerHex, peerEpoch) {
110731
+ const engine = this.#engine;
110732
+ const bundle = this.#bundle;
110733
+ if (!engine || !bundle)
110734
+ return;
110735
+ if (!this.#bundleOffered.claim(peerHex, peerEpoch, engine.policyEpoch()))
110736
+ return;
110737
+ try {
110738
+ sendMesh({
110739
+ type: "mesh:bundle",
110740
+ meshId: engine.meshId(),
110741
+ bundle: Buffer.from(bundle).toString("base64")
110742
+ });
110743
+ } catch {
110744
+ }
110745
+ }
110664
110746
  #learnPeer(p) {
110665
110747
  const engine = this.#engine;
110666
110748
  if (!engine)
@@ -110669,6 +110751,7 @@ var MeshNode = class {
110669
110751
  if (pk.length !== 32)
110670
110752
  return;
110671
110753
  const hex6 = pk.toString("hex");
110754
+ this.#offerBundleTo(hex6, p.epoch ?? 0);
110672
110755
  if (p.cert) {
110673
110756
  try {
110674
110757
  const bytes2 = Buffer.from(p.cert, "base64");
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fineguide-tesseract/mesh-core",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tesseract-agent",
3
- "version": "0.11.0",
3
+ "version": "0.11.1",
4
4
  "license": "UNLICENSED",
5
5
  "publishConfig": {
6
6
  "access": "public",