@agentvault/agentvault 0.19.33 → 0.19.35
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/cli.js +115 -34
- package/dist/cli.js.map +2 -2
- package/dist/index.js +115 -34
- package/dist/index.js.map +2 -2
- package/dist/openclaw-entry.js.map +2 -2
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -44744,7 +44744,7 @@ var init_ratchet = __esm({
|
|
|
44744
44744
|
async "../crypto/dist/ratchet.js"() {
|
|
44745
44745
|
"use strict";
|
|
44746
44746
|
await init_libsodium_wrappers();
|
|
44747
|
-
MAX_SKIP =
|
|
44747
|
+
MAX_SKIP = 1e3;
|
|
44748
44748
|
CHAIN_KEY_SEED = new Uint8Array([1]);
|
|
44749
44749
|
MSG_KEY_SEED = new Uint8Array([2]);
|
|
44750
44750
|
HEADER_KEY_SEED = new Uint8Array([3]);
|
|
@@ -47175,6 +47175,12 @@ var init_channel = __esm({
|
|
|
47175
47175
|
this._primaryConversationId = this._persisted.primaryConversationId;
|
|
47176
47176
|
this._fingerprint = this._persisted.fingerprint;
|
|
47177
47177
|
this._lastInboundRoomId = this._persisted.lastInboundRoomId;
|
|
47178
|
+
if (this._persisted.seenMessageIds) {
|
|
47179
|
+
this._seenMessageIds = new Set(this._persisted.seenMessageIds.slice(-_SecureChannel.SEEN_MSG_MAX));
|
|
47180
|
+
}
|
|
47181
|
+
if (this._persisted.seenA2AMessageIds) {
|
|
47182
|
+
this._a2aSeenMessageIds = new Set(this._persisted.seenA2AMessageIds.slice(-_SecureChannel.A2A_SEEN_MAX));
|
|
47183
|
+
}
|
|
47178
47184
|
for (const [convId, sessionData] of Object.entries(
|
|
47179
47185
|
this._persisted.sessions
|
|
47180
47186
|
)) {
|
|
@@ -47321,6 +47327,7 @@ var init_channel = __esm({
|
|
|
47321
47327
|
}
|
|
47322
47328
|
const messageGroupId = randomUUID2();
|
|
47323
47329
|
let sentCount = 0;
|
|
47330
|
+
const pendingWsSends = [];
|
|
47324
47331
|
for (const [convId, session] of this._sessions) {
|
|
47325
47332
|
if (!session.activated) continue;
|
|
47326
47333
|
if (roomConvIds.has(convId)) continue;
|
|
@@ -47353,7 +47360,7 @@ var init_channel = __esm({
|
|
|
47353
47360
|
if (this._persisted?.hubId) {
|
|
47354
47361
|
payload.sender_hub_id = this._persisted.hubId;
|
|
47355
47362
|
}
|
|
47356
|
-
|
|
47363
|
+
pendingWsSends.push(
|
|
47357
47364
|
JSON.stringify({
|
|
47358
47365
|
event: "message",
|
|
47359
47366
|
data: payload
|
|
@@ -47379,6 +47386,9 @@ var init_channel = __esm({
|
|
|
47379
47386
|
console.warn("[SecureChannel] send() delivered to 0 sessions (all skipped or failed)");
|
|
47380
47387
|
}
|
|
47381
47388
|
await this._persistState();
|
|
47389
|
+
for (const frame of pendingWsSends) {
|
|
47390
|
+
this._ws.send(frame);
|
|
47391
|
+
}
|
|
47382
47392
|
}
|
|
47383
47393
|
/**
|
|
47384
47394
|
* Send a typing indicator to all owner devices.
|
|
@@ -47657,6 +47667,7 @@ var init_channel = __esm({
|
|
|
47657
47667
|
if (recipients.length === 0) {
|
|
47658
47668
|
throw new Error("No active sessions in room");
|
|
47659
47669
|
}
|
|
47670
|
+
await this._persistState();
|
|
47660
47671
|
if (this._state === "ready" && this._ws) {
|
|
47661
47672
|
this._ws.send(
|
|
47662
47673
|
JSON.stringify({
|
|
@@ -47696,7 +47707,6 @@ var init_channel = __esm({
|
|
|
47696
47707
|
throw new Error(`Failed to send room message: ${err}`);
|
|
47697
47708
|
}
|
|
47698
47709
|
}
|
|
47699
|
-
await this._persistState();
|
|
47700
47710
|
}
|
|
47701
47711
|
/**
|
|
47702
47712
|
* Leave a room: remove sessions and persisted room state.
|
|
@@ -47795,11 +47805,12 @@ var init_channel = __esm({
|
|
|
47795
47805
|
attachment: attachMeta
|
|
47796
47806
|
});
|
|
47797
47807
|
const messageGroupId = randomUUID2();
|
|
47808
|
+
const pendingWsSends = [];
|
|
47798
47809
|
for (const [convId, session] of this._sessions) {
|
|
47799
47810
|
if (!session.activated) continue;
|
|
47800
47811
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
47801
47812
|
const transport = encryptedMessageToTransport(encrypted);
|
|
47802
|
-
|
|
47813
|
+
pendingWsSends.push(
|
|
47803
47814
|
JSON.stringify({
|
|
47804
47815
|
event: "message",
|
|
47805
47816
|
data: {
|
|
@@ -47813,6 +47824,9 @@ var init_channel = __esm({
|
|
|
47813
47824
|
);
|
|
47814
47825
|
}
|
|
47815
47826
|
await this._persistState();
|
|
47827
|
+
for (const frame of pendingWsSends) {
|
|
47828
|
+
this._ws.send(frame);
|
|
47829
|
+
}
|
|
47816
47830
|
}
|
|
47817
47831
|
async sendActionConfirmation(confirmation) {
|
|
47818
47832
|
const envelope = {
|
|
@@ -48824,6 +48838,9 @@ var init_channel = __esm({
|
|
|
48824
48838
|
this._lastWakeTick = Date.now();
|
|
48825
48839
|
try {
|
|
48826
48840
|
const data = JSON.parse(raw.toString());
|
|
48841
|
+
if (data.event && data.event !== "ping" && data.event !== "typing") {
|
|
48842
|
+
console.log(`[SecureChannel] WS event: ${data.event} conv=${(data.conversation_id || data.data?.conversation_id || "").toString().slice(0, 8)}`);
|
|
48843
|
+
}
|
|
48827
48844
|
if (data.event === "ping") {
|
|
48828
48845
|
ws.send(JSON.stringify({ event: "pong" }));
|
|
48829
48846
|
return;
|
|
@@ -49626,11 +49643,12 @@ ${messageText}`;
|
|
|
49626
49643
|
});
|
|
49627
49644
|
this._appendHistory("agent", plaintext, topicId);
|
|
49628
49645
|
const messageGroupId = randomUUID2();
|
|
49646
|
+
const pendingWsSends = [];
|
|
49629
49647
|
for (const [convId, session] of this._sessions) {
|
|
49630
49648
|
if (!session.activated) continue;
|
|
49631
49649
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
49632
49650
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49633
|
-
|
|
49651
|
+
pendingWsSends.push(
|
|
49634
49652
|
JSON.stringify({
|
|
49635
49653
|
event: "message",
|
|
49636
49654
|
data: {
|
|
@@ -49644,6 +49662,9 @@ ${messageText}`;
|
|
|
49644
49662
|
);
|
|
49645
49663
|
}
|
|
49646
49664
|
await this._persistState();
|
|
49665
|
+
for (const frame of pendingWsSends) {
|
|
49666
|
+
this._ws.send(frame);
|
|
49667
|
+
}
|
|
49647
49668
|
}
|
|
49648
49669
|
/**
|
|
49649
49670
|
* Relay an owner's message to all sibling sessions as encrypted sync messages.
|
|
@@ -49666,13 +49687,14 @@ ${messageText}`;
|
|
|
49666
49687
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49667
49688
|
topicId
|
|
49668
49689
|
});
|
|
49690
|
+
const pendingWsSends = [];
|
|
49669
49691
|
for (const [siblingConvId, siblingSession] of this._sessions) {
|
|
49670
49692
|
if (siblingConvId === sourceConvId) continue;
|
|
49671
49693
|
if (!siblingSession.activated) continue;
|
|
49672
49694
|
if (roomConvIds.has(siblingConvId)) continue;
|
|
49673
49695
|
const syncEncrypted = siblingSession.ratchet.encrypt(syncPayload);
|
|
49674
49696
|
const syncTransport = encryptedMessageToTransport(syncEncrypted);
|
|
49675
|
-
|
|
49697
|
+
pendingWsSends.push(
|
|
49676
49698
|
JSON.stringify({
|
|
49677
49699
|
event: "message",
|
|
49678
49700
|
data: {
|
|
@@ -49683,6 +49705,10 @@ ${messageText}`;
|
|
|
49683
49705
|
})
|
|
49684
49706
|
);
|
|
49685
49707
|
}
|
|
49708
|
+
await this._persistState();
|
|
49709
|
+
for (const frame of pendingWsSends) {
|
|
49710
|
+
this._ws.send(frame);
|
|
49711
|
+
}
|
|
49686
49712
|
}
|
|
49687
49713
|
/**
|
|
49688
49714
|
* Resolve the agent's workspace directory.
|
|
@@ -49726,6 +49752,7 @@ ${messageText}`;
|
|
|
49726
49752
|
const plaintext = JSON.stringify(payload);
|
|
49727
49753
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
49728
49754
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49755
|
+
await this._persistState();
|
|
49729
49756
|
this._ws.send(
|
|
49730
49757
|
JSON.stringify({
|
|
49731
49758
|
event: "message",
|
|
@@ -49736,7 +49763,6 @@ ${messageText}`;
|
|
|
49736
49763
|
}
|
|
49737
49764
|
})
|
|
49738
49765
|
);
|
|
49739
|
-
await this._persistState();
|
|
49740
49766
|
}
|
|
49741
49767
|
/**
|
|
49742
49768
|
* Send stored message history to a newly-activated session.
|
|
@@ -49759,6 +49785,7 @@ ${messageText}`;
|
|
|
49759
49785
|
});
|
|
49760
49786
|
const encrypted = session.ratchet.encrypt(replayPayload);
|
|
49761
49787
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49788
|
+
await this._persistState();
|
|
49762
49789
|
this._ws.send(
|
|
49763
49790
|
JSON.stringify({
|
|
49764
49791
|
event: "message",
|
|
@@ -50011,15 +50038,31 @@ ${messageText}`;
|
|
|
50011
50038
|
console.log(
|
|
50012
50039
|
`[SecureChannel] Room ratchet re-initialized for conv ${convId.slice(0, 8)}...`
|
|
50013
50040
|
);
|
|
50014
|
-
|
|
50015
|
-
|
|
50016
|
-
|
|
50017
|
-
|
|
50041
|
+
const incomingMsgNum = encrypted.header.messageNumber;
|
|
50042
|
+
if (incomingMsgNum <= 5) {
|
|
50043
|
+
try {
|
|
50044
|
+
plaintext = session.ratchet.decrypt(encrypted);
|
|
50045
|
+
session.activated = true;
|
|
50046
|
+
if (this._persisted.sessions[convId]) {
|
|
50047
|
+
this._persisted.sessions[convId].activated = true;
|
|
50048
|
+
}
|
|
50049
|
+
await this._persistState();
|
|
50050
|
+
console.log(
|
|
50051
|
+
`[SecureChannel] Room session ${convId.slice(0, 8)}... re-activated after ratchet re-init`
|
|
50052
|
+
);
|
|
50053
|
+
} catch (retryErr) {
|
|
50054
|
+
console.warn(
|
|
50055
|
+
`[SecureChannel] Room re-init retry failed for conv ${convId.slice(0, 8)} (msgNum=${incomingMsgNum}):`,
|
|
50056
|
+
retryErr
|
|
50057
|
+
);
|
|
50058
|
+
return;
|
|
50059
|
+
}
|
|
50060
|
+
} else {
|
|
50061
|
+
console.log(
|
|
50062
|
+
`[SecureChannel] Room re-init: skipping message with msgNum=${incomingMsgNum} for conv ${convId.slice(0, 8)} (too far ahead for fresh ratchet)`
|
|
50063
|
+
);
|
|
50064
|
+
return;
|
|
50018
50065
|
}
|
|
50019
|
-
await this._persistState();
|
|
50020
|
-
console.log(
|
|
50021
|
-
`[SecureChannel] Room session ${convId.slice(0, 8)}... re-activated after ratchet re-init`
|
|
50022
|
-
);
|
|
50023
50066
|
} catch (reinitErr) {
|
|
50024
50067
|
console.error(
|
|
50025
50068
|
`[SecureChannel] Room ratchet re-init failed for conv ${convId.slice(0, 8)}...:`,
|
|
@@ -50198,13 +50241,14 @@ ${messageText}`;
|
|
|
50198
50241
|
const dist = chain.getDistribution(this._deviceId);
|
|
50199
50242
|
const distJson = JSON.stringify(dist);
|
|
50200
50243
|
const alreadyDistributed = new Set(room.distributedTo ?? []);
|
|
50244
|
+
const pendingWsSends = [];
|
|
50201
50245
|
for (const convId of room.conversationIds) {
|
|
50202
50246
|
const session = this._sessions.get(convId);
|
|
50203
50247
|
if (!session || alreadyDistributed.has(session.ownerDeviceId)) continue;
|
|
50204
50248
|
const encrypted = session.ratchet.encrypt(distJson);
|
|
50205
50249
|
const transport = encryptedMessageToTransport(encrypted);
|
|
50206
50250
|
if (this._state === "ready" && this._ws) {
|
|
50207
|
-
|
|
50251
|
+
pendingWsSends.push(
|
|
50208
50252
|
JSON.stringify({
|
|
50209
50253
|
event: "sender_key_distribution",
|
|
50210
50254
|
data: {
|
|
@@ -50220,6 +50264,9 @@ ${messageText}`;
|
|
|
50220
50264
|
}
|
|
50221
50265
|
room.distributedTo = [...alreadyDistributed];
|
|
50222
50266
|
await this._persistState();
|
|
50267
|
+
for (const frame of pendingWsSends) {
|
|
50268
|
+
this._ws.send(frame);
|
|
50269
|
+
}
|
|
50223
50270
|
}
|
|
50224
50271
|
/**
|
|
50225
50272
|
* Handle an incoming sender_key_distribution event.
|
|
@@ -50417,8 +50464,10 @@ ${messageText}`;
|
|
|
50417
50464
|
try {
|
|
50418
50465
|
a2aPlaintext = ratchet.decrypt(encryptedMessage);
|
|
50419
50466
|
} catch (decryptErr) {
|
|
50420
|
-
console.error(`[SecureChannel] A2A decrypt failed \u2014 restoring ratchet
|
|
50467
|
+
console.error(`[SecureChannel] A2A decrypt failed \u2014 restoring ratchet, initiating rekey:`, decryptErr);
|
|
50421
50468
|
channelEntry.session.ratchetState = ratchetSnapshot;
|
|
50469
|
+
await this._persistState();
|
|
50470
|
+
await this._initiateA2ARekey(channelId);
|
|
50422
50471
|
return;
|
|
50423
50472
|
}
|
|
50424
50473
|
channelEntry.session.ratchetState = ratchet.serialize();
|
|
@@ -50460,24 +50509,9 @@ ${messageText}`;
|
|
|
50460
50509
|
}
|
|
50461
50510
|
} else {
|
|
50462
50511
|
console.warn(
|
|
50463
|
-
`[SecureChannel] Received encrypted A2A but no session for ${channelId.slice(0, 8)} \u2014
|
|
50512
|
+
`[SecureChannel] Received encrypted A2A but no session for ${channelId.slice(0, 8)} \u2014 initiating rekey`
|
|
50464
50513
|
);
|
|
50465
|
-
|
|
50466
|
-
if (entry && !entry.pendingEphemeralPrivateKey && !entry.session) {
|
|
50467
|
-
try {
|
|
50468
|
-
const a2aEphemeral = await generateEphemeralKeypair();
|
|
50469
|
-
const ephPubHex = bytesToHex(a2aEphemeral.publicKey);
|
|
50470
|
-
entry.pendingEphemeralPrivateKey = bytesToHex(a2aEphemeral.privateKey);
|
|
50471
|
-
this._ws?.send(JSON.stringify({
|
|
50472
|
-
event: "a2a_key_exchange",
|
|
50473
|
-
data: { channel_id: channelId, ephemeral_key: ephPubHex }
|
|
50474
|
-
}));
|
|
50475
|
-
await this._persistState();
|
|
50476
|
-
console.log(`[SecureChannel] On-demand A2A key exchange for ${channelId.slice(0, 8)}`);
|
|
50477
|
-
} catch (kxErr) {
|
|
50478
|
-
console.warn(`[SecureChannel] On-demand key exchange failed:`, kxErr);
|
|
50479
|
-
}
|
|
50480
|
-
}
|
|
50514
|
+
await this._initiateA2ARekey(channelId);
|
|
50481
50515
|
}
|
|
50482
50516
|
} else {
|
|
50483
50517
|
const a2aMsg = {
|
|
@@ -50494,6 +50528,51 @@ ${messageText}`;
|
|
|
50494
50528
|
}
|
|
50495
50529
|
}
|
|
50496
50530
|
}
|
|
50531
|
+
/**
|
|
50532
|
+
* Initiate A2A channel rekey after decrypt failure.
|
|
50533
|
+
* Calls the backend to reset channel to approved, clears local session,
|
|
50534
|
+
* and submits a fresh ephemeral key for key exchange.
|
|
50535
|
+
*/
|
|
50536
|
+
async _initiateA2ARekey(channelId) {
|
|
50537
|
+
const entry = this._persisted?.a2aChannels?.[channelId];
|
|
50538
|
+
if (!entry || !this._deviceJwt || !this._ws) return;
|
|
50539
|
+
const now = Date.now();
|
|
50540
|
+
const lastRekey = entry._lastRekeyAttempt ?? 0;
|
|
50541
|
+
if (now - lastRekey < 3e4) return;
|
|
50542
|
+
entry._lastRekeyAttempt = now;
|
|
50543
|
+
console.log(`[SecureChannel] Initiating A2A rekey for ${channelId.slice(0, 8)}...`);
|
|
50544
|
+
try {
|
|
50545
|
+
const res = await fetch(
|
|
50546
|
+
`${this.config.apiUrl}/api/v1/a2a/channels/${channelId}/rekey`,
|
|
50547
|
+
{
|
|
50548
|
+
method: "POST",
|
|
50549
|
+
headers: { Authorization: `Bearer ${this._deviceJwt}` }
|
|
50550
|
+
}
|
|
50551
|
+
);
|
|
50552
|
+
if (!res.ok) {
|
|
50553
|
+
const detail = await res.text();
|
|
50554
|
+
console.warn(`[SecureChannel] A2A rekey API failed (${res.status}): ${detail}`);
|
|
50555
|
+
return;
|
|
50556
|
+
}
|
|
50557
|
+
delete entry.session;
|
|
50558
|
+
delete entry.observerSession;
|
|
50559
|
+
delete entry.pendingEphemeralPrivateKey;
|
|
50560
|
+
delete entry.pendingObserverEphemeralPrivateKey;
|
|
50561
|
+
const a2aEphemeral = await generateEphemeralKeypair();
|
|
50562
|
+
entry.pendingEphemeralPrivateKey = bytesToHex(a2aEphemeral.privateKey);
|
|
50563
|
+
this._ws.send(JSON.stringify({
|
|
50564
|
+
event: "a2a_key_exchange",
|
|
50565
|
+
data: {
|
|
50566
|
+
channel_id: channelId,
|
|
50567
|
+
ephemeral_key: bytesToHex(a2aEphemeral.publicKey)
|
|
50568
|
+
}
|
|
50569
|
+
}));
|
|
50570
|
+
await this._persistState();
|
|
50571
|
+
console.log(`[SecureChannel] A2A rekey submitted for ${channelId.slice(0, 8)}`);
|
|
50572
|
+
} catch (err) {
|
|
50573
|
+
console.warn(`[SecureChannel] A2A rekey failed:`, err);
|
|
50574
|
+
}
|
|
50575
|
+
}
|
|
50497
50576
|
/**
|
|
50498
50577
|
* Paginated sync: fetch missed messages in pages of 200, up to 5 pages (1000 messages).
|
|
50499
50578
|
* Tracks message IDs in _syncMessageIds to prevent duplicate processing from concurrent WS messages.
|
|
@@ -50914,6 +50993,8 @@ ${messageText}`;
|
|
|
50914
50993
|
const hasA2AChannels = !!this._persisted.a2aChannels && Object.keys(this._persisted.a2aChannels).length > 0;
|
|
50915
50994
|
if (!hasOwnerSessions && !hasA2AChannels) return;
|
|
50916
50995
|
this._persisted.lastInboundRoomId = this._lastInboundRoomId;
|
|
50996
|
+
this._persisted.seenMessageIds = [...this._seenMessageIds].slice(-_SecureChannel.SEEN_MSG_MAX);
|
|
50997
|
+
this._persisted.seenA2AMessageIds = [...this._a2aSeenMessageIds].slice(-_SecureChannel.A2A_SEEN_MAX);
|
|
50917
50998
|
for (const [convId, session] of this._sessions) {
|
|
50918
50999
|
this._persisted.sessions[convId] = {
|
|
50919
51000
|
ownerDeviceId: session.ownerDeviceId,
|