@agentvault/claude-bridge 0.7.11 → 0.7.13
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 +160 -7
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -63854,6 +63854,35 @@ var init_mls_kp_pool = __esm2({
|
|
|
63854
63854
|
await init_dist();
|
|
63855
63855
|
}
|
|
63856
63856
|
});
|
|
63857
|
+
function ownIdentity() {
|
|
63858
|
+
const v22 = true ? "0.23.22" : FALLBACK;
|
|
63859
|
+
return `${PACKAGE}@${v22}`;
|
|
63860
|
+
}
|
|
63861
|
+
function buildClientVersion(override) {
|
|
63862
|
+
try {
|
|
63863
|
+
const candidate = (override ?? "").trim();
|
|
63864
|
+
if (candidate) {
|
|
63865
|
+
const cleaned = candidate.replace(DISALLOWED, "").slice(0, MAX_LEN);
|
|
63866
|
+
if (cleaned && !cleaned.includes("..")) return cleaned;
|
|
63867
|
+
}
|
|
63868
|
+
return ownIdentity().replace(DISALLOWED, "").slice(0, MAX_LEN);
|
|
63869
|
+
} catch {
|
|
63870
|
+
return FALLBACK;
|
|
63871
|
+
}
|
|
63872
|
+
}
|
|
63873
|
+
var PACKAGE;
|
|
63874
|
+
var FALLBACK;
|
|
63875
|
+
var MAX_LEN;
|
|
63876
|
+
var DISALLOWED;
|
|
63877
|
+
var init_client_version = __esm2({
|
|
63878
|
+
"src/client-version.ts"() {
|
|
63879
|
+
"use strict";
|
|
63880
|
+
PACKAGE = "@agentvault/agentvault";
|
|
63881
|
+
FALLBACK = "0.0.0-dev";
|
|
63882
|
+
MAX_LEN = 64;
|
|
63883
|
+
DISALLOWED = /[^A-Za-z0-9._@/+-]/g;
|
|
63884
|
+
}
|
|
63885
|
+
});
|
|
63857
63886
|
var CredentialStore;
|
|
63858
63887
|
var init_credential_store = __esm2({
|
|
63859
63888
|
"src/credential-store.ts"() {
|
|
@@ -64743,6 +64772,7 @@ var init_channel = __esm2({
|
|
|
64743
64772
|
await init_dist();
|
|
64744
64773
|
init_mls_state();
|
|
64745
64774
|
await init_mls_kp_pool();
|
|
64775
|
+
init_client_version();
|
|
64746
64776
|
init_credential_store();
|
|
64747
64777
|
await init_crypto_helpers();
|
|
64748
64778
|
await init_state();
|
|
@@ -64828,6 +64858,17 @@ var init_channel = __esm2({
|
|
|
64828
64858
|
_pendingPollTimer = null;
|
|
64829
64859
|
_syncMessageIds = null;
|
|
64830
64860
|
_deliveryHeartbeat = null;
|
|
64861
|
+
/**
|
|
64862
|
+
* How often to re-check the KeyPackage pool against the server (#826).
|
|
64863
|
+
*
|
|
64864
|
+
* KeyPackages expire after 7 days and, before this, only a reconnect
|
|
64865
|
+
* republished them — so an agent that simply stayed up ran to zero usable
|
|
64866
|
+
* and silently stopped accepting new MLS 1:1 conversations while looking
|
|
64867
|
+
* perfectly healthy. 6h gives ~28 attempts inside one lifetime, so a run of
|
|
64868
|
+
* transient failures cannot age the pool out. Cost is one GET per interval.
|
|
64869
|
+
*/
|
|
64870
|
+
static _KP_REPLENISH_INTERVAL_MS = 6 * 60 * 60 * 1e3;
|
|
64871
|
+
_kpReplenishTimer = null;
|
|
64831
64872
|
_a2aReconcileTimer = null;
|
|
64832
64873
|
_deliveryPulling = false;
|
|
64833
64874
|
_drDeliveryPulling = false;
|
|
@@ -65792,7 +65833,7 @@ var init_channel = __esm2({
|
|
|
65792
65833
|
*/
|
|
65793
65834
|
sendActivitySpan(spanData) {
|
|
65794
65835
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65795
|
-
const pluginVersion = true ? "0.23.
|
|
65836
|
+
const pluginVersion = true ? "0.23.22" : "0.0.0-dev";
|
|
65796
65837
|
const agentName = this.config.agentName ?? "Agent";
|
|
65797
65838
|
const resource = {
|
|
65798
65839
|
"service.name": "agentvault-agent",
|
|
@@ -67661,7 +67702,7 @@ var init_channel = __esm2({
|
|
|
67661
67702
|
return;
|
|
67662
67703
|
}
|
|
67663
67704
|
const wsUrl = this.config.apiUrl.replace(/^http/, "ws");
|
|
67664
|
-
const url22 = `${wsUrl}/api/v1/ws?token=${encodeURIComponent(this._deviceJwt)}&device_id=${this._deviceId}`;
|
|
67705
|
+
const url22 = `${wsUrl}/api/v1/ws?token=${encodeURIComponent(this._deviceJwt)}&device_id=${this._deviceId}&client_version=${encodeURIComponent(buildClientVersion(this.config.clientVersion))}`;
|
|
67665
67706
|
const ws = new WebSocket2(url22);
|
|
67666
67707
|
this._ws = ws;
|
|
67667
67708
|
ws.on("open", async () => {
|
|
@@ -67702,7 +67743,7 @@ var init_channel = __esm2({
|
|
|
67702
67743
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67703
67744
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67704
67745
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67705
|
-
pluginVersion: true ? "0.23.
|
|
67746
|
+
pluginVersion: true ? "0.23.22" : "0.0.0-dev"
|
|
67706
67747
|
});
|
|
67707
67748
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67708
67749
|
}
|
|
@@ -67711,6 +67752,7 @@ var init_channel = __esm2({
|
|
|
67711
67752
|
console.warn("[SecureChannel] A2A channel sync on connect failed:", err);
|
|
67712
67753
|
});
|
|
67713
67754
|
await this._ensureKpPoolPublished();
|
|
67755
|
+
this._startKpReplenishTimer();
|
|
67714
67756
|
this._pullDeliveryQueue().catch((err) => {
|
|
67715
67757
|
console.warn("[SecureChannel] Initial delivery pull failed:", err);
|
|
67716
67758
|
});
|
|
@@ -68025,7 +68067,7 @@ var init_channel = __esm2({
|
|
|
68025
68067
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
68026
68068
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
68027
68069
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
68028
|
-
pluginVersion: true ? "0.23.
|
|
68070
|
+
pluginVersion: true ? "0.23.22" : "0.0.0-dev"
|
|
68029
68071
|
});
|
|
68030
68072
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
68031
68073
|
}
|
|
@@ -68368,6 +68410,7 @@ var init_channel = __esm2({
|
|
|
68368
68410
|
this._deliveryHeartbeat = null;
|
|
68369
68411
|
}
|
|
68370
68412
|
this._stopA2aReconcileTimer();
|
|
68413
|
+
this._stopKpReplenishTimer();
|
|
68371
68414
|
if (this._stopped) return;
|
|
68372
68415
|
this._setState("disconnected");
|
|
68373
68416
|
this._scheduleReconnect();
|
|
@@ -69761,6 +69804,106 @@ ${messageText}`;
|
|
|
69761
69804
|
this._kpPoolFilling = false;
|
|
69762
69805
|
}
|
|
69763
69806
|
}
|
|
69807
|
+
/** Start the periodic KeyPackage replenish (#826). Idempotent. */
|
|
69808
|
+
_startKpReplenishTimer() {
|
|
69809
|
+
this._stopKpReplenishTimer();
|
|
69810
|
+
this._kpReplenishTimer = setInterval(() => {
|
|
69811
|
+
void this._replenishKpPoolFromServer();
|
|
69812
|
+
}, _SecureChannel._KP_REPLENISH_INTERVAL_MS);
|
|
69813
|
+
}
|
|
69814
|
+
_stopKpReplenishTimer() {
|
|
69815
|
+
if (this._kpReplenishTimer) {
|
|
69816
|
+
clearInterval(this._kpReplenishTimer);
|
|
69817
|
+
this._kpReplenishTimer = null;
|
|
69818
|
+
}
|
|
69819
|
+
}
|
|
69820
|
+
/**
|
|
69821
|
+
* The server's EXPIRY-AWARE usable KeyPackage count, or null when unknown.
|
|
69822
|
+
*
|
|
69823
|
+
* `count_unconsumed` filters `expires_at > utcnow()`, which is the only count
|
|
69824
|
+
* that reflects what a peer can actually fetch. Returns null (not 0) on any
|
|
69825
|
+
* failure so callers can tell "server says empty" from "we could not ask" —
|
|
69826
|
+
* guessing 0 here would publish a fresh pool on every transient 503.
|
|
69827
|
+
*/
|
|
69828
|
+
async _fetchServerKpCount() {
|
|
69829
|
+
try {
|
|
69830
|
+
const horizonSeconds = Math.floor(
|
|
69831
|
+
_SecureChannel._KP_REPLENISH_INTERVAL_MS * 2 / 1e3
|
|
69832
|
+
);
|
|
69833
|
+
const res = await fetch(
|
|
69834
|
+
`${this.config.apiUrl}/api/v1/mls/key-packages/count?device_id=${this._deviceId}&renew_within_seconds=${horizonSeconds}`,
|
|
69835
|
+
{ headers: { Authorization: `Bearer ${this._deviceJwt}` } }
|
|
69836
|
+
);
|
|
69837
|
+
if (!res.ok) return null;
|
|
69838
|
+
const body = await res.json();
|
|
69839
|
+
const n22 = body?.count;
|
|
69840
|
+
return typeof n22 === "number" && Number.isFinite(n22) ? n22 : null;
|
|
69841
|
+
} catch {
|
|
69842
|
+
return null;
|
|
69843
|
+
}
|
|
69844
|
+
}
|
|
69845
|
+
/**
|
|
69846
|
+
* Top the pool up against the SERVER's count (#826).
|
|
69847
|
+
*
|
|
69848
|
+
* ⚠️ DO NOT replace this with a periodic call to `_ensureKpPoolPublished()`.
|
|
69849
|
+
* That function early-returns on `_pendingKpBundles.length >= _KP_POOL_TARGET`,
|
|
69850
|
+
* and that array holds PRIVATE bundles which never expire locally — so on an
|
|
69851
|
+
* agent that has been connected longer than the KeyPackage lifetime it is
|
|
69852
|
+
* still full while the server has zero usable. Measured on prod 2026-08-09:
|
|
69853
|
+
* cortina and eclaude were online and green with 10 unconsumed / 0 usable,
|
|
69854
|
+
* unable to accept any new MLS 1:1. A timer over the local guard would have
|
|
69855
|
+
* run forever and changed nothing.
|
|
69856
|
+
*
|
|
69857
|
+
* Publishes only the shortfall, and never throws — this runs on a timer and
|
|
69858
|
+
* must not be able to take the channel down.
|
|
69859
|
+
*/
|
|
69860
|
+
async _replenishKpPoolFromServer() {
|
|
69861
|
+
try {
|
|
69862
|
+
if (this._kpPoolFilling) return;
|
|
69863
|
+
const usable = await this._fetchServerKpCount();
|
|
69864
|
+
if (usable === null) return;
|
|
69865
|
+
const shortfall = _SecureChannel._KP_POOL_TARGET - usable;
|
|
69866
|
+
if (shortfall <= 0) return;
|
|
69867
|
+
this._kpPoolFilling = true;
|
|
69868
|
+
try {
|
|
69869
|
+
const identity = new TextEncoder().encode(this._deviceId);
|
|
69870
|
+
let published = 0;
|
|
69871
|
+
for (let i2 = 0; i2 < shortfall; i2++) {
|
|
69872
|
+
const mlsMgr = new MLSGroupManager();
|
|
69873
|
+
const kp = await mlsMgr.generateKeyPackage(identity);
|
|
69874
|
+
const kpHex = Buffer.from(
|
|
69875
|
+
MLSGroupManager.serializeKeyPackage(kp.publicPackage)
|
|
69876
|
+
).toString("hex");
|
|
69877
|
+
const res = await fetch(`${this.config.apiUrl}/api/v1/mls/key-packages`, {
|
|
69878
|
+
method: "POST",
|
|
69879
|
+
headers: {
|
|
69880
|
+
Authorization: `Bearer ${this._deviceJwt}`,
|
|
69881
|
+
"Content-Type": "application/json"
|
|
69882
|
+
},
|
|
69883
|
+
body: JSON.stringify({ key_package: kpHex })
|
|
69884
|
+
});
|
|
69885
|
+
if (!res.ok) {
|
|
69886
|
+
console.warn(
|
|
69887
|
+
`[SecureChannel] KeyPackage replenish rejected (status ${res.status}) after ${published}/${shortfall}`
|
|
69888
|
+
);
|
|
69889
|
+
break;
|
|
69890
|
+
}
|
|
69891
|
+
this._recordPublishedKp(kp);
|
|
69892
|
+
if (!this._mlsKeyPackage) this._mlsKeyPackage = kp;
|
|
69893
|
+
published++;
|
|
69894
|
+
}
|
|
69895
|
+
if (published > 0) {
|
|
69896
|
+
console.log(
|
|
69897
|
+
`[SecureChannel] MLS KeyPackage pool replenished (${published} new; server had ${usable}/${_SecureChannel._KP_POOL_TARGET} usable)`
|
|
69898
|
+
);
|
|
69899
|
+
}
|
|
69900
|
+
} finally {
|
|
69901
|
+
this._kpPoolFilling = false;
|
|
69902
|
+
}
|
|
69903
|
+
} catch (err) {
|
|
69904
|
+
console.warn("[SecureChannel] KeyPackage replenish failed:", err);
|
|
69905
|
+
}
|
|
69906
|
+
}
|
|
69764
69907
|
async _handleMlsWelcome(data) {
|
|
69765
69908
|
const groupId = data.group_id;
|
|
69766
69909
|
const conversationId = data.conversation_id;
|
|
@@ -97914,7 +98057,7 @@ var init_index = __esm2({
|
|
|
97914
98057
|
init_skill_invoker();
|
|
97915
98058
|
await init_skill_telemetry();
|
|
97916
98059
|
await init_policy_enforcer();
|
|
97917
|
-
VERSION = true ? "0.23.
|
|
98060
|
+
VERSION = true ? "0.23.22" : "0.0.0-dev";
|
|
97918
98061
|
}
|
|
97919
98062
|
});
|
|
97920
98063
|
await init_index();
|
|
@@ -134268,7 +134411,7 @@ async function main() {
|
|
|
134268
134411
|
"[bridge] warning: passing the invite token on the command line is visible to other local users via 'ps'. Prefer: AV_INVITE_TOKEN=\u2026 npx @agentvault/claude-bridge"
|
|
134269
134412
|
);
|
|
134270
134413
|
}
|
|
134271
|
-
logLine(`[bridge] version: ${true ? "0.7.
|
|
134414
|
+
logLine(`[bridge] version: ${true ? "0.7.13" : "dev"}`);
|
|
134272
134415
|
logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
134273
134416
|
logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
134274
134417
|
if (cfg.armRoom) {
|
|
@@ -134290,7 +134433,17 @@ async function main() {
|
|
|
134290
134433
|
dataDir: cfg.dataDir,
|
|
134291
134434
|
apiUrl: cfg.apiUrl,
|
|
134292
134435
|
agentName: cfg.agentName,
|
|
134293
|
-
platform: "node"
|
|
134436
|
+
platform: "node",
|
|
134437
|
+
// #836: identify the BRIDGE, not the plugin it vendors. Two reasons this
|
|
134438
|
+
// must be explicit. Operators install and upgrade the bridge, so the bridge
|
|
134439
|
+
// build is the one they can act on — reporting the inner plugin version is
|
|
134440
|
+
// the exact confusion #836 was opened about (a bridge on 0.3.2 read as
|
|
134441
|
+
// "same bug, didn't restart"). And because this build BUNDLES the plugin,
|
|
134442
|
+
// the plugin's own `__AV_VERSION__` resolves to OUR package.json here, so
|
|
134443
|
+
// its default would render "@agentvault/agentvault@0.7.x" — the wrong
|
|
134444
|
+
// package name attached to the bridge's version number, which is worse
|
|
134445
|
+
// than either alone.
|
|
134446
|
+
clientVersion: `@agentvault/claude-bridge@${true ? "0.7.13" : "dev"}`
|
|
134294
134447
|
});
|
|
134295
134448
|
const agentSystemPrompt = cfg.systemPrompt ?? `You are ${cfg.agentName}, an AI agent on AgentVault. You talk with your owner in 1:1 direct messages and collaborate with other agents in shared rooms. That is your identity \u2014 introduce yourself by that name and do not claim to be any other agent. To speak, call the say tool. In a 1:1 with your owner, reply to what they say. In a room you see every message \u2014 call say only when you have something worth adding, and otherwise stay silent. Keep messages concise.`;
|
|
134296
134449
|
const deviceJwt = () => {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.13",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "AgentVault Claude Bridge
|
|
5
|
+
"description": "AgentVault Claude Bridge — daemon for bridging a Claude agent into secure E2E-encrypted AgentVault 1:1 direct messages and rooms.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|