@agentvault/claude-bridge 0.7.1 → 0.7.2
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 +100 -20
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -64679,6 +64679,10 @@ var init_channel = __esm2({
|
|
|
64679
64679
|
_sessionGroupIds = /* @__PURE__ */ new Map();
|
|
64680
64680
|
/** Consecutive MLS decrypt failure count per group key for epoch recovery. */
|
|
64681
64681
|
_mlsDecryptFailCounts = /* @__PURE__ */ new Map();
|
|
64682
|
+
/** #474: per-group consecutive processCommit failures. Mirrors the decrypt
|
|
64683
|
+
* counter above — a commit that fails is divergence, and divergence must
|
|
64684
|
+
* self-heal rather than be logged and dropped. */
|
|
64685
|
+
_mlsCommitFailCounts = /* @__PURE__ */ new Map();
|
|
64682
64686
|
static MAX_MLS_DECRYPT_FAILS = 3;
|
|
64683
64687
|
/** Cached MLS KeyPackage bundle for this device (regenerated on each connect). */
|
|
64684
64688
|
_mlsKeyPackage = null;
|
|
@@ -65559,7 +65563,7 @@ var init_channel = __esm2({
|
|
|
65559
65563
|
*/
|
|
65560
65564
|
sendActivitySpan(spanData) {
|
|
65561
65565
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65562
|
-
const pluginVersion = true ? "0.23.
|
|
65566
|
+
const pluginVersion = true ? "0.23.12" : "0.0.0-dev";
|
|
65563
65567
|
const agentName = this.config.agentName ?? "Agent";
|
|
65564
65568
|
const resource = {
|
|
65565
65569
|
"service.name": "agentvault-agent",
|
|
@@ -67298,7 +67302,7 @@ var init_channel = __esm2({
|
|
|
67298
67302
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67299
67303
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67300
67304
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67301
|
-
pluginVersion: true ? "0.23.
|
|
67305
|
+
pluginVersion: true ? "0.23.12" : "0.0.0-dev"
|
|
67302
67306
|
});
|
|
67303
67307
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67304
67308
|
}
|
|
@@ -67616,7 +67620,7 @@ var init_channel = __esm2({
|
|
|
67616
67620
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67617
67621
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67618
67622
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67619
|
-
pluginVersion: true ? "0.23.
|
|
67623
|
+
pluginVersion: true ? "0.23.12" : "0.0.0-dev"
|
|
67620
67624
|
});
|
|
67621
67625
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67622
67626
|
}
|
|
@@ -69012,14 +69016,9 @@ ${messageText}`;
|
|
|
69012
69016
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69013
69017
|
console.log(`[SecureChannel] MLS commit processed for room ${roomId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69014
69018
|
} catch (err) {
|
|
69015
|
-
|
|
69016
|
-
if (isOldEpoch) {
|
|
69017
|
-
console.log(`[SecureChannel] Discarding old-epoch commit for room ${roomId.slice(0, 8)}`);
|
|
69018
|
-
} else {
|
|
69019
|
-
console.error(`[SecureChannel] MLS commit failed for room ${roomId.slice(0, 8)}:`, err);
|
|
69020
|
-
throw err;
|
|
69021
|
-
}
|
|
69019
|
+
await this._onCommitFailure(roomId, groupId, err, `room ${roomId.slice(0, 8)}`);
|
|
69022
69020
|
}
|
|
69021
|
+
this._mlsCommitFailCounts.delete(roomId);
|
|
69023
69022
|
} else {
|
|
69024
69023
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69025
69024
|
console.log(`[SecureChannel] Buffered MLS commit for room ${roomId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -69037,14 +69036,9 @@ ${messageText}`;
|
|
|
69037
69036
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69038
69037
|
console.log(`[SecureChannel] MLS commit processed for A2A ${chId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69039
69038
|
} catch (err) {
|
|
69040
|
-
|
|
69041
|
-
if (isOldEpoch) {
|
|
69042
|
-
console.log(`[SecureChannel] Discarding old-epoch commit for A2A ${chId.slice(0, 8)}`);
|
|
69043
|
-
} else {
|
|
69044
|
-
console.error(`[SecureChannel] MLS commit failed for A2A ${chId.slice(0, 8)}:`, err);
|
|
69045
|
-
throw err;
|
|
69046
|
-
}
|
|
69039
|
+
await this._onCommitFailure(`a2a:${chId}`, groupId, err, `A2A ${chId.slice(0, 8)}`);
|
|
69047
69040
|
}
|
|
69041
|
+
this._mlsCommitFailCounts.delete(`a2a:${chId}`);
|
|
69048
69042
|
} else {
|
|
69049
69043
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69050
69044
|
console.log(`[SecureChannel] Buffered MLS commit for A2A ${chId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -69101,6 +69095,52 @@ ${messageText}`;
|
|
|
69101
69095
|
* sends us one. Deduped to a 30s window per group so a burst of undecryptable
|
|
69102
69096
|
* rows doesn't spam request-welcome. Fire-and-forget.
|
|
69103
69097
|
*/
|
|
69098
|
+
/**
|
|
69099
|
+
* #474 — a failed `processCommit` is DIVERGENCE, and divergence must
|
|
69100
|
+
* self-heal. It previously did neither: the room stayed broken forever.
|
|
69101
|
+
*
|
|
69102
|
+
* Two swallow paths existed, and only the second was ever reported:
|
|
69103
|
+
*
|
|
69104
|
+
* 1. `isOldEpoch` substring-matched the error text for "epoch" / "old" /
|
|
69105
|
+
* "Validation" and discarded deliberately. `ValidationError: Desired gen
|
|
69106
|
+
* in the past` contains "Validation", so the LOUDEST production failure
|
|
69107
|
+
* was filed as benign — 1,439 occurrences on one agent, all thrown away
|
|
69108
|
+
* by a string match.
|
|
69109
|
+
* 2. Everything else re-threw to the WS dispatcher, which logs and drops.
|
|
69110
|
+
*
|
|
69111
|
+
* Result: ~1,600 commit failures across 13 rooms and ZERO resyncs. Classifying
|
|
69112
|
+
* divergence by grepping an error message is the root of it — error text is
|
|
69113
|
+
* not a protocol, and the one case that must never be silent was the one that
|
|
69114
|
+
* happened to contain the magic word.
|
|
69115
|
+
*
|
|
69116
|
+
* So: no classification. COUNT, and after MAX_MLS_DECRYPT_FAILS consecutive
|
|
69117
|
+
* failures run the same ladder the decrypt path has used since #440 — replay
|
|
69118
|
+
* the missed commits via /sync, and fall back to re-join only if that cannot
|
|
69119
|
+
* catch up. Counting rather than resyncing on first failure matters: a
|
|
69120
|
+
* genuinely stale commit is normal, and resyncing on each one would have
|
|
69121
|
+
* fired ~1,600 Welcome requests.
|
|
69122
|
+
*
|
|
69123
|
+
* Never re-throws. The caller's only handler logs and drops, so throwing here
|
|
69124
|
+
* is indistinguishable from swallowing — it just moves the swallow one frame up.
|
|
69125
|
+
*/
|
|
69126
|
+
async _onCommitFailure(groupKey, mlsGroupId, err, label) {
|
|
69127
|
+
const count = (this._mlsCommitFailCounts.get(groupKey) ?? 0) + 1;
|
|
69128
|
+
this._mlsCommitFailCounts.set(groupKey, count);
|
|
69129
|
+
console.error(
|
|
69130
|
+
`[SecureChannel] MLS commit failed (${count}/${_SecureChannel.MAX_MLS_DECRYPT_FAILS}) for ${label}:`,
|
|
69131
|
+
err
|
|
69132
|
+
);
|
|
69133
|
+
if (count < _SecureChannel.MAX_MLS_DECRYPT_FAILS) return;
|
|
69134
|
+
console.warn(`[SecureChannel] MLS commit resync triggered for ${label} \u2014 ${count} consecutive failures`);
|
|
69135
|
+
try {
|
|
69136
|
+
if (!await this._resyncOnDivergence(groupKey, mlsGroupId)) {
|
|
69137
|
+
await this._triggerMlsEpochRecovery(groupKey, mlsGroupId);
|
|
69138
|
+
}
|
|
69139
|
+
this._mlsCommitFailCounts.delete(groupKey);
|
|
69140
|
+
} catch (recoverErr) {
|
|
69141
|
+
console.error(`[SecureChannel] MLS commit resync FAILED for ${label}:`, recoverErr);
|
|
69142
|
+
}
|
|
69143
|
+
}
|
|
69104
69144
|
async _requestWelcomeSelfHeal(groupId) {
|
|
69105
69145
|
const now = Date.now();
|
|
69106
69146
|
const last = this._welcomeSelfHealInflight.get(groupId) ?? 0;
|
|
@@ -69902,9 +69942,43 @@ ${messageText}`;
|
|
|
69902
69942
|
const errMsg = addErr instanceof Error ? addErr.message : String(addErr);
|
|
69903
69943
|
if (errMsg.includes("already in the group")) {
|
|
69904
69944
|
console.log(`[SecureChannel] A2A member already in group \u2014 removing and re-adding ${req.requesting_device_id.slice(0, 8)}`);
|
|
69945
|
+
const preRemoveState = JSON.stringify(mlsGroup.exportState());
|
|
69946
|
+
let removeCommitPublished = false;
|
|
69905
69947
|
try {
|
|
69906
69948
|
const identityBytes = new TextEncoder().encode(req.requesting_device_id);
|
|
69907
|
-
|
|
69949
|
+
const leafIndex = mlsGroup.memberLeafIndex(identityBytes);
|
|
69950
|
+
if (leafIndex === null) {
|
|
69951
|
+
console.log(`[SecureChannel] A2A re-add: no leaf index for ${req.requesting_device_id.slice(0, 8)} \u2014 cannot remove`);
|
|
69952
|
+
continue;
|
|
69953
|
+
}
|
|
69954
|
+
const removeResult = await mlsGroup.removeMember(leafIndex);
|
|
69955
|
+
try {
|
|
69956
|
+
const removeResp = await fetch(
|
|
69957
|
+
`${this.config.apiUrl}/api/v1/mls/groups/${chState.mlsGroupId}/distribute`,
|
|
69958
|
+
{
|
|
69959
|
+
method: "POST",
|
|
69960
|
+
headers: { Authorization: `Bearer ${this._deviceJwt}`, "Content-Type": "application/json" },
|
|
69961
|
+
body: JSON.stringify({
|
|
69962
|
+
group_id: chState.mlsGroupId,
|
|
69963
|
+
device_id: this._deviceId,
|
|
69964
|
+
commit: Buffer.from(removeResult.commit).toString("hex"),
|
|
69965
|
+
commit_epoch: Number(mlsGroup.epoch)
|
|
69966
|
+
})
|
|
69967
|
+
}
|
|
69968
|
+
);
|
|
69969
|
+
if (removeResp.ok) {
|
|
69970
|
+
removeCommitPublished = true;
|
|
69971
|
+
} else {
|
|
69972
|
+
const detail = await removeResp.text().catch(() => "");
|
|
69973
|
+
console.warn(`[SecureChannel] A2A remove-commit distribute failed (${removeResp.status}): ${detail}`);
|
|
69974
|
+
}
|
|
69975
|
+
} catch (distErr) {
|
|
69976
|
+
console.warn(`[SecureChannel] A2A remove-commit distribute network error:`, distErr);
|
|
69977
|
+
}
|
|
69978
|
+
if (!removeCommitPublished) {
|
|
69979
|
+
mlsGroup.importState(JSON.parse(preRemoveState));
|
|
69980
|
+
continue;
|
|
69981
|
+
}
|
|
69908
69982
|
const kpRes2 = await fetch(
|
|
69909
69983
|
`${this.config.apiUrl}/api/v1/mls/key-packages/batch?device_ids=${req.requesting_device_id}`,
|
|
69910
69984
|
{ headers: { Authorization: `Bearer ${this._deviceJwt}` } }
|
|
@@ -69928,6 +70002,12 @@ ${messageText}`;
|
|
|
69928
70002
|
}
|
|
69929
70003
|
} catch (readdErr) {
|
|
69930
70004
|
console.log(`[SecureChannel] A2A remove+re-add FAILED: ${readdErr instanceof Error ? readdErr.message : String(readdErr)}`);
|
|
70005
|
+
if (!removeCommitPublished) {
|
|
70006
|
+
try {
|
|
70007
|
+
mlsGroup.importState(JSON.parse(preRemoveState));
|
|
70008
|
+
} catch {
|
|
70009
|
+
}
|
|
70010
|
+
}
|
|
69931
70011
|
continue;
|
|
69932
70012
|
}
|
|
69933
70013
|
} else {
|
|
@@ -97395,7 +97475,7 @@ var init_index = __esm2({
|
|
|
97395
97475
|
init_skill_invoker();
|
|
97396
97476
|
await init_skill_telemetry();
|
|
97397
97477
|
await init_policy_enforcer();
|
|
97398
|
-
VERSION = true ? "0.23.
|
|
97478
|
+
VERSION = true ? "0.23.12" : "0.0.0-dev";
|
|
97399
97479
|
}
|
|
97400
97480
|
});
|
|
97401
97481
|
await init_index();
|
|
@@ -133651,7 +133731,7 @@ async function main() {
|
|
|
133651
133731
|
"[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"
|
|
133652
133732
|
);
|
|
133653
133733
|
}
|
|
133654
|
-
console.error(`[bridge] version: ${true ? "0.7.
|
|
133734
|
+
console.error(`[bridge] version: ${true ? "0.7.2" : "dev"}`);
|
|
133655
133735
|
console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
133656
133736
|
console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
133657
133737
|
if (cfg.armRoom) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.2",
|
|
4
4
|
"type": "module",
|
|
5
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",
|