@agentvault/claude-bridge 0.7.9 → 0.7.11
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 +123 -3
- package/dist/session.d.ts +2 -0
- package/dist/worker-queue.d.ts +6 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -63791,6 +63791,35 @@ async function deleteMlsState(dataDir, groupId) {
|
|
|
63791
63791
|
} catch {
|
|
63792
63792
|
}
|
|
63793
63793
|
}
|
|
63794
|
+
async function quarantineOrphanedMlsState(dataDir, isStillMember, opts = {}) {
|
|
63795
|
+
const max = opts.max ?? 20;
|
|
63796
|
+
const quarantined = [];
|
|
63797
|
+
let names;
|
|
63798
|
+
try {
|
|
63799
|
+
names = await readdir(dataDir);
|
|
63800
|
+
} catch {
|
|
63801
|
+
return quarantined;
|
|
63802
|
+
}
|
|
63803
|
+
for (const name of names) {
|
|
63804
|
+
if (quarantined.length >= max) break;
|
|
63805
|
+
const m22 = /^mls-(?!kp-pending-)([A-Za-z0-9_-]+)\.json$/.exec(name);
|
|
63806
|
+
if (!m22) continue;
|
|
63807
|
+
const groupId = m22[1];
|
|
63808
|
+
let member;
|
|
63809
|
+
try {
|
|
63810
|
+
member = await isStillMember(groupId);
|
|
63811
|
+
} catch {
|
|
63812
|
+
continue;
|
|
63813
|
+
}
|
|
63814
|
+
if (member === null || member === true) continue;
|
|
63815
|
+
try {
|
|
63816
|
+
await rename(join(dataDir, name), join(dataDir, `${name}.orphaned`));
|
|
63817
|
+
quarantined.push(groupId);
|
|
63818
|
+
} catch {
|
|
63819
|
+
}
|
|
63820
|
+
}
|
|
63821
|
+
return quarantined;
|
|
63822
|
+
}
|
|
63794
63823
|
var SYNC_LOCK_STALE_MS;
|
|
63795
63824
|
var pendingKpPath;
|
|
63796
63825
|
var init_mls_state = __esm2({
|
|
@@ -64124,6 +64153,41 @@ var init_transport2 = __esm2({
|
|
|
64124
64153
|
"use strict";
|
|
64125
64154
|
}
|
|
64126
64155
|
});
|
|
64156
|
+
function a2aChannelSignature(channels) {
|
|
64157
|
+
return JSON.stringify(
|
|
64158
|
+
channels.map((c22) => ({
|
|
64159
|
+
id: c22.channelId?.slice(0, 8),
|
|
64160
|
+
status: c22.status,
|
|
64161
|
+
participants: c22.participant_count
|
|
64162
|
+
})).sort((a2, b22) => (a2.id ?? "").localeCompare(b22.id ?? ""))
|
|
64163
|
+
);
|
|
64164
|
+
}
|
|
64165
|
+
var A2ALogGate;
|
|
64166
|
+
var init_a2a_log_gate = __esm2({
|
|
64167
|
+
"src/a2a-log-gate.ts"() {
|
|
64168
|
+
"use strict";
|
|
64169
|
+
A2ALogGate = class {
|
|
64170
|
+
last = null;
|
|
64171
|
+
/** True when this state differs from the last reported one. Records it. */
|
|
64172
|
+
shouldReport(channels) {
|
|
64173
|
+
const sig = a2aChannelSignature(channels);
|
|
64174
|
+
if (sig === this.last) return false;
|
|
64175
|
+
this.last = sig;
|
|
64176
|
+
return true;
|
|
64177
|
+
}
|
|
64178
|
+
};
|
|
64179
|
+
}
|
|
64180
|
+
});
|
|
64181
|
+
function mlsMembershipFromStatus(status) {
|
|
64182
|
+
if (status === 200) return true;
|
|
64183
|
+
if (status === 403 || status === 404) return false;
|
|
64184
|
+
return null;
|
|
64185
|
+
}
|
|
64186
|
+
var init_mls_membership_probe = __esm2({
|
|
64187
|
+
"src/mls-membership-probe.ts"() {
|
|
64188
|
+
"use strict";
|
|
64189
|
+
}
|
|
64190
|
+
});
|
|
64127
64191
|
var openclaw_compat_exports = {};
|
|
64128
64192
|
__export2(openclaw_compat_exports, {
|
|
64129
64193
|
AGENT_EVENT_CANDIDATES: () => AGENT_EVENT_CANDIDATES,
|
|
@@ -64683,6 +64747,8 @@ var init_channel = __esm2({
|
|
|
64683
64747
|
await init_crypto_helpers();
|
|
64684
64748
|
await init_state();
|
|
64685
64749
|
init_transport2();
|
|
64750
|
+
init_a2a_log_gate();
|
|
64751
|
+
init_mls_membership_probe();
|
|
64686
64752
|
ROOM_AGENT_TYPES = /* @__PURE__ */ new Set([
|
|
64687
64753
|
"message",
|
|
64688
64754
|
"text",
|
|
@@ -64748,6 +64814,11 @@ var init_channel = __esm2({
|
|
|
64748
64814
|
_heartbeatTimer = null;
|
|
64749
64815
|
_heartbeatCallback = null;
|
|
64750
64816
|
_heartbeatIntervalSeconds = 0;
|
|
64817
|
+
/** #798: report the A2A channel set only when it CHANGES. This line polls
|
|
64818
|
+
* every 30s and produced 90.2% of bridge.log (90,841/100,702 lines) and a
|
|
64819
|
+
* 27.9MB gateway.log on wren, every one reading `channels=0`. Not deleted —
|
|
64820
|
+
* #794's A2A watch needs the transitions. */
|
|
64821
|
+
_a2aLogGate = new A2ALogGate();
|
|
64751
64822
|
_wakeDetectorTimer = null;
|
|
64752
64823
|
_lastWakeTick = Date.now();
|
|
64753
64824
|
_trustToken = null;
|
|
@@ -66041,6 +66112,47 @@ var init_channel = __esm2({
|
|
|
66041
66112
|
);
|
|
66042
66113
|
this.emit("rooms_reconciled", { pruned: stale });
|
|
66043
66114
|
}
|
|
66115
|
+
/**
|
|
66116
|
+
* #629 — quarantine MLS state for groups this device is no longer in.
|
|
66117
|
+
*
|
|
66118
|
+
* `_reconcileRoomsWithServer` above covers ROOMS: anything absent from
|
|
66119
|
+
* `GET /rooms` is pruned. It cannot see a group that was never a room — a 1:1
|
|
66120
|
+
* conversation group or an A2A channel group — so that state lingered with
|
|
66121
|
+
* nothing to reconcile it. Measured on loopita 2026-08-08: one group-state
|
|
66122
|
+
* file whose group had no server-side row at all.
|
|
66123
|
+
*
|
|
66124
|
+
* `quarantineOrphanedMlsState` was written for exactly this and shipped with
|
|
66125
|
+
* 11 passing tests, but nothing ever called it. This is that call site.
|
|
66126
|
+
*
|
|
66127
|
+
* Runs AFTER the room reconcile so rooms are already gone and this only sees
|
|
66128
|
+
* genuine non-room orphans.
|
|
66129
|
+
*
|
|
66130
|
+
* Quarantine RENAMES to `.orphaned` rather than deleting: if the verdict is
|
|
66131
|
+
* ever wrong the state is recoverable by hand, which deletion would not be.
|
|
66132
|
+
*/
|
|
66133
|
+
async _quarantineOrphanedMlsGroups() {
|
|
66134
|
+
if (!this._deviceJwt) return;
|
|
66135
|
+
const quarantined = await quarantineOrphanedMlsState(
|
|
66136
|
+
this.config.dataDir,
|
|
66137
|
+
async (groupId) => {
|
|
66138
|
+
try {
|
|
66139
|
+
const res = await fetch(
|
|
66140
|
+
`${this.config.apiUrl}/api/v1/mls/groups/${encodeURIComponent(groupId)}`,
|
|
66141
|
+
{ headers: { Authorization: `Bearer ${this._deviceJwt}` } }
|
|
66142
|
+
);
|
|
66143
|
+
return mlsMembershipFromStatus(res.status);
|
|
66144
|
+
} catch {
|
|
66145
|
+
return null;
|
|
66146
|
+
}
|
|
66147
|
+
}
|
|
66148
|
+
);
|
|
66149
|
+
if (quarantined.length > 0) {
|
|
66150
|
+
console.log(
|
|
66151
|
+
`[SecureChannel] Quarantined ${quarantined.length} orphaned MLS group state file(s): ` + quarantined.map((g22) => g22.slice(0, 8)).join(", ")
|
|
66152
|
+
);
|
|
66153
|
+
this.emit("mls_state_quarantined", { groupIds: quarantined });
|
|
66154
|
+
}
|
|
66155
|
+
}
|
|
66044
66156
|
/**
|
|
66045
66157
|
* Handle an in-band `{event:"error"}` frame from the server.
|
|
66046
66158
|
*
|
|
@@ -67201,7 +67313,9 @@ var init_channel = __esm2({
|
|
|
67201
67313
|
}
|
|
67202
67314
|
const myHub = this._persisted.hubAddress || "";
|
|
67203
67315
|
const myHubId = this._persisted.hubId || "";
|
|
67204
|
-
|
|
67316
|
+
if (this._a2aLogGate.shouldReport(channels)) {
|
|
67317
|
+
console.log(`[listA2AChannels] myHub=${myHub}, channels=${channels.length}, raw=${JSON.stringify(channels.map((c22) => ({ id: c22.channelId?.slice(0, 8), status: c22.status, participants: c22.participant_count })))}`);
|
|
67318
|
+
}
|
|
67205
67319
|
for (const ch2 of channels) {
|
|
67206
67320
|
if (ch2.status === "active" || ch2.status === "approved") {
|
|
67207
67321
|
const myParticipant = ch2.participants?.find(
|
|
@@ -67563,6 +67677,8 @@ var init_channel = __esm2({
|
|
|
67563
67677
|
this._setState("ready");
|
|
67564
67678
|
void this._reconcileRoomsWithServer().catch(
|
|
67565
67679
|
(err) => console.warn(`[SecureChannel] room reconcile failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
|
|
67680
|
+
).then(() => this._quarantineOrphanedMlsGroups()).catch(
|
|
67681
|
+
(err) => console.warn(`[SecureChannel] MLS orphan quarantine failed (ignored): ${err instanceof Error ? err.message : String(err)}`)
|
|
67566
67682
|
);
|
|
67567
67683
|
if (this.config.enableScanning) {
|
|
67568
67684
|
this._scanEngine = new ScanEngine();
|
|
@@ -133430,13 +133546,17 @@ var PersistentClaudeSession = class {
|
|
|
133430
133546
|
const r7 = m6;
|
|
133431
133547
|
const num = (k2) => typeof r7[k2] === "number" ? r7[k2] : void 0;
|
|
133432
133548
|
const str = (k2) => typeof r7[k2] === "string" ? r7[k2] : void 0;
|
|
133549
|
+
const usage = r7.usage ?? {};
|
|
133550
|
+
const unum = (k2) => typeof usage[k2] === "number" ? usage[k2] : void 0;
|
|
133433
133551
|
this.resultTelemetry = {
|
|
133434
133552
|
durationMs: num("duration_ms"),
|
|
133435
133553
|
durationApiMs: num("duration_api_ms"),
|
|
133436
133554
|
numTurns: num("num_turns"),
|
|
133437
133555
|
subtype: str("subtype"),
|
|
133438
133556
|
stopReason: str("stop_reason"),
|
|
133439
|
-
apiErrorStatus: num("api_error_status")
|
|
133557
|
+
apiErrorStatus: num("api_error_status"),
|
|
133558
|
+
cacheReadTokens: unum("cache_read_input_tokens"),
|
|
133559
|
+
cacheCreationTokens: unum("cache_creation_input_tokens")
|
|
133440
133560
|
};
|
|
133441
133561
|
const reply = this.activeReply;
|
|
133442
133562
|
if (this.currentReplyExpected && !this.saidThisTurn && this.turnText.trim()) {
|
|
@@ -134148,7 +134268,7 @@ async function main() {
|
|
|
134148
134268
|
"[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"
|
|
134149
134269
|
);
|
|
134150
134270
|
}
|
|
134151
|
-
logLine(`[bridge] version: ${true ? "0.7.
|
|
134271
|
+
logLine(`[bridge] version: ${true ? "0.7.11" : "dev"}`);
|
|
134152
134272
|
logLine(`[bridge] data dir: ${cfg.dataDir} (${cfg.dataDirSource})`);
|
|
134153
134273
|
logLine(`[bridge] workspace: ${cfg.workspaceDir} \xB7 permissions: ${cfg.permissionMode} \xB7 enclave dir fenced`);
|
|
134154
134274
|
if (cfg.armRoom) {
|
package/dist/session.d.ts
CHANGED
package/dist/worker-queue.d.ts
CHANGED
|
@@ -82,6 +82,12 @@ export type WorkerIncident = {
|
|
|
82
82
|
subtype?: string;
|
|
83
83
|
stopReason?: string;
|
|
84
84
|
apiErrorStatus?: number;
|
|
85
|
+
/** Prompt-cache state — `cacheReadTokens === 0` with a large
|
|
86
|
+
* `cacheCreationTokens` marks a COLD turn, which costs ~2x a warm one for
|
|
87
|
+
* reasons unrelated to the code under test. Without it, a latency figure
|
|
88
|
+
* in this file cannot be read honestly. */
|
|
89
|
+
cacheReadTokens?: number;
|
|
90
|
+
cacheCreationTokens?: number;
|
|
85
91
|
};
|
|
86
92
|
};
|
|
87
93
|
export interface WorkerQueueDeps {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentvault/claude-bridge",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.11",
|
|
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",
|