@agentvault/claude-bridge 0.7.3 → 0.7.5
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 +243 -25
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -64616,6 +64616,8 @@ var OUTBOUND_DEDUPE_WINDOW_MS;
|
|
|
64616
64616
|
var OUTBOUND_DEDUPE_MAX_SUPPRESS_MS;
|
|
64617
64617
|
var OUTBOUND_DEDUPE_MAX;
|
|
64618
64618
|
var OUTBOUND_DEDUPE_TYPES;
|
|
64619
|
+
var RETRY_CANDIDATE_TTL_MS;
|
|
64620
|
+
var RETRY_CANDIDATE_MAX;
|
|
64619
64621
|
var SecureChannel;
|
|
64620
64622
|
var init_channel = __esm2({
|
|
64621
64623
|
async "src/channel.ts"() {
|
|
@@ -64653,6 +64655,8 @@ var init_channel = __esm2({
|
|
|
64653
64655
|
OUTBOUND_DEDUPE_MAX_SUPPRESS_MS = 15e3;
|
|
64654
64656
|
OUTBOUND_DEDUPE_MAX = 256;
|
|
64655
64657
|
OUTBOUND_DEDUPE_TYPES = /* @__PURE__ */ new Set(["text", "status_alert", "artifact", "attachment"]);
|
|
64658
|
+
RETRY_CANDIDATE_TTL_MS = 6e4;
|
|
64659
|
+
RETRY_CANDIDATE_MAX = 32;
|
|
64656
64660
|
SecureChannel = class _SecureChannel extends EventEmitter {
|
|
64657
64661
|
constructor(config22) {
|
|
64658
64662
|
super();
|
|
@@ -64730,6 +64734,24 @@ var init_channel = __esm2({
|
|
|
64730
64734
|
_pendingKpBundles = [];
|
|
64731
64735
|
/** Pool target: keep this many unconsumed KeyPackages published; backend caps to match. */
|
|
64732
64736
|
static _KP_POOL_TARGET = 10;
|
|
64737
|
+
/**
|
|
64738
|
+
* Local retention bound for `_pendingKpBundles` (#363). Deliberately ABOVE
|
|
64739
|
+
* `_KP_POOL_TARGET`, because the two counts are in DIFFERENT UNITS: the backend
|
|
64740
|
+
* keeps up to `_KP_POOL_TARGET` **unconsumed** KeyPackages, while this array
|
|
64741
|
+
* holds every bundle published since the last join — consumed ones are removed
|
|
64742
|
+
* only by `_removeKpFromPool` on a successful join.
|
|
64743
|
+
*
|
|
64744
|
+
* Capping local retention AT the backend's target therefore evicts bundles the
|
|
64745
|
+
* backend can still serve, and their private material cannot be rebuilt (#341:
|
|
64746
|
+
* `mls_rs_uniffi` exposes no KeyPackage repository), so any Welcome minted
|
|
64747
|
+
* against an evicted bundle is PERMANENTLY unjoinable.
|
|
64748
|
+
*
|
|
64749
|
+
* Same defect and same remedy as #511, where the web store capped 6 against a
|
|
64750
|
+
* backend pool of 10 and was raised to 12. Retaining extra bundles is cheap and
|
|
64751
|
+
* purely protective — `_handleMlsWelcome` trial-decrypts against the whole pool,
|
|
64752
|
+
* so a surplus only ever adds chances to match.
|
|
64753
|
+
*/
|
|
64754
|
+
static _KP_POOL_RETENTION = 20;
|
|
64733
64755
|
/** Reentrancy guard so concurrent pool top-ups don't double-publish past target. */
|
|
64734
64756
|
_kpPoolFilling = false;
|
|
64735
64757
|
/** Buffer for MLS commits received before Welcome (keyed by groupId, sorted by epoch). */
|
|
@@ -64754,6 +64776,20 @@ var init_channel = __esm2({
|
|
|
64754
64776
|
* single delivery. In-memory only (short window; no cross-restart persistence needed).
|
|
64755
64777
|
*/
|
|
64756
64778
|
_recentDeliveries = /* @__PURE__ */ new Map();
|
|
64779
|
+
/**
|
|
64780
|
+
* Last outbound plaintext per MLS group id (#732), so a message refused with
|
|
64781
|
+
* `unknown group` can be resent once the map is reconciled.
|
|
64782
|
+
*
|
|
64783
|
+
* PLAINTEXT, not the frame: the refused frame's `payload` is ciphertext
|
|
64784
|
+
* encrypted to a group that no longer exists, so replaying those bytes into
|
|
64785
|
+
* the replacement group would store something nobody can decrypt. The retry
|
|
64786
|
+
* has to encrypt again, which means keeping what was encrypted.
|
|
64787
|
+
*
|
|
64788
|
+
* In-memory only, TTL-bounded, and consumed on first use — this exists for
|
|
64789
|
+
* the ~50ms between a frame reaching the socket and the server refusing it,
|
|
64790
|
+
* not as a durable outbox (that is #688's dead-letter).
|
|
64791
|
+
*/
|
|
64792
|
+
_retryCandidates = /* @__PURE__ */ new Map();
|
|
64757
64793
|
_scanEngine = null;
|
|
64758
64794
|
_scanRuleSetVersion = 0;
|
|
64759
64795
|
_telemetryReporter = null;
|
|
@@ -64853,13 +64889,21 @@ var init_channel = __esm2({
|
|
|
64853
64889
|
* Non-fatal: a transient network error or 5xx keeps the current JWT and
|
|
64854
64890
|
* relies on the next reconnect. Terminal failures (401/403) surface via
|
|
64855
64891
|
* the `auth_failed` event so callers can prompt the user to recover.
|
|
64892
|
+
*
|
|
64893
|
+
* Returns TRUE when the credentials are terminally dead. #743: the caller
|
|
64894
|
+
* MUST NOT go on to open a WebSocket in that case — the server has already
|
|
64895
|
+
* told us this credential can never be served, and connecting anyway is what
|
|
64896
|
+
* produced `ws_guard action=denylist` on repeat in prod (device 0e87ff50, a
|
|
64897
|
+
* hard-deleted device, ~12/hour). The verdict is returned explicitly rather
|
|
64898
|
+
* than read back off `_authFailedThisSession`, whose lifetime is reset per
|
|
64899
|
+
* connect and is easy to get subtly wrong.
|
|
64856
64900
|
*/
|
|
64857
64901
|
async _maybeReissueDeviceJwt() {
|
|
64858
64902
|
const jwt22 = this._persisted?.deviceJwt ?? this._deviceJwt;
|
|
64859
|
-
if (!jwt22) return;
|
|
64903
|
+
if (!jwt22) return false;
|
|
64860
64904
|
const exp = this._decodeJwtExp(jwt22);
|
|
64861
64905
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
64862
|
-
if (exp - nowSec >= 30 * 86400) return;
|
|
64906
|
+
if (exp - nowSec >= 30 * 86400) return false;
|
|
64863
64907
|
try {
|
|
64864
64908
|
const resp = await fetch(`${this.config.apiUrl}/api/v1/auth/reissue`, {
|
|
64865
64909
|
method: "POST",
|
|
@@ -64879,7 +64923,7 @@ var init_channel = __esm2({
|
|
|
64879
64923
|
} else {
|
|
64880
64924
|
console.warn("[SecureChannel] reissue returned 200 but no device_jwt in response");
|
|
64881
64925
|
}
|
|
64882
|
-
return;
|
|
64926
|
+
return false;
|
|
64883
64927
|
}
|
|
64884
64928
|
if (resp.status === 401 || resp.status === 403) {
|
|
64885
64929
|
console.warn(`[SecureChannel] reissue rejected (${resp.status}); credentials need attention`);
|
|
@@ -64887,12 +64931,13 @@ var init_channel = __esm2({
|
|
|
64887
64931
|
const reason = resp.status === 401 ? "device_jwt_expired" : "device_revoked";
|
|
64888
64932
|
await this._postAuthFailedToBackend(reason);
|
|
64889
64933
|
this.emit("auth_failed", { reason });
|
|
64890
|
-
return;
|
|
64934
|
+
return true;
|
|
64891
64935
|
}
|
|
64892
64936
|
console.warn(`[SecureChannel] reissue ${resp.status}; keeping current jwt`);
|
|
64893
64937
|
} catch (err) {
|
|
64894
64938
|
console.warn("[SecureChannel] reissue network error; keeping current jwt:", err);
|
|
64895
64939
|
}
|
|
64940
|
+
return false;
|
|
64896
64941
|
}
|
|
64897
64942
|
/**
|
|
64898
64943
|
* Wraps `fetch` for authenticated AV REST endpoints with reactive device-JWT
|
|
@@ -65378,7 +65423,9 @@ var init_channel = __esm2({
|
|
|
65378
65423
|
}
|
|
65379
65424
|
scanStatus = scanResult.status;
|
|
65380
65425
|
}
|
|
65381
|
-
|
|
65426
|
+
if (!options?.isResend) {
|
|
65427
|
+
this._appendHistory("agent", plaintext, topicId);
|
|
65428
|
+
}
|
|
65382
65429
|
const roomConvIds = /* @__PURE__ */ new Set();
|
|
65383
65430
|
if (this._persisted?.rooms) {
|
|
65384
65431
|
for (const room of Object.values(this._persisted.rooms)) {
|
|
@@ -65392,8 +65439,11 @@ var init_channel = __esm2({
|
|
|
65392
65439
|
let sentCount = 0;
|
|
65393
65440
|
const pendingWsSends = [];
|
|
65394
65441
|
const sentSharedGroupIds = /* @__PURE__ */ new Set();
|
|
65442
|
+
const addressedMlsGroupIds = [];
|
|
65395
65443
|
if (this._persisted?.mlsGroups && this._state === "ready" && this._ws) {
|
|
65444
|
+
const targetSharedGid = targetConvId ? this._sessionGroupIds?.get(targetConvId) : void 0;
|
|
65396
65445
|
for (const [gid, entry] of Object.entries(this._persisted.mlsGroups)) {
|
|
65446
|
+
if (targetConvId && gid !== targetSharedGid) continue;
|
|
65397
65447
|
if (!entry.mlsGroupId) continue;
|
|
65398
65448
|
const mlsGroup = this._mlsGroups.get(`1to1-group:${gid}`);
|
|
65399
65449
|
if (!mlsGroup?.isInitialized || Number(mlsGroup.epoch) <= 0) continue;
|
|
@@ -65415,6 +65465,7 @@ var init_channel = __esm2({
|
|
|
65415
65465
|
if (this._persisted?.hubAddress) payload.hub_address = this._persisted.hubAddress;
|
|
65416
65466
|
if (this._persisted?.hubId) payload.sender_hub_id = this._persisted.hubId;
|
|
65417
65467
|
pendingWsSends.push(JSON.stringify({ event: "message_mls", data: payload }));
|
|
65468
|
+
addressedMlsGroupIds.push(entry.mlsGroupId);
|
|
65418
65469
|
sentSharedGroupIds.add(gid);
|
|
65419
65470
|
sentCount++;
|
|
65420
65471
|
console.log(`[SecureChannel] Shared MLS group send for group ${gid.slice(0, 8)} (${entry.mlsGroupId.slice(0, 8)})`);
|
|
@@ -65467,6 +65518,7 @@ var init_channel = __esm2({
|
|
|
65467
65518
|
data: payload
|
|
65468
65519
|
})
|
|
65469
65520
|
);
|
|
65521
|
+
addressedMlsGroupIds.push(mlsGroupId);
|
|
65470
65522
|
if (convGroupId) sentSharedGroupIds.add(convGroupId);
|
|
65471
65523
|
} else {
|
|
65472
65524
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
@@ -65552,6 +65604,7 @@ var init_channel = __esm2({
|
|
|
65552
65604
|
if (this._persisted?.hubAddress) payload.hub_address = this._persisted.hubAddress;
|
|
65553
65605
|
if (this._persisted?.hubId) payload.sender_hub_id = this._persisted.hubId;
|
|
65554
65606
|
pendingWsSends.push(JSON.stringify({ event: "message_mls", data: payload }));
|
|
65607
|
+
addressedMlsGroupIds.push(resolvedMlsGroupId);
|
|
65555
65608
|
sentCount++;
|
|
65556
65609
|
if (mlsOnlyConvGroupId) sentSharedGroupIds.add(mlsOnlyConvGroupId);
|
|
65557
65610
|
console.log(`[SecureChannel] MLS-only send for conv ${mlsConvId.slice(0, 8)} (no DR session)`);
|
|
@@ -65567,6 +65620,28 @@ var init_channel = __esm2({
|
|
|
65567
65620
|
for (const frame of pendingWsSends) {
|
|
65568
65621
|
this._ws.send(frame);
|
|
65569
65622
|
}
|
|
65623
|
+
if (!options?.isResend) {
|
|
65624
|
+
for (const mlsGroupId of addressedMlsGroupIds) {
|
|
65625
|
+
this._rememberRetryCandidate(mlsGroupId, plaintext, options);
|
|
65626
|
+
}
|
|
65627
|
+
}
|
|
65628
|
+
}
|
|
65629
|
+
/**
|
|
65630
|
+
* Retain one outbound plaintext against the MLS group it was sent to (#732).
|
|
65631
|
+
* Evicts expired entries, then the oldest, so the map cannot grow unbounded
|
|
65632
|
+
* on an agent that sends steadily and is never refused.
|
|
65633
|
+
*/
|
|
65634
|
+
_rememberRetryCandidate(mlsGroupId, plaintext, options) {
|
|
65635
|
+
const now = Date.now();
|
|
65636
|
+
for (const [k2, v22] of this._retryCandidates) {
|
|
65637
|
+
if (now - v22.ts > RETRY_CANDIDATE_TTL_MS) this._retryCandidates.delete(k2);
|
|
65638
|
+
}
|
|
65639
|
+
this._retryCandidates.set(mlsGroupId, { plaintext, options, ts: now });
|
|
65640
|
+
while (this._retryCandidates.size > RETRY_CANDIDATE_MAX) {
|
|
65641
|
+
const oldest = this._retryCandidates.keys().next().value;
|
|
65642
|
+
if (!oldest) break;
|
|
65643
|
+
this._retryCandidates.delete(oldest);
|
|
65644
|
+
}
|
|
65570
65645
|
}
|
|
65571
65646
|
/**
|
|
65572
65647
|
* Send a typing indicator to all owner devices.
|
|
@@ -65594,7 +65669,7 @@ var init_channel = __esm2({
|
|
|
65594
65669
|
*/
|
|
65595
65670
|
sendActivitySpan(spanData) {
|
|
65596
65671
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65597
|
-
const pluginVersion = true ? "0.23.
|
|
65672
|
+
const pluginVersion = true ? "0.23.17" : "0.0.0-dev";
|
|
65598
65673
|
const agentName = this.config.agentName ?? "Agent";
|
|
65599
65674
|
const resource = {
|
|
65600
65675
|
"service.name": "agentvault-agent",
|
|
@@ -65939,13 +66014,20 @@ var init_channel = __esm2({
|
|
|
65939
66014
|
* entry, then persist.
|
|
65940
66015
|
*/
|
|
65941
66016
|
async _handleServerError(payload) {
|
|
65942
|
-
|
|
66017
|
+
const detail = payload?.detail;
|
|
66018
|
+
if (detail !== "conversation deleted" && detail !== "unknown group") return;
|
|
65943
66019
|
const mlsGroupId = payload.group_id;
|
|
65944
66020
|
if (!mlsGroupId) return;
|
|
65945
66021
|
const groups = this._persisted?.mlsGroups;
|
|
65946
66022
|
if (!groups) return;
|
|
65947
66023
|
const gid = Object.keys(groups).find((k2) => groups[k2]?.mlsGroupId === mlsGroupId);
|
|
65948
66024
|
if (!gid) return;
|
|
66025
|
+
if (detail === "unknown group") {
|
|
66026
|
+
const changed = await this._reconcileConversationGroups(gid, mlsGroupId);
|
|
66027
|
+
if (changed) await this._retryAfterReconcile(mlsGroupId);
|
|
66028
|
+
return;
|
|
66029
|
+
}
|
|
66030
|
+
this._retryCandidates.delete(mlsGroupId);
|
|
65949
66031
|
try {
|
|
65950
66032
|
await deleteMlsState(this.config.dataDir, mlsGroupId);
|
|
65951
66033
|
} catch {
|
|
@@ -65958,6 +66040,120 @@ var init_channel = __esm2({
|
|
|
65958
66040
|
);
|
|
65959
66041
|
this.emit("dm_group_pruned", { conversationGroupId: gid, mlsGroupId });
|
|
65960
66042
|
}
|
|
66043
|
+
/**
|
|
66044
|
+
* Re-resolve the conversation→group map from the server (#719).
|
|
66045
|
+
*
|
|
66046
|
+
* `_persisted.conversationGroupIds` and `_persisted.groupId` are built ONCE,
|
|
66047
|
+
* in `_activate()`, from the activation response. `_activate` runs at
|
|
66048
|
+
* enrollment and never again, so when the owner deletes a conversation and a
|
|
66049
|
+
* new one is created the agent keeps addressing the OLD group forever.
|
|
66050
|
+
* Nothing reconciled it — not restart (the stale map is reloaded from disk),
|
|
66051
|
+
* not reconnect, not the heartbeat.
|
|
66052
|
+
*
|
|
66053
|
+
* wren, 2026-07-31: `groupId` 53798dd2 had no `mls_groups` row while three
|
|
66054
|
+
* ACTIVE conversations sat on 99faf707. Inbound worked, every reply was
|
|
66055
|
+
* refused `unknown group` and dropped. Silent since 2026-07-02 — the #460
|
|
66056
|
+
* fan-out was spraying all 12 known groups, so a live one always got hit.
|
|
66057
|
+
*
|
|
66058
|
+
* Best-effort and conservative, mirroring `_refreshRoomRoster`: on any
|
|
66059
|
+
* failure the existing map is kept. The prune is NARROW — only the group the
|
|
66060
|
+
* server named, and only once the server has positively reported an active
|
|
66061
|
+
* conversation on some other group. Absence of data is never treated as
|
|
66062
|
+
* evidence of deletion.
|
|
66063
|
+
*
|
|
66064
|
+
* Returns TRUE only when the PRIMARY group pointer actually moved, which is
|
|
66065
|
+
* the signal #732's resend is gated on. Deliberately narrower than "anything
|
|
66066
|
+
* in the map changed": if some other group went stale we cannot tell which
|
|
66067
|
+
* conversation replaced it, and delivering a reply to the wrong counterparty
|
|
66068
|
+
* is strictly worse than losing it.
|
|
66069
|
+
*/
|
|
66070
|
+
async _reconcileConversationGroups(staleGid, staleMlsGroupId) {
|
|
66071
|
+
const jwt22 = this._deviceJwt ?? this._persisted?.deviceJwt;
|
|
66072
|
+
if (!jwt22 || !this._persisted) return false;
|
|
66073
|
+
let rows;
|
|
66074
|
+
try {
|
|
66075
|
+
const res = await fetch(`${this.config.apiUrl}/api/v1/conversations`, {
|
|
66076
|
+
headers: { Authorization: `Bearer ${jwt22}` }
|
|
66077
|
+
});
|
|
66078
|
+
if (!res.ok) return false;
|
|
66079
|
+
rows = await res.json();
|
|
66080
|
+
} catch (err) {
|
|
66081
|
+
console.warn("[SecureChannel] Conversation re-resolve failed:", err);
|
|
66082
|
+
return false;
|
|
66083
|
+
}
|
|
66084
|
+
if (!Array.isArray(rows)) return false;
|
|
66085
|
+
const mine = rows.filter(
|
|
66086
|
+
(r22) => r22?.agent_device_id === this._deviceId && r22?.status === "active" && typeof r22?.group_id === "string" && typeof r22?.id === "string"
|
|
66087
|
+
);
|
|
66088
|
+
if (mine.length === 0) return false;
|
|
66089
|
+
const before = this._persisted.groupId;
|
|
66090
|
+
this._sessionGroupIds = new Map(mine.map((r22) => [r22.id, r22.group_id]));
|
|
66091
|
+
this._persisted.conversationGroupIds = Object.fromEntries(this._sessionGroupIds);
|
|
66092
|
+
const primary = mine.find((r22) => r22.id === this._persisted.primaryConversationId) ?? mine[0];
|
|
66093
|
+
this._persisted.groupId = primary.group_id;
|
|
66094
|
+
this._persisted.primaryConversationId = primary.id;
|
|
66095
|
+
const liveGroupIds = new Set(mine.map((r22) => r22.group_id));
|
|
66096
|
+
if (!liveGroupIds.has(staleGid)) {
|
|
66097
|
+
try {
|
|
66098
|
+
await deleteMlsState(this.config.dataDir, staleMlsGroupId);
|
|
66099
|
+
} catch {
|
|
66100
|
+
}
|
|
66101
|
+
this._mlsGroups.delete(`1to1-group:${staleGid}`);
|
|
66102
|
+
delete this._persisted.mlsGroups?.[staleGid];
|
|
66103
|
+
this.emit("dm_group_pruned", {
|
|
66104
|
+
conversationGroupId: staleGid,
|
|
66105
|
+
mlsGroupId: staleMlsGroupId
|
|
66106
|
+
});
|
|
66107
|
+
}
|
|
66108
|
+
await this._persistState();
|
|
66109
|
+
console.log(
|
|
66110
|
+
`[SecureChannel] Re-resolved conversation groups after 'unknown group' ${staleMlsGroupId.slice(0, 8)}: primary group ${String(before).slice(0, 8)} \u2192 ${this._persisted.groupId.slice(0, 8)} (${mine.length} active conversation(s))`
|
|
66111
|
+
);
|
|
66112
|
+
return this._persisted.groupId !== before && before === staleGid;
|
|
66113
|
+
}
|
|
66114
|
+
/**
|
|
66115
|
+
* Resend the message that a now-reconciled `unknown group` refusal killed (#732).
|
|
66116
|
+
*
|
|
66117
|
+
* Called ONLY after `_reconcileConversationGroups` reported that the primary
|
|
66118
|
+
* group pointer moved off the refused group, so there is exactly one sensible
|
|
66119
|
+
* destination: the conversation the reconcile settled on.
|
|
66120
|
+
*
|
|
66121
|
+
* The candidate is consumed BEFORE the resend, not after — one attempt is the
|
|
66122
|
+
* bound, and that must hold even if the resend itself throws. A retry that
|
|
66123
|
+
* re-armed on failure would turn a persistent refusal into an infinite loop,
|
|
66124
|
+
* which is a worse failure than the dropped message it set out to fix.
|
|
66125
|
+
*
|
|
66126
|
+
* The resend is always TARGETED, never a broadcast: the original send may
|
|
66127
|
+
* have fanned out to several groups of which only one was refused, and
|
|
66128
|
+
* re-broadcasting would duplicate the message in all the others.
|
|
66129
|
+
*/
|
|
66130
|
+
async _retryAfterReconcile(staleMlsGroupId) {
|
|
66131
|
+
const candidate = this._retryCandidates.get(staleMlsGroupId);
|
|
66132
|
+
this._retryCandidates.delete(staleMlsGroupId);
|
|
66133
|
+
if (!candidate) return;
|
|
66134
|
+
if (Date.now() - candidate.ts > RETRY_CANDIDATE_TTL_MS) {
|
|
66135
|
+
console.warn(
|
|
66136
|
+
`[deliver] delivery_result=LOST group=${staleMlsGroupId.slice(0, 8)} reason=retry-window-expired`
|
|
66137
|
+
);
|
|
66138
|
+
return;
|
|
66139
|
+
}
|
|
66140
|
+
const conversationId = this._persisted?.primaryConversationId;
|
|
66141
|
+
if (!conversationId) return;
|
|
66142
|
+
try {
|
|
66143
|
+
await this.send(candidate.plaintext, {
|
|
66144
|
+
...candidate.options,
|
|
66145
|
+
conversationId,
|
|
66146
|
+
isResend: true
|
|
66147
|
+
});
|
|
66148
|
+
console.log(
|
|
66149
|
+
`[deliver] delivery_result=RESENT group=${staleMlsGroupId.slice(0, 8)} \u2192 ${String(this._persisted?.groupId).slice(0, 8)} conv=${conversationId.slice(0, 8)}`
|
|
66150
|
+
);
|
|
66151
|
+
} catch (err) {
|
|
66152
|
+
console.error(
|
|
66153
|
+
`[deliver] delivery_result=LOST group=${staleMlsGroupId.slice(0, 8)} reason=resend-failed detail=${err instanceof Error ? err.message : String(err)}`
|
|
66154
|
+
);
|
|
66155
|
+
}
|
|
66156
|
+
}
|
|
65961
66157
|
/**
|
|
65962
66158
|
* Return info for all joined rooms.
|
|
65963
66159
|
*/
|
|
@@ -66330,7 +66526,7 @@ var init_channel = __esm2({
|
|
|
66330
66526
|
});
|
|
66331
66527
|
}
|
|
66332
66528
|
const targetLabel = resolved.kind === "owner" ? "owner" : `${resolved.kind}:${resolved.id?.slice(0, 8)}...`;
|
|
66333
|
-
console.log(`[deliver] target=${targetLabel} content=${content.type} result=
|
|
66529
|
+
console.log(`[deliver] target=${targetLabel} content=${content.type} result=sent`);
|
|
66334
66530
|
if (dedupeKey) {
|
|
66335
66531
|
try {
|
|
66336
66532
|
const now = Date.now();
|
|
@@ -67224,13 +67420,12 @@ var init_channel = __esm2({
|
|
|
67224
67420
|
sessions,
|
|
67225
67421
|
messageHistory: this._persisted.messageHistory ?? []
|
|
67226
67422
|
};
|
|
67227
|
-
if (
|
|
67228
|
-
|
|
67229
|
-
|
|
67230
|
-
this._persisted.groupId = firstConv.group_id;
|
|
67423
|
+
if (primary) {
|
|
67424
|
+
if (primary.group_id) {
|
|
67425
|
+
this._persisted.groupId = primary.group_id;
|
|
67231
67426
|
}
|
|
67232
|
-
if (
|
|
67233
|
-
this._persisted.defaultTopicId =
|
|
67427
|
+
if (primary.default_topic_id) {
|
|
67428
|
+
this._persisted.defaultTopicId = primary.default_topic_id;
|
|
67234
67429
|
}
|
|
67235
67430
|
}
|
|
67236
67431
|
this._sessionGroupIds = /* @__PURE__ */ new Map();
|
|
@@ -67292,7 +67487,13 @@ var init_channel = __esm2({
|
|
|
67292
67487
|
this._ws = null;
|
|
67293
67488
|
}
|
|
67294
67489
|
this._setState("connecting");
|
|
67295
|
-
await this._maybeReissueDeviceJwt()
|
|
67490
|
+
if (await this._maybeReissueDeviceJwt()) {
|
|
67491
|
+
console.warn(
|
|
67492
|
+
"[SecureChannel] credentials are terminally dead \u2014 aborting connect (no WS attempt)"
|
|
67493
|
+
);
|
|
67494
|
+
this._setState("error");
|
|
67495
|
+
return;
|
|
67496
|
+
}
|
|
67296
67497
|
const wsUrl = this.config.apiUrl.replace(/^http/, "ws");
|
|
67297
67498
|
const url22 = `${wsUrl}/api/v1/ws?token=${encodeURIComponent(this._deviceJwt)}&device_id=${this._deviceId}`;
|
|
67298
67499
|
const ws = new WebSocket2(url22);
|
|
@@ -67333,7 +67534,7 @@ var init_channel = __esm2({
|
|
|
67333
67534
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67334
67535
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67335
67536
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67336
|
-
pluginVersion: true ? "0.23.
|
|
67537
|
+
pluginVersion: true ? "0.23.17" : "0.0.0-dev"
|
|
67337
67538
|
});
|
|
67338
67539
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67339
67540
|
}
|
|
@@ -67402,7 +67603,12 @@ var init_channel = __esm2({
|
|
|
67402
67603
|
try {
|
|
67403
67604
|
const data = JSON.parse(raw.toString());
|
|
67404
67605
|
if (data.event && data.event !== "ping" && data.event !== "typing") {
|
|
67405
|
-
|
|
67606
|
+
const d22 = data.data ?? {};
|
|
67607
|
+
const conv = (data.conversation_id || d22.conversation_id || "").toString();
|
|
67608
|
+
const parts = [`conv=${conv.slice(0, 8)}`];
|
|
67609
|
+
if (d22.group_id) parts.push(`group=${String(d22.group_id).slice(0, 8)}`);
|
|
67610
|
+
if (d22.detail) parts.push(`detail=${d22.detail}`);
|
|
67611
|
+
console.log(`[SecureChannel] WS event: ${data.event} ${parts.join(" ")}`);
|
|
67406
67612
|
}
|
|
67407
67613
|
if (data.event === "ping") {
|
|
67408
67614
|
ws.send(JSON.stringify({ event: "pong" }));
|
|
@@ -67651,7 +67857,7 @@ var init_channel = __esm2({
|
|
|
67651
67857
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67652
67858
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67653
67859
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67654
|
-
pluginVersion: true ? "0.23.
|
|
67860
|
+
pluginVersion: true ? "0.23.17" : "0.0.0-dev"
|
|
67655
67861
|
});
|
|
67656
67862
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67657
67863
|
}
|
|
@@ -67960,6 +68166,10 @@ var init_channel = __esm2({
|
|
|
67960
68166
|
}
|
|
67961
68167
|
if (data.event === "error") {
|
|
67962
68168
|
const detail = data.data?.detail || data.detail || "Unknown server error";
|
|
68169
|
+
const rejectedGroup = String(data.data?.group_id ?? "").slice(0, 8);
|
|
68170
|
+
console.error(
|
|
68171
|
+
`[deliver] delivery_result=REJECTED group=${rejectedGroup || "unknown"} detail=${detail}`
|
|
68172
|
+
);
|
|
67963
68173
|
console.error(`[SecureChannel] Server error: ${detail}`);
|
|
67964
68174
|
await this._handleServerError(data.data || data);
|
|
67965
68175
|
this.emit("error", new Error(`Server: ${detail}`));
|
|
@@ -69046,10 +69256,10 @@ ${messageText}`;
|
|
|
69046
69256
|
await mlsGroup.processCommit(commitBytes);
|
|
69047
69257
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69048
69258
|
console.log(`[SecureChannel] MLS commit processed for room ${roomId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69259
|
+
this._mlsCommitFailCounts.delete(roomId);
|
|
69049
69260
|
} catch (err) {
|
|
69050
69261
|
await this._onCommitFailure(roomId, groupId, err, `room ${roomId.slice(0, 8)}`);
|
|
69051
69262
|
}
|
|
69052
|
-
this._mlsCommitFailCounts.delete(roomId);
|
|
69053
69263
|
} else {
|
|
69054
69264
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69055
69265
|
console.log(`[SecureChannel] Buffered MLS commit for room ${roomId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -69066,10 +69276,10 @@ ${messageText}`;
|
|
|
69066
69276
|
await mlsGroup.processCommit(commitBytes);
|
|
69067
69277
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69068
69278
|
console.log(`[SecureChannel] MLS commit processed for A2A ${chId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69279
|
+
this._mlsCommitFailCounts.delete(`a2a:${chId}`);
|
|
69069
69280
|
} catch (err) {
|
|
69070
69281
|
await this._onCommitFailure(`a2a:${chId}`, groupId, err, `A2A ${chId.slice(0, 8)}`);
|
|
69071
69282
|
}
|
|
69072
|
-
this._mlsCommitFailCounts.delete(`a2a:${chId}`);
|
|
69073
69283
|
} else {
|
|
69074
69284
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69075
69285
|
console.log(`[SecureChannel] Buffered MLS commit for A2A ${chId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -69314,7 +69524,7 @@ ${messageText}`;
|
|
|
69314
69524
|
if (!this._pendingKpBundles.some((b22) => idOf(b22) === id)) {
|
|
69315
69525
|
this._pendingKpBundles.push(kp);
|
|
69316
69526
|
}
|
|
69317
|
-
while (this._pendingKpBundles.length > _SecureChannel.
|
|
69527
|
+
while (this._pendingKpBundles.length > _SecureChannel._KP_POOL_RETENTION) {
|
|
69318
69528
|
this._pendingKpBundles.shift();
|
|
69319
69529
|
}
|
|
69320
69530
|
}
|
|
@@ -70939,7 +71149,7 @@ ${messageText}`;
|
|
|
70939
71149
|
return;
|
|
70940
71150
|
}
|
|
70941
71151
|
this._authFailedThisSession = true;
|
|
70942
|
-
const authReason = data?.reason
|
|
71152
|
+
const authReason = data?.reason ?? "device_revoked";
|
|
70943
71153
|
console.warn(
|
|
70944
71154
|
`[SecureChannel] connection_rejected (reason=${data?.reason ?? "unknown"}, retryable=false) \u2014 terminal; surfacing auth_failed`
|
|
70945
71155
|
);
|
|
@@ -71103,6 +71313,14 @@ function terminalReenrollMessage(agentName, reason) {
|
|
|
71103
71313
|
const who = agentName ? `[${agentName}] ` : "";
|
|
71104
71314
|
return `${who}device is no longer valid (${reason}) \u2014 it was revoked or replaced. Re-enroll with a fresh token from AgentVault; the old credentials are dead. This agent will stop reconnecting.`;
|
|
71105
71315
|
}
|
|
71316
|
+
function formatChannelError(err) {
|
|
71317
|
+
const msg = String(err);
|
|
71318
|
+
const rejection = /Server: (.+)$/.exec(msg);
|
|
71319
|
+
if (rejection) {
|
|
71320
|
+
return `[AgentVault] DELIVERY FAILED \u2014 the server refused this frame: ${rejection[1]}. The connection survives; this message was dropped and nothing retries it.`;
|
|
71321
|
+
}
|
|
71322
|
+
return `[AgentVault] channel error (non-fatal to the connection): ${msg}`;
|
|
71323
|
+
}
|
|
71106
71324
|
function attachLifecycle(channel, opts = {}) {
|
|
71107
71325
|
const log = opts.log ?? (() => {
|
|
71108
71326
|
});
|
|
@@ -71303,7 +71521,7 @@ var init_openclaw_plugin = __esm2({
|
|
|
71303
71521
|
});
|
|
71304
71522
|
_channels.set(account.accountId, channel);
|
|
71305
71523
|
channel.on("error", (err) => {
|
|
71306
|
-
_log?.(
|
|
71524
|
+
_log?.(formatChannelError(err));
|
|
71307
71525
|
});
|
|
71308
71526
|
attachLifecycle(channel, {
|
|
71309
71527
|
agentName: account.agentName,
|
|
@@ -97500,7 +97718,7 @@ var init_index = __esm2({
|
|
|
97500
97718
|
init_skill_invoker();
|
|
97501
97719
|
await init_skill_telemetry();
|
|
97502
97720
|
await init_policy_enforcer();
|
|
97503
|
-
VERSION = true ? "0.23.
|
|
97721
|
+
VERSION = true ? "0.23.17" : "0.0.0-dev";
|
|
97504
97722
|
}
|
|
97505
97723
|
});
|
|
97506
97724
|
await init_index();
|
|
@@ -133764,7 +133982,7 @@ async function main() {
|
|
|
133764
133982
|
"[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"
|
|
133765
133983
|
);
|
|
133766
133984
|
}
|
|
133767
|
-
console.error(`[bridge] version: ${true ? "0.7.
|
|
133985
|
+
console.error(`[bridge] version: ${true ? "0.7.5" : "dev"}`);
|
|
133768
133986
|
console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
133769
133987
|
console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
133770
133988
|
if (cfg.armRoom) {
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.5",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "AgentVault Claude Bridge
|
|
5
|
+
"description": "AgentVault Claude Bridge \u2014 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": {
|