@agentvault/agentvault 0.23.20 → 0.23.21

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/index.js CHANGED
@@ -64066,6 +64066,17 @@ var init_channel = __esm({
64066
64066
  _pendingPollTimer = null;
64067
64067
  _syncMessageIds = null;
64068
64068
  _deliveryHeartbeat = null;
64069
+ /**
64070
+ * How often to re-check the KeyPackage pool against the server (#826).
64071
+ *
64072
+ * KeyPackages expire after 7 days and, before this, only a reconnect
64073
+ * republished them — so an agent that simply stayed up ran to zero usable
64074
+ * and silently stopped accepting new MLS 1:1 conversations while looking
64075
+ * perfectly healthy. 6h gives ~28 attempts inside one lifetime, so a run of
64076
+ * transient failures cannot age the pool out. Cost is one GET per interval.
64077
+ */
64078
+ static _KP_REPLENISH_INTERVAL_MS = 6 * 60 * 60 * 1e3;
64079
+ _kpReplenishTimer = null;
64069
64080
  _a2aReconcileTimer = null;
64070
64081
  _deliveryPulling = false;
64071
64082
  _drDeliveryPulling = false;
@@ -65030,7 +65041,7 @@ var init_channel = __esm({
65030
65041
  */
65031
65042
  sendActivitySpan(spanData) {
65032
65043
  if (!this._ws || this._ws.readyState !== WebSocket.OPEN) return;
65033
- const pluginVersion = true ? "0.23.20" : "0.0.0-dev";
65044
+ const pluginVersion = true ? "0.23.21" : "0.0.0-dev";
65034
65045
  const agentName = this.config.agentName ?? "Agent";
65035
65046
  const resource = {
65036
65047
  "service.name": "agentvault-agent",
@@ -66940,7 +66951,7 @@ var init_channel = __esm({
66940
66951
  agentVersion: this.config.agentVersion ?? "0.0.0",
66941
66952
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
66942
66953
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
66943
- pluginVersion: true ? "0.23.20" : "0.0.0-dev"
66954
+ pluginVersion: true ? "0.23.21" : "0.0.0-dev"
66944
66955
  });
66945
66956
  this._telemetryReporter.startAutoFlush(3e4);
66946
66957
  }
@@ -66949,6 +66960,7 @@ var init_channel = __esm({
66949
66960
  console.warn("[SecureChannel] A2A channel sync on connect failed:", err);
66950
66961
  });
66951
66962
  await this._ensureKpPoolPublished();
66963
+ this._startKpReplenishTimer();
66952
66964
  this._pullDeliveryQueue().catch((err) => {
66953
66965
  console.warn("[SecureChannel] Initial delivery pull failed:", err);
66954
66966
  });
@@ -67263,7 +67275,7 @@ var init_channel = __esm({
67263
67275
  agentVersion: this.config.agentVersion ?? "0.0.0",
67264
67276
  // __AV_VERSION__ is injected by esbuild from package.json at build time.
67265
67277
  // Falls back to "0.0.0-dev" in non-bundled contexts (tests).
67266
- pluginVersion: true ? "0.23.20" : "0.0.0-dev"
67278
+ pluginVersion: true ? "0.23.21" : "0.0.0-dev"
67267
67279
  });
67268
67280
  this._telemetryReporter.startAutoFlush(3e4);
67269
67281
  }
@@ -67606,6 +67618,7 @@ var init_channel = __esm({
67606
67618
  this._deliveryHeartbeat = null;
67607
67619
  }
67608
67620
  this._stopA2aReconcileTimer();
67621
+ this._stopKpReplenishTimer();
67609
67622
  if (this._stopped) return;
67610
67623
  this._setState("disconnected");
67611
67624
  this._scheduleReconnect();
@@ -68999,6 +69012,103 @@ ${messageText}`;
68999
69012
  this._kpPoolFilling = false;
69000
69013
  }
69001
69014
  }
69015
+ /** Start the periodic KeyPackage replenish (#826). Idempotent. */
69016
+ _startKpReplenishTimer() {
69017
+ this._stopKpReplenishTimer();
69018
+ this._kpReplenishTimer = setInterval(() => {
69019
+ void this._replenishKpPoolFromServer();
69020
+ }, _SecureChannel._KP_REPLENISH_INTERVAL_MS);
69021
+ }
69022
+ _stopKpReplenishTimer() {
69023
+ if (this._kpReplenishTimer) {
69024
+ clearInterval(this._kpReplenishTimer);
69025
+ this._kpReplenishTimer = null;
69026
+ }
69027
+ }
69028
+ /**
69029
+ * The server's EXPIRY-AWARE usable KeyPackage count, or null when unknown.
69030
+ *
69031
+ * `count_unconsumed` filters `expires_at > utcnow()`, which is the only count
69032
+ * that reflects what a peer can actually fetch. Returns null (not 0) on any
69033
+ * failure so callers can tell "server says empty" from "we could not ask" —
69034
+ * guessing 0 here would publish a fresh pool on every transient 503.
69035
+ */
69036
+ async _fetchServerKpCount() {
69037
+ try {
69038
+ const res = await fetch(
69039
+ `${this.config.apiUrl}/api/v1/mls/key-packages/count?device_id=${this._deviceId}`,
69040
+ { headers: { Authorization: `Bearer ${this._deviceJwt}` } }
69041
+ );
69042
+ if (!res.ok) return null;
69043
+ const body = await res.json();
69044
+ const n2 = body?.count;
69045
+ return typeof n2 === "number" && Number.isFinite(n2) ? n2 : null;
69046
+ } catch {
69047
+ return null;
69048
+ }
69049
+ }
69050
+ /**
69051
+ * Top the pool up against the SERVER's count (#826).
69052
+ *
69053
+ * ⚠️ DO NOT replace this with a periodic call to `_ensureKpPoolPublished()`.
69054
+ * That function early-returns on `_pendingKpBundles.length >= _KP_POOL_TARGET`,
69055
+ * and that array holds PRIVATE bundles which never expire locally — so on an
69056
+ * agent that has been connected longer than the KeyPackage lifetime it is
69057
+ * still full while the server has zero usable. Measured on prod 2026-08-09:
69058
+ * cortina and eclaude were online and green with 10 unconsumed / 0 usable,
69059
+ * unable to accept any new MLS 1:1. A timer over the local guard would have
69060
+ * run forever and changed nothing.
69061
+ *
69062
+ * Publishes only the shortfall, and never throws — this runs on a timer and
69063
+ * must not be able to take the channel down.
69064
+ */
69065
+ async _replenishKpPoolFromServer() {
69066
+ try {
69067
+ if (this._kpPoolFilling) return;
69068
+ const usable = await this._fetchServerKpCount();
69069
+ if (usable === null) return;
69070
+ const shortfall = _SecureChannel._KP_POOL_TARGET - usable;
69071
+ if (shortfall <= 0) return;
69072
+ this._kpPoolFilling = true;
69073
+ try {
69074
+ const identity = new TextEncoder().encode(this._deviceId);
69075
+ let published = 0;
69076
+ for (let i2 = 0; i2 < shortfall; i2++) {
69077
+ const mlsMgr = new MLSGroupManager();
69078
+ const kp = await mlsMgr.generateKeyPackage(identity);
69079
+ const kpHex = Buffer.from(
69080
+ MLSGroupManager.serializeKeyPackage(kp.publicPackage)
69081
+ ).toString("hex");
69082
+ const res = await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
69083
+ method: "POST",
69084
+ headers: {
69085
+ Authorization: `Bearer ${this._deviceJwt}`,
69086
+ "Content-Type": "application/json"
69087
+ },
69088
+ body: JSON.stringify({ key_package: kpHex })
69089
+ });
69090
+ if (!res.ok) {
69091
+ console.warn(
69092
+ `[SecureChannel] KeyPackage replenish rejected (status ${res.status}) after ${published}/${shortfall}`
69093
+ );
69094
+ break;
69095
+ }
69096
+ this._recordPublishedKp(kp);
69097
+ if (!this._mlsKeyPackage) this._mlsKeyPackage = kp;
69098
+ published++;
69099
+ }
69100
+ if (published > 0) {
69101
+ console.log(
69102
+ `[SecureChannel] MLS KeyPackage pool replenished (${published} new; server had ${usable}/${_SecureChannel._KP_POOL_TARGET} usable)`
69103
+ );
69104
+ }
69105
+ } finally {
69106
+ this._kpPoolFilling = false;
69107
+ }
69108
+ } catch (err) {
69109
+ console.warn("[SecureChannel] KeyPackage replenish failed:", err);
69110
+ }
69111
+ }
69002
69112
  async _handleMlsWelcome(data) {
69003
69113
  const groupId = data.group_id;
69004
69114
  const conversationId = data.conversation_id;
@@ -97592,7 +97702,7 @@ var init_index = __esm({
97592
97702
  init_skill_invoker();
97593
97703
  await init_skill_telemetry();
97594
97704
  await init_policy_enforcer();
97595
- VERSION = true ? "0.23.20" : "0.0.0-dev";
97705
+ VERSION = true ? "0.23.21" : "0.0.0-dev";
97596
97706
  }
97597
97707
  });
97598
97708
  await init_index();