@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/index.js
CHANGED
|
@@ -44743,7 +44743,7 @@ var init_ratchet = __esm({
|
|
|
44743
44743
|
async "../crypto/dist/ratchet.js"() {
|
|
44744
44744
|
"use strict";
|
|
44745
44745
|
await init_libsodium_wrappers();
|
|
44746
|
-
MAX_SKIP =
|
|
44746
|
+
MAX_SKIP = 1e3;
|
|
44747
44747
|
CHAIN_KEY_SEED = new Uint8Array([1]);
|
|
44748
44748
|
MSG_KEY_SEED = new Uint8Array([2]);
|
|
44749
44749
|
HEADER_KEY_SEED = new Uint8Array([3]);
|
|
@@ -47236,6 +47236,12 @@ var init_channel = __esm({
|
|
|
47236
47236
|
this._primaryConversationId = this._persisted.primaryConversationId;
|
|
47237
47237
|
this._fingerprint = this._persisted.fingerprint;
|
|
47238
47238
|
this._lastInboundRoomId = this._persisted.lastInboundRoomId;
|
|
47239
|
+
if (this._persisted.seenMessageIds) {
|
|
47240
|
+
this._seenMessageIds = new Set(this._persisted.seenMessageIds.slice(-_SecureChannel.SEEN_MSG_MAX));
|
|
47241
|
+
}
|
|
47242
|
+
if (this._persisted.seenA2AMessageIds) {
|
|
47243
|
+
this._a2aSeenMessageIds = new Set(this._persisted.seenA2AMessageIds.slice(-_SecureChannel.A2A_SEEN_MAX));
|
|
47244
|
+
}
|
|
47239
47245
|
for (const [convId, sessionData] of Object.entries(
|
|
47240
47246
|
this._persisted.sessions
|
|
47241
47247
|
)) {
|
|
@@ -47382,6 +47388,7 @@ var init_channel = __esm({
|
|
|
47382
47388
|
}
|
|
47383
47389
|
const messageGroupId = randomUUID2();
|
|
47384
47390
|
let sentCount = 0;
|
|
47391
|
+
const pendingWsSends = [];
|
|
47385
47392
|
for (const [convId, session] of this._sessions) {
|
|
47386
47393
|
if (!session.activated) continue;
|
|
47387
47394
|
if (roomConvIds.has(convId)) continue;
|
|
@@ -47414,7 +47421,7 @@ var init_channel = __esm({
|
|
|
47414
47421
|
if (this._persisted?.hubId) {
|
|
47415
47422
|
payload.sender_hub_id = this._persisted.hubId;
|
|
47416
47423
|
}
|
|
47417
|
-
|
|
47424
|
+
pendingWsSends.push(
|
|
47418
47425
|
JSON.stringify({
|
|
47419
47426
|
event: "message",
|
|
47420
47427
|
data: payload
|
|
@@ -47440,6 +47447,9 @@ var init_channel = __esm({
|
|
|
47440
47447
|
console.warn("[SecureChannel] send() delivered to 0 sessions (all skipped or failed)");
|
|
47441
47448
|
}
|
|
47442
47449
|
await this._persistState();
|
|
47450
|
+
for (const frame of pendingWsSends) {
|
|
47451
|
+
this._ws.send(frame);
|
|
47452
|
+
}
|
|
47443
47453
|
}
|
|
47444
47454
|
/**
|
|
47445
47455
|
* Send a typing indicator to all owner devices.
|
|
@@ -47718,6 +47728,7 @@ var init_channel = __esm({
|
|
|
47718
47728
|
if (recipients.length === 0) {
|
|
47719
47729
|
throw new Error("No active sessions in room");
|
|
47720
47730
|
}
|
|
47731
|
+
await this._persistState();
|
|
47721
47732
|
if (this._state === "ready" && this._ws) {
|
|
47722
47733
|
this._ws.send(
|
|
47723
47734
|
JSON.stringify({
|
|
@@ -47757,7 +47768,6 @@ var init_channel = __esm({
|
|
|
47757
47768
|
throw new Error(`Failed to send room message: ${err}`);
|
|
47758
47769
|
}
|
|
47759
47770
|
}
|
|
47760
|
-
await this._persistState();
|
|
47761
47771
|
}
|
|
47762
47772
|
/**
|
|
47763
47773
|
* Leave a room: remove sessions and persisted room state.
|
|
@@ -47856,11 +47866,12 @@ var init_channel = __esm({
|
|
|
47856
47866
|
attachment: attachMeta
|
|
47857
47867
|
});
|
|
47858
47868
|
const messageGroupId = randomUUID2();
|
|
47869
|
+
const pendingWsSends = [];
|
|
47859
47870
|
for (const [convId, session] of this._sessions) {
|
|
47860
47871
|
if (!session.activated) continue;
|
|
47861
47872
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
47862
47873
|
const transport = encryptedMessageToTransport(encrypted);
|
|
47863
|
-
|
|
47874
|
+
pendingWsSends.push(
|
|
47864
47875
|
JSON.stringify({
|
|
47865
47876
|
event: "message",
|
|
47866
47877
|
data: {
|
|
@@ -47874,6 +47885,9 @@ var init_channel = __esm({
|
|
|
47874
47885
|
);
|
|
47875
47886
|
}
|
|
47876
47887
|
await this._persistState();
|
|
47888
|
+
for (const frame of pendingWsSends) {
|
|
47889
|
+
this._ws.send(frame);
|
|
47890
|
+
}
|
|
47877
47891
|
}
|
|
47878
47892
|
async sendActionConfirmation(confirmation) {
|
|
47879
47893
|
const envelope = {
|
|
@@ -48885,6 +48899,9 @@ var init_channel = __esm({
|
|
|
48885
48899
|
this._lastWakeTick = Date.now();
|
|
48886
48900
|
try {
|
|
48887
48901
|
const data = JSON.parse(raw.toString());
|
|
48902
|
+
if (data.event && data.event !== "ping" && data.event !== "typing") {
|
|
48903
|
+
console.log(`[SecureChannel] WS event: ${data.event} conv=${(data.conversation_id || data.data?.conversation_id || "").toString().slice(0, 8)}`);
|
|
48904
|
+
}
|
|
48888
48905
|
if (data.event === "ping") {
|
|
48889
48906
|
ws.send(JSON.stringify({ event: "pong" }));
|
|
48890
48907
|
return;
|
|
@@ -49687,11 +49704,12 @@ ${messageText}`;
|
|
|
49687
49704
|
});
|
|
49688
49705
|
this._appendHistory("agent", plaintext, topicId);
|
|
49689
49706
|
const messageGroupId = randomUUID2();
|
|
49707
|
+
const pendingWsSends = [];
|
|
49690
49708
|
for (const [convId, session] of this._sessions) {
|
|
49691
49709
|
if (!session.activated) continue;
|
|
49692
49710
|
const encrypted = session.ratchet.encrypt(envelope);
|
|
49693
49711
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49694
|
-
|
|
49712
|
+
pendingWsSends.push(
|
|
49695
49713
|
JSON.stringify({
|
|
49696
49714
|
event: "message",
|
|
49697
49715
|
data: {
|
|
@@ -49705,6 +49723,9 @@ ${messageText}`;
|
|
|
49705
49723
|
);
|
|
49706
49724
|
}
|
|
49707
49725
|
await this._persistState();
|
|
49726
|
+
for (const frame of pendingWsSends) {
|
|
49727
|
+
this._ws.send(frame);
|
|
49728
|
+
}
|
|
49708
49729
|
}
|
|
49709
49730
|
/**
|
|
49710
49731
|
* Relay an owner's message to all sibling sessions as encrypted sync messages.
|
|
@@ -49727,13 +49748,14 @@ ${messageText}`;
|
|
|
49727
49748
|
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49728
49749
|
topicId
|
|
49729
49750
|
});
|
|
49751
|
+
const pendingWsSends = [];
|
|
49730
49752
|
for (const [siblingConvId, siblingSession] of this._sessions) {
|
|
49731
49753
|
if (siblingConvId === sourceConvId) continue;
|
|
49732
49754
|
if (!siblingSession.activated) continue;
|
|
49733
49755
|
if (roomConvIds.has(siblingConvId)) continue;
|
|
49734
49756
|
const syncEncrypted = siblingSession.ratchet.encrypt(syncPayload);
|
|
49735
49757
|
const syncTransport = encryptedMessageToTransport(syncEncrypted);
|
|
49736
|
-
|
|
49758
|
+
pendingWsSends.push(
|
|
49737
49759
|
JSON.stringify({
|
|
49738
49760
|
event: "message",
|
|
49739
49761
|
data: {
|
|
@@ -49744,6 +49766,10 @@ ${messageText}`;
|
|
|
49744
49766
|
})
|
|
49745
49767
|
);
|
|
49746
49768
|
}
|
|
49769
|
+
await this._persistState();
|
|
49770
|
+
for (const frame of pendingWsSends) {
|
|
49771
|
+
this._ws.send(frame);
|
|
49772
|
+
}
|
|
49747
49773
|
}
|
|
49748
49774
|
/**
|
|
49749
49775
|
* Resolve the agent's workspace directory.
|
|
@@ -49787,6 +49813,7 @@ ${messageText}`;
|
|
|
49787
49813
|
const plaintext = JSON.stringify(payload);
|
|
49788
49814
|
const encrypted = session.ratchet.encrypt(plaintext);
|
|
49789
49815
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49816
|
+
await this._persistState();
|
|
49790
49817
|
this._ws.send(
|
|
49791
49818
|
JSON.stringify({
|
|
49792
49819
|
event: "message",
|
|
@@ -49797,7 +49824,6 @@ ${messageText}`;
|
|
|
49797
49824
|
}
|
|
49798
49825
|
})
|
|
49799
49826
|
);
|
|
49800
|
-
await this._persistState();
|
|
49801
49827
|
}
|
|
49802
49828
|
/**
|
|
49803
49829
|
* Send stored message history to a newly-activated session.
|
|
@@ -49820,6 +49846,7 @@ ${messageText}`;
|
|
|
49820
49846
|
});
|
|
49821
49847
|
const encrypted = session.ratchet.encrypt(replayPayload);
|
|
49822
49848
|
const transport = encryptedMessageToTransport(encrypted);
|
|
49849
|
+
await this._persistState();
|
|
49823
49850
|
this._ws.send(
|
|
49824
49851
|
JSON.stringify({
|
|
49825
49852
|
event: "message",
|
|
@@ -50072,15 +50099,31 @@ ${messageText}`;
|
|
|
50072
50099
|
console.log(
|
|
50073
50100
|
`[SecureChannel] Room ratchet re-initialized for conv ${convId.slice(0, 8)}...`
|
|
50074
50101
|
);
|
|
50075
|
-
|
|
50076
|
-
|
|
50077
|
-
|
|
50078
|
-
|
|
50102
|
+
const incomingMsgNum = encrypted.header.messageNumber;
|
|
50103
|
+
if (incomingMsgNum <= 5) {
|
|
50104
|
+
try {
|
|
50105
|
+
plaintext = session.ratchet.decrypt(encrypted);
|
|
50106
|
+
session.activated = true;
|
|
50107
|
+
if (this._persisted.sessions[convId]) {
|
|
50108
|
+
this._persisted.sessions[convId].activated = true;
|
|
50109
|
+
}
|
|
50110
|
+
await this._persistState();
|
|
50111
|
+
console.log(
|
|
50112
|
+
`[SecureChannel] Room session ${convId.slice(0, 8)}... re-activated after ratchet re-init`
|
|
50113
|
+
);
|
|
50114
|
+
} catch (retryErr) {
|
|
50115
|
+
console.warn(
|
|
50116
|
+
`[SecureChannel] Room re-init retry failed for conv ${convId.slice(0, 8)} (msgNum=${incomingMsgNum}):`,
|
|
50117
|
+
retryErr
|
|
50118
|
+
);
|
|
50119
|
+
return;
|
|
50120
|
+
}
|
|
50121
|
+
} else {
|
|
50122
|
+
console.log(
|
|
50123
|
+
`[SecureChannel] Room re-init: skipping message with msgNum=${incomingMsgNum} for conv ${convId.slice(0, 8)} (too far ahead for fresh ratchet)`
|
|
50124
|
+
);
|
|
50125
|
+
return;
|
|
50079
50126
|
}
|
|
50080
|
-
await this._persistState();
|
|
50081
|
-
console.log(
|
|
50082
|
-
`[SecureChannel] Room session ${convId.slice(0, 8)}... re-activated after ratchet re-init`
|
|
50083
|
-
);
|
|
50084
50127
|
} catch (reinitErr) {
|
|
50085
50128
|
console.error(
|
|
50086
50129
|
`[SecureChannel] Room ratchet re-init failed for conv ${convId.slice(0, 8)}...:`,
|
|
@@ -50259,13 +50302,14 @@ ${messageText}`;
|
|
|
50259
50302
|
const dist = chain.getDistribution(this._deviceId);
|
|
50260
50303
|
const distJson = JSON.stringify(dist);
|
|
50261
50304
|
const alreadyDistributed = new Set(room.distributedTo ?? []);
|
|
50305
|
+
const pendingWsSends = [];
|
|
50262
50306
|
for (const convId of room.conversationIds) {
|
|
50263
50307
|
const session = this._sessions.get(convId);
|
|
50264
50308
|
if (!session || alreadyDistributed.has(session.ownerDeviceId)) continue;
|
|
50265
50309
|
const encrypted = session.ratchet.encrypt(distJson);
|
|
50266
50310
|
const transport = encryptedMessageToTransport(encrypted);
|
|
50267
50311
|
if (this._state === "ready" && this._ws) {
|
|
50268
|
-
|
|
50312
|
+
pendingWsSends.push(
|
|
50269
50313
|
JSON.stringify({
|
|
50270
50314
|
event: "sender_key_distribution",
|
|
50271
50315
|
data: {
|
|
@@ -50281,6 +50325,9 @@ ${messageText}`;
|
|
|
50281
50325
|
}
|
|
50282
50326
|
room.distributedTo = [...alreadyDistributed];
|
|
50283
50327
|
await this._persistState();
|
|
50328
|
+
for (const frame of pendingWsSends) {
|
|
50329
|
+
this._ws.send(frame);
|
|
50330
|
+
}
|
|
50284
50331
|
}
|
|
50285
50332
|
/**
|
|
50286
50333
|
* Handle an incoming sender_key_distribution event.
|
|
@@ -50478,8 +50525,10 @@ ${messageText}`;
|
|
|
50478
50525
|
try {
|
|
50479
50526
|
a2aPlaintext = ratchet.decrypt(encryptedMessage);
|
|
50480
50527
|
} catch (decryptErr) {
|
|
50481
|
-
console.error(`[SecureChannel] A2A decrypt failed \u2014 restoring ratchet
|
|
50528
|
+
console.error(`[SecureChannel] A2A decrypt failed \u2014 restoring ratchet, initiating rekey:`, decryptErr);
|
|
50482
50529
|
channelEntry.session.ratchetState = ratchetSnapshot;
|
|
50530
|
+
await this._persistState();
|
|
50531
|
+
await this._initiateA2ARekey(channelId);
|
|
50483
50532
|
return;
|
|
50484
50533
|
}
|
|
50485
50534
|
channelEntry.session.ratchetState = ratchet.serialize();
|
|
@@ -50521,24 +50570,9 @@ ${messageText}`;
|
|
|
50521
50570
|
}
|
|
50522
50571
|
} else {
|
|
50523
50572
|
console.warn(
|
|
50524
|
-
`[SecureChannel] Received encrypted A2A but no session for ${channelId.slice(0, 8)} \u2014
|
|
50573
|
+
`[SecureChannel] Received encrypted A2A but no session for ${channelId.slice(0, 8)} \u2014 initiating rekey`
|
|
50525
50574
|
);
|
|
50526
|
-
|
|
50527
|
-
if (entry && !entry.pendingEphemeralPrivateKey && !entry.session) {
|
|
50528
|
-
try {
|
|
50529
|
-
const a2aEphemeral = await generateEphemeralKeypair();
|
|
50530
|
-
const ephPubHex = bytesToHex(a2aEphemeral.publicKey);
|
|
50531
|
-
entry.pendingEphemeralPrivateKey = bytesToHex(a2aEphemeral.privateKey);
|
|
50532
|
-
this._ws?.send(JSON.stringify({
|
|
50533
|
-
event: "a2a_key_exchange",
|
|
50534
|
-
data: { channel_id: channelId, ephemeral_key: ephPubHex }
|
|
50535
|
-
}));
|
|
50536
|
-
await this._persistState();
|
|
50537
|
-
console.log(`[SecureChannel] On-demand A2A key exchange for ${channelId.slice(0, 8)}`);
|
|
50538
|
-
} catch (kxErr) {
|
|
50539
|
-
console.warn(`[SecureChannel] On-demand key exchange failed:`, kxErr);
|
|
50540
|
-
}
|
|
50541
|
-
}
|
|
50575
|
+
await this._initiateA2ARekey(channelId);
|
|
50542
50576
|
}
|
|
50543
50577
|
} else {
|
|
50544
50578
|
const a2aMsg = {
|
|
@@ -50555,6 +50589,51 @@ ${messageText}`;
|
|
|
50555
50589
|
}
|
|
50556
50590
|
}
|
|
50557
50591
|
}
|
|
50592
|
+
/**
|
|
50593
|
+
* Initiate A2A channel rekey after decrypt failure.
|
|
50594
|
+
* Calls the backend to reset channel to approved, clears local session,
|
|
50595
|
+
* and submits a fresh ephemeral key for key exchange.
|
|
50596
|
+
*/
|
|
50597
|
+
async _initiateA2ARekey(channelId) {
|
|
50598
|
+
const entry = this._persisted?.a2aChannels?.[channelId];
|
|
50599
|
+
if (!entry || !this._deviceJwt || !this._ws) return;
|
|
50600
|
+
const now = Date.now();
|
|
50601
|
+
const lastRekey = entry._lastRekeyAttempt ?? 0;
|
|
50602
|
+
if (now - lastRekey < 3e4) return;
|
|
50603
|
+
entry._lastRekeyAttempt = now;
|
|
50604
|
+
console.log(`[SecureChannel] Initiating A2A rekey for ${channelId.slice(0, 8)}...`);
|
|
50605
|
+
try {
|
|
50606
|
+
const res = await fetch(
|
|
50607
|
+
`${this.config.apiUrl}/api/v1/a2a/channels/${channelId}/rekey`,
|
|
50608
|
+
{
|
|
50609
|
+
method: "POST",
|
|
50610
|
+
headers: { Authorization: `Bearer ${this._deviceJwt}` }
|
|
50611
|
+
}
|
|
50612
|
+
);
|
|
50613
|
+
if (!res.ok) {
|
|
50614
|
+
const detail = await res.text();
|
|
50615
|
+
console.warn(`[SecureChannel] A2A rekey API failed (${res.status}): ${detail}`);
|
|
50616
|
+
return;
|
|
50617
|
+
}
|
|
50618
|
+
delete entry.session;
|
|
50619
|
+
delete entry.observerSession;
|
|
50620
|
+
delete entry.pendingEphemeralPrivateKey;
|
|
50621
|
+
delete entry.pendingObserverEphemeralPrivateKey;
|
|
50622
|
+
const a2aEphemeral = await generateEphemeralKeypair();
|
|
50623
|
+
entry.pendingEphemeralPrivateKey = bytesToHex(a2aEphemeral.privateKey);
|
|
50624
|
+
this._ws.send(JSON.stringify({
|
|
50625
|
+
event: "a2a_key_exchange",
|
|
50626
|
+
data: {
|
|
50627
|
+
channel_id: channelId,
|
|
50628
|
+
ephemeral_key: bytesToHex(a2aEphemeral.publicKey)
|
|
50629
|
+
}
|
|
50630
|
+
}));
|
|
50631
|
+
await this._persistState();
|
|
50632
|
+
console.log(`[SecureChannel] A2A rekey submitted for ${channelId.slice(0, 8)}`);
|
|
50633
|
+
} catch (err) {
|
|
50634
|
+
console.warn(`[SecureChannel] A2A rekey failed:`, err);
|
|
50635
|
+
}
|
|
50636
|
+
}
|
|
50558
50637
|
/**
|
|
50559
50638
|
* Paginated sync: fetch missed messages in pages of 200, up to 5 pages (1000 messages).
|
|
50560
50639
|
* Tracks message IDs in _syncMessageIds to prevent duplicate processing from concurrent WS messages.
|
|
@@ -50975,6 +51054,8 @@ ${messageText}`;
|
|
|
50975
51054
|
const hasA2AChannels = !!this._persisted.a2aChannels && Object.keys(this._persisted.a2aChannels).length > 0;
|
|
50976
51055
|
if (!hasOwnerSessions && !hasA2AChannels) return;
|
|
50977
51056
|
this._persisted.lastInboundRoomId = this._lastInboundRoomId;
|
|
51057
|
+
this._persisted.seenMessageIds = [...this._seenMessageIds].slice(-_SecureChannel.SEEN_MSG_MAX);
|
|
51058
|
+
this._persisted.seenA2AMessageIds = [...this._a2aSeenMessageIds].slice(-_SecureChannel.A2A_SEEN_MAX);
|
|
50978
51059
|
for (const [convId, session] of this._sessions) {
|
|
50979
51060
|
this._persisted.sessions[convId] = {
|
|
50980
51061
|
ownerDeviceId: session.ownerDeviceId,
|