@agentvault/claude-bridge 0.7.4 → 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 +222 -24
- package/package.json +1 -1
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();
|
|
@@ -64772,6 +64776,20 @@ var init_channel = __esm2({
|
|
|
64772
64776
|
* single delivery. In-memory only (short window; no cross-restart persistence needed).
|
|
64773
64777
|
*/
|
|
64774
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();
|
|
64775
64793
|
_scanEngine = null;
|
|
64776
64794
|
_scanRuleSetVersion = 0;
|
|
64777
64795
|
_telemetryReporter = null;
|
|
@@ -64871,13 +64889,21 @@ var init_channel = __esm2({
|
|
|
64871
64889
|
* Non-fatal: a transient network error or 5xx keeps the current JWT and
|
|
64872
64890
|
* relies on the next reconnect. Terminal failures (401/403) surface via
|
|
64873
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.
|
|
64874
64900
|
*/
|
|
64875
64901
|
async _maybeReissueDeviceJwt() {
|
|
64876
64902
|
const jwt22 = this._persisted?.deviceJwt ?? this._deviceJwt;
|
|
64877
|
-
if (!jwt22) return;
|
|
64903
|
+
if (!jwt22) return false;
|
|
64878
64904
|
const exp = this._decodeJwtExp(jwt22);
|
|
64879
64905
|
const nowSec = Math.floor(Date.now() / 1e3);
|
|
64880
|
-
if (exp - nowSec >= 30 * 86400) return;
|
|
64906
|
+
if (exp - nowSec >= 30 * 86400) return false;
|
|
64881
64907
|
try {
|
|
64882
64908
|
const resp = await fetch(`${this.config.apiUrl}/api/v1/auth/reissue`, {
|
|
64883
64909
|
method: "POST",
|
|
@@ -64897,7 +64923,7 @@ var init_channel = __esm2({
|
|
|
64897
64923
|
} else {
|
|
64898
64924
|
console.warn("[SecureChannel] reissue returned 200 but no device_jwt in response");
|
|
64899
64925
|
}
|
|
64900
|
-
return;
|
|
64926
|
+
return false;
|
|
64901
64927
|
}
|
|
64902
64928
|
if (resp.status === 401 || resp.status === 403) {
|
|
64903
64929
|
console.warn(`[SecureChannel] reissue rejected (${resp.status}); credentials need attention`);
|
|
@@ -64905,12 +64931,13 @@ var init_channel = __esm2({
|
|
|
64905
64931
|
const reason = resp.status === 401 ? "device_jwt_expired" : "device_revoked";
|
|
64906
64932
|
await this._postAuthFailedToBackend(reason);
|
|
64907
64933
|
this.emit("auth_failed", { reason });
|
|
64908
|
-
return;
|
|
64934
|
+
return true;
|
|
64909
64935
|
}
|
|
64910
64936
|
console.warn(`[SecureChannel] reissue ${resp.status}; keeping current jwt`);
|
|
64911
64937
|
} catch (err) {
|
|
64912
64938
|
console.warn("[SecureChannel] reissue network error; keeping current jwt:", err);
|
|
64913
64939
|
}
|
|
64940
|
+
return false;
|
|
64914
64941
|
}
|
|
64915
64942
|
/**
|
|
64916
64943
|
* Wraps `fetch` for authenticated AV REST endpoints with reactive device-JWT
|
|
@@ -65396,7 +65423,9 @@ var init_channel = __esm2({
|
|
|
65396
65423
|
}
|
|
65397
65424
|
scanStatus = scanResult.status;
|
|
65398
65425
|
}
|
|
65399
|
-
|
|
65426
|
+
if (!options?.isResend) {
|
|
65427
|
+
this._appendHistory("agent", plaintext, topicId);
|
|
65428
|
+
}
|
|
65400
65429
|
const roomConvIds = /* @__PURE__ */ new Set();
|
|
65401
65430
|
if (this._persisted?.rooms) {
|
|
65402
65431
|
for (const room of Object.values(this._persisted.rooms)) {
|
|
@@ -65410,6 +65439,7 @@ var init_channel = __esm2({
|
|
|
65410
65439
|
let sentCount = 0;
|
|
65411
65440
|
const pendingWsSends = [];
|
|
65412
65441
|
const sentSharedGroupIds = /* @__PURE__ */ new Set();
|
|
65442
|
+
const addressedMlsGroupIds = [];
|
|
65413
65443
|
if (this._persisted?.mlsGroups && this._state === "ready" && this._ws) {
|
|
65414
65444
|
const targetSharedGid = targetConvId ? this._sessionGroupIds?.get(targetConvId) : void 0;
|
|
65415
65445
|
for (const [gid, entry] of Object.entries(this._persisted.mlsGroups)) {
|
|
@@ -65435,6 +65465,7 @@ var init_channel = __esm2({
|
|
|
65435
65465
|
if (this._persisted?.hubAddress) payload.hub_address = this._persisted.hubAddress;
|
|
65436
65466
|
if (this._persisted?.hubId) payload.sender_hub_id = this._persisted.hubId;
|
|
65437
65467
|
pendingWsSends.push(JSON.stringify({ event: "message_mls", data: payload }));
|
|
65468
|
+
addressedMlsGroupIds.push(entry.mlsGroupId);
|
|
65438
65469
|
sentSharedGroupIds.add(gid);
|
|
65439
65470
|
sentCount++;
|
|
65440
65471
|
console.log(`[SecureChannel] Shared MLS group send for group ${gid.slice(0, 8)} (${entry.mlsGroupId.slice(0, 8)})`);
|
|
@@ -65487,6 +65518,7 @@ var init_channel = __esm2({
|
|
|
65487
65518
|
data: payload
|
|
65488
65519
|
})
|
|
65489
65520
|
);
|
|
65521
|
+
addressedMlsGroupIds.push(mlsGroupId);
|
|
65490
65522
|
if (convGroupId) sentSharedGroupIds.add(convGroupId);
|
|
65491
65523
|
} else {
|
|
65492
65524
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
@@ -65572,6 +65604,7 @@ var init_channel = __esm2({
|
|
|
65572
65604
|
if (this._persisted?.hubAddress) payload.hub_address = this._persisted.hubAddress;
|
|
65573
65605
|
if (this._persisted?.hubId) payload.sender_hub_id = this._persisted.hubId;
|
|
65574
65606
|
pendingWsSends.push(JSON.stringify({ event: "message_mls", data: payload }));
|
|
65607
|
+
addressedMlsGroupIds.push(resolvedMlsGroupId);
|
|
65575
65608
|
sentCount++;
|
|
65576
65609
|
if (mlsOnlyConvGroupId) sentSharedGroupIds.add(mlsOnlyConvGroupId);
|
|
65577
65610
|
console.log(`[SecureChannel] MLS-only send for conv ${mlsConvId.slice(0, 8)} (no DR session)`);
|
|
@@ -65587,6 +65620,28 @@ var init_channel = __esm2({
|
|
|
65587
65620
|
for (const frame of pendingWsSends) {
|
|
65588
65621
|
this._ws.send(frame);
|
|
65589
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
|
+
}
|
|
65590
65645
|
}
|
|
65591
65646
|
/**
|
|
65592
65647
|
* Send a typing indicator to all owner devices.
|
|
@@ -65614,7 +65669,7 @@ var init_channel = __esm2({
|
|
|
65614
65669
|
*/
|
|
65615
65670
|
sendActivitySpan(spanData) {
|
|
65616
65671
|
if (!this._ws || this._ws.readyState !== WebSocket2.OPEN) return;
|
|
65617
|
-
const pluginVersion = true ? "0.23.
|
|
65672
|
+
const pluginVersion = true ? "0.23.17" : "0.0.0-dev";
|
|
65618
65673
|
const agentName = this.config.agentName ?? "Agent";
|
|
65619
65674
|
const resource = {
|
|
65620
65675
|
"service.name": "agentvault-agent",
|
|
@@ -65959,13 +66014,20 @@ var init_channel = __esm2({
|
|
|
65959
66014
|
* entry, then persist.
|
|
65960
66015
|
*/
|
|
65961
66016
|
async _handleServerError(payload) {
|
|
65962
|
-
|
|
66017
|
+
const detail = payload?.detail;
|
|
66018
|
+
if (detail !== "conversation deleted" && detail !== "unknown group") return;
|
|
65963
66019
|
const mlsGroupId = payload.group_id;
|
|
65964
66020
|
if (!mlsGroupId) return;
|
|
65965
66021
|
const groups = this._persisted?.mlsGroups;
|
|
65966
66022
|
if (!groups) return;
|
|
65967
66023
|
const gid = Object.keys(groups).find((k2) => groups[k2]?.mlsGroupId === mlsGroupId);
|
|
65968
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);
|
|
65969
66031
|
try {
|
|
65970
66032
|
await deleteMlsState(this.config.dataDir, mlsGroupId);
|
|
65971
66033
|
} catch {
|
|
@@ -65978,6 +66040,120 @@ var init_channel = __esm2({
|
|
|
65978
66040
|
);
|
|
65979
66041
|
this.emit("dm_group_pruned", { conversationGroupId: gid, mlsGroupId });
|
|
65980
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
|
+
}
|
|
65981
66157
|
/**
|
|
65982
66158
|
* Return info for all joined rooms.
|
|
65983
66159
|
*/
|
|
@@ -66350,7 +66526,7 @@ var init_channel = __esm2({
|
|
|
66350
66526
|
});
|
|
66351
66527
|
}
|
|
66352
66528
|
const targetLabel = resolved.kind === "owner" ? "owner" : `${resolved.kind}:${resolved.id?.slice(0, 8)}...`;
|
|
66353
|
-
console.log(`[deliver] target=${targetLabel} content=${content.type} result=
|
|
66529
|
+
console.log(`[deliver] target=${targetLabel} content=${content.type} result=sent`);
|
|
66354
66530
|
if (dedupeKey) {
|
|
66355
66531
|
try {
|
|
66356
66532
|
const now = Date.now();
|
|
@@ -67244,13 +67420,12 @@ var init_channel = __esm2({
|
|
|
67244
67420
|
sessions,
|
|
67245
67421
|
messageHistory: this._persisted.messageHistory ?? []
|
|
67246
67422
|
};
|
|
67247
|
-
if (
|
|
67248
|
-
|
|
67249
|
-
|
|
67250
|
-
this._persisted.groupId = firstConv.group_id;
|
|
67423
|
+
if (primary) {
|
|
67424
|
+
if (primary.group_id) {
|
|
67425
|
+
this._persisted.groupId = primary.group_id;
|
|
67251
67426
|
}
|
|
67252
|
-
if (
|
|
67253
|
-
this._persisted.defaultTopicId =
|
|
67427
|
+
if (primary.default_topic_id) {
|
|
67428
|
+
this._persisted.defaultTopicId = primary.default_topic_id;
|
|
67254
67429
|
}
|
|
67255
67430
|
}
|
|
67256
67431
|
this._sessionGroupIds = /* @__PURE__ */ new Map();
|
|
@@ -67312,7 +67487,13 @@ var init_channel = __esm2({
|
|
|
67312
67487
|
this._ws = null;
|
|
67313
67488
|
}
|
|
67314
67489
|
this._setState("connecting");
|
|
67315
|
-
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
|
+
}
|
|
67316
67497
|
const wsUrl = this.config.apiUrl.replace(/^http/, "ws");
|
|
67317
67498
|
const url22 = `${wsUrl}/api/v1/ws?token=${encodeURIComponent(this._deviceJwt)}&device_id=${this._deviceId}`;
|
|
67318
67499
|
const ws = new WebSocket2(url22);
|
|
@@ -67353,7 +67534,7 @@ var init_channel = __esm2({
|
|
|
67353
67534
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67354
67535
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67355
67536
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67356
|
-
pluginVersion: true ? "0.23.
|
|
67537
|
+
pluginVersion: true ? "0.23.17" : "0.0.0-dev"
|
|
67357
67538
|
});
|
|
67358
67539
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67359
67540
|
}
|
|
@@ -67422,7 +67603,12 @@ var init_channel = __esm2({
|
|
|
67422
67603
|
try {
|
|
67423
67604
|
const data = JSON.parse(raw.toString());
|
|
67424
67605
|
if (data.event && data.event !== "ping" && data.event !== "typing") {
|
|
67425
|
-
|
|
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(" ")}`);
|
|
67426
67612
|
}
|
|
67427
67613
|
if (data.event === "ping") {
|
|
67428
67614
|
ws.send(JSON.stringify({ event: "pong" }));
|
|
@@ -67671,7 +67857,7 @@ var init_channel = __esm2({
|
|
|
67671
67857
|
agentVersion: this.config.agentVersion ?? "0.0.0",
|
|
67672
67858
|
// __AV_VERSION__ is injected by esbuild from package.json at build time.
|
|
67673
67859
|
// Falls back to "0.0.0-dev" in non-bundled contexts (tests).
|
|
67674
|
-
pluginVersion: true ? "0.23.
|
|
67860
|
+
pluginVersion: true ? "0.23.17" : "0.0.0-dev"
|
|
67675
67861
|
});
|
|
67676
67862
|
this._telemetryReporter.startAutoFlush(3e4);
|
|
67677
67863
|
}
|
|
@@ -67980,6 +68166,10 @@ var init_channel = __esm2({
|
|
|
67980
68166
|
}
|
|
67981
68167
|
if (data.event === "error") {
|
|
67982
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
|
+
);
|
|
67983
68173
|
console.error(`[SecureChannel] Server error: ${detail}`);
|
|
67984
68174
|
await this._handleServerError(data.data || data);
|
|
67985
68175
|
this.emit("error", new Error(`Server: ${detail}`));
|
|
@@ -69066,10 +69256,10 @@ ${messageText}`;
|
|
|
69066
69256
|
await mlsGroup.processCommit(commitBytes);
|
|
69067
69257
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69068
69258
|
console.log(`[SecureChannel] MLS commit processed for room ${roomId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69259
|
+
this._mlsCommitFailCounts.delete(roomId);
|
|
69069
69260
|
} catch (err) {
|
|
69070
69261
|
await this._onCommitFailure(roomId, groupId, err, `room ${roomId.slice(0, 8)}`);
|
|
69071
69262
|
}
|
|
69072
|
-
this._mlsCommitFailCounts.delete(roomId);
|
|
69073
69263
|
} else {
|
|
69074
69264
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69075
69265
|
console.log(`[SecureChannel] Buffered MLS commit for room ${roomId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -69086,10 +69276,10 @@ ${messageText}`;
|
|
|
69086
69276
|
await mlsGroup.processCommit(commitBytes);
|
|
69087
69277
|
await saveMlsState(this.config.dataDir, groupId, JSON.stringify(mlsGroup.exportState()));
|
|
69088
69278
|
console.log(`[SecureChannel] MLS commit processed for A2A ${chId.slice(0, 8)} (epoch=${mlsGroup.epoch})`);
|
|
69279
|
+
this._mlsCommitFailCounts.delete(`a2a:${chId}`);
|
|
69089
69280
|
} catch (err) {
|
|
69090
69281
|
await this._onCommitFailure(`a2a:${chId}`, groupId, err, `A2A ${chId.slice(0, 8)}`);
|
|
69091
69282
|
}
|
|
69092
|
-
this._mlsCommitFailCounts.delete(`a2a:${chId}`);
|
|
69093
69283
|
} else {
|
|
69094
69284
|
this._bufferMlsCommit(groupId, epoch, data);
|
|
69095
69285
|
console.log(`[SecureChannel] Buffered MLS commit for A2A ${chId.slice(0, 8)} (epoch=${epoch}, group not initialized)`);
|
|
@@ -70959,7 +71149,7 @@ ${messageText}`;
|
|
|
70959
71149
|
return;
|
|
70960
71150
|
}
|
|
70961
71151
|
this._authFailedThisSession = true;
|
|
70962
|
-
const authReason = data?.reason
|
|
71152
|
+
const authReason = data?.reason ?? "device_revoked";
|
|
70963
71153
|
console.warn(
|
|
70964
71154
|
`[SecureChannel] connection_rejected (reason=${data?.reason ?? "unknown"}, retryable=false) \u2014 terminal; surfacing auth_failed`
|
|
70965
71155
|
);
|
|
@@ -71123,6 +71313,14 @@ function terminalReenrollMessage(agentName, reason) {
|
|
|
71123
71313
|
const who = agentName ? `[${agentName}] ` : "";
|
|
71124
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.`;
|
|
71125
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
|
+
}
|
|
71126
71324
|
function attachLifecycle(channel, opts = {}) {
|
|
71127
71325
|
const log = opts.log ?? (() => {
|
|
71128
71326
|
});
|
|
@@ -71323,7 +71521,7 @@ var init_openclaw_plugin = __esm2({
|
|
|
71323
71521
|
});
|
|
71324
71522
|
_channels.set(account.accountId, channel);
|
|
71325
71523
|
channel.on("error", (err) => {
|
|
71326
|
-
_log?.(
|
|
71524
|
+
_log?.(formatChannelError(err));
|
|
71327
71525
|
});
|
|
71328
71526
|
attachLifecycle(channel, {
|
|
71329
71527
|
agentName: account.agentName,
|
|
@@ -97520,7 +97718,7 @@ var init_index = __esm2({
|
|
|
97520
97718
|
init_skill_invoker();
|
|
97521
97719
|
await init_skill_telemetry();
|
|
97522
97720
|
await init_policy_enforcer();
|
|
97523
|
-
VERSION = true ? "0.23.
|
|
97721
|
+
VERSION = true ? "0.23.17" : "0.0.0-dev";
|
|
97524
97722
|
}
|
|
97525
97723
|
});
|
|
97526
97724
|
await init_index();
|
|
@@ -133784,7 +133982,7 @@ async function main() {
|
|
|
133784
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"
|
|
133785
133983
|
);
|
|
133786
133984
|
}
|
|
133787
|
-
console.error(`[bridge] version: ${true ? "0.7.
|
|
133985
|
+
console.error(`[bridge] version: ${true ? "0.7.5" : "dev"}`);
|
|
133788
133986
|
console.error(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
133789
133987
|
console.error(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
133790
133988
|
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.5",
|
|
4
4
|
"type": "module",
|
|
5
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",
|