@silicaclaw/cli 2026.3.20-16 → 2026.3.20-18

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/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
 
3
3
  ## v1.0 beta - 2026-03-20
4
4
 
5
+ ### 2026.3.20-18
6
+
7
+ - release build:
8
+ - prepared another fresh latest-channel package build without publishing
9
+ - regenerated the npm tarball through the verified release packing workflow
10
+
11
+ ### 2026.3.20-17
12
+
13
+ - release build:
14
+ - prepared another fresh latest-channel package build without publishing
15
+ - regenerated the npm tarball through the verified release packing workflow
16
+
5
17
  ### 2026.3.20-16
6
18
 
7
19
  - release build:
package/VERSION CHANGED
@@ -1 +1 @@
1
- v2026.3.20-16
1
+ v2026.3.20-18
@@ -180,6 +180,7 @@ export declare class LocalNodeService {
180
180
  private privateMessageReceipts;
181
181
  private privateEncryptionKeyPair;
182
182
  private privatePeerRoutes;
183
+ private privatePeerEncryptionKeys;
183
184
  private privateMessageBodyCache;
184
185
  private privateMessageDeliveryStatusCache;
185
186
  private messageGovernance;
@@ -850,6 +851,8 @@ export declare class LocalNodeService {
850
851
  private persistPrivateMessageReceipts;
851
852
  private flushPrivatePersistence;
852
853
  private flushPrivateMessagesPersist;
854
+ private hydratePrivateMessageBodyCache;
855
+ private buildPersistedPrivateMessages;
853
856
  private flushPrivateMessageReceiptsPersist;
854
857
  private log;
855
858
  private getAdapterDiagnostics;
@@ -786,6 +786,7 @@ class LocalNodeService {
786
786
  privateMessageReceipts = [];
787
787
  privateEncryptionKeyPair = null;
788
788
  privatePeerRoutes = {};
789
+ privatePeerEncryptionKeys = {};
789
790
  privateMessageBodyCache = new Map();
790
791
  privateMessageDeliveryStatusCache = new Map();
791
792
  messageGovernance;
@@ -1638,12 +1639,13 @@ class LocalNodeService {
1638
1639
  const peerProfile = this.directory.profiles[peerAgentId];
1639
1640
  const current = conversations.get(message.conversation_id);
1640
1641
  const nextLast = Math.max(current?.last_message_at || 0, message.created_at || 0) || null;
1642
+ const learnedPeerKey = this.privatePeerEncryptionKeys[peerAgentId] || "";
1641
1643
  conversations.set(message.conversation_id, {
1642
1644
  conversation_id: message.conversation_id,
1643
1645
  peer_agent_id: peerAgentId,
1644
1646
  peer_display_name: peerProfile?.display_name || peerAgentId,
1645
1647
  peer_avatar_url: peerProfile?.avatar_url || "",
1646
- peer_public_key: peerProfile?.private_encryption_public_key || "",
1648
+ peer_public_key: learnedPeerKey || peerProfile?.private_encryption_public_key || "",
1647
1649
  last_message_at: nextLast,
1648
1650
  unread_count: current?.unread_count || 0,
1649
1651
  });
@@ -1665,8 +1667,8 @@ class LocalNodeService {
1665
1667
  }
1666
1668
  return !normalizedConversationId || message.conversation_id === normalizedConversationId;
1667
1669
  })
1668
- .sort((a, b) => a.created_at - b.created_at)
1669
- .slice(-resolvedLimit)
1670
+ .sort((a, b) => b.created_at - a.created_at)
1671
+ .slice(0, resolvedLimit)
1670
1672
  .map((message) => ({
1671
1673
  message_id: message.message_id,
1672
1674
  conversation_id: message.conversation_id,
@@ -1685,7 +1687,9 @@ class LocalNodeService {
1685
1687
  return { sent: false, reason: "missing_identity_or_private_key" };
1686
1688
  }
1687
1689
  const toAgentId = String(input.to_agent_id || "").trim();
1688
- const recipientKey = String(input.recipient_encryption_public_key || "").trim();
1690
+ const learnedRecipientKey = this.privatePeerEncryptionKeys[toAgentId] || "";
1691
+ const profileRecipientKey = this.directory.profiles[toAgentId]?.private_encryption_public_key || "";
1692
+ const recipientKey = String(learnedRecipientKey || input.recipient_encryption_public_key || profileRecipientKey || "").trim();
1689
1693
  const body = String(input.body || "").trim();
1690
1694
  if (toAgentId === this.identity.agent_id) {
1691
1695
  return { sent: false, reason: "self_private_message_not_allowed" };
@@ -2345,7 +2349,9 @@ class LocalNodeService {
2345
2349
  };
2346
2350
  this.socialMessages = this.normalizeSocialMessages(await this.socialMessageRepo.get());
2347
2351
  this.socialMessageObservations = this.normalizeSocialMessageObservations(await this.socialMessageObservationRepo.get());
2348
- this.privateMessages = this.normalizePrivateMessages(await this.privateMessageRepo.get());
2352
+ const storedPrivateMessages = await this.privateMessageRepo.get();
2353
+ this.hydratePrivateMessageBodyCache(storedPrivateMessages);
2354
+ this.privateMessages = this.normalizePrivateMessages(storedPrivateMessages);
2349
2355
  this.privateMessageReceipts = this.normalizePrivateMessageReceipts(await this.privateMessageReceiptRepo.get());
2350
2356
  this.directory = (0, core_1.ingestProfileRecord)(this.directory, { type: "profile", profile: this.profile });
2351
2357
  this.compactCacheInMemory();
@@ -2439,7 +2445,7 @@ class LocalNodeService {
2439
2445
  return;
2440
2446
  }
2441
2447
  }
2442
- if (meta?.peerId && record.profile.agent_id) {
2448
+ if (meta?.peerId && record.profile.agent_id && !this.privatePeerRoutes[record.profile.agent_id]) {
2443
2449
  this.privatePeerRoutes[record.profile.agent_id] = meta.peerId;
2444
2450
  }
2445
2451
  this.directory = (0, core_1.ingestProfileRecord)(this.directory, record);
@@ -2458,7 +2464,7 @@ class LocalNodeService {
2458
2464
  return;
2459
2465
  }
2460
2466
  }
2461
- if (meta?.peerId && record.agent_id) {
2467
+ if (meta?.peerId && record.agent_id && !this.privatePeerRoutes[record.agent_id]) {
2462
2468
  this.privatePeerRoutes[record.agent_id] = meta.peerId;
2463
2469
  }
2464
2470
  this.directory = (0, core_1.ingestPresenceRecord)(this.directory, record);
@@ -2475,7 +2481,7 @@ class LocalNodeService {
2475
2481
  await this.log("warn", `Rejected social message with invalid signature (${record.message_id.slice(0, 10)})`);
2476
2482
  return;
2477
2483
  }
2478
- if (meta?.peerId && record.agent_id) {
2484
+ if (meta?.peerId && record.agent_id && !this.privatePeerRoutes[record.agent_id]) {
2479
2485
  this.privatePeerRoutes[record.agent_id] = meta.peerId;
2480
2486
  }
2481
2487
  if (this.hasSocialMessage(record.message_id)) {
@@ -2519,6 +2525,12 @@ class LocalNodeService {
2519
2525
  if (!record || !(0, core_1.verifyPrivateMessage)(record)) {
2520
2526
  return;
2521
2527
  }
2528
+ if (meta?.peerId && record.from_agent_id) {
2529
+ this.privatePeerRoutes[record.from_agent_id] = meta.peerId;
2530
+ }
2531
+ if (record.from_agent_id && record.sender_encryption_public_key) {
2532
+ this.privatePeerEncryptionKeys[record.from_agent_id] = record.sender_encryption_public_key;
2533
+ }
2522
2534
  if (record.to_agent_id !== this.identity?.agent_id || this.hasPrivateMessage(record.message_id)) {
2523
2535
  return;
2524
2536
  }
@@ -2531,6 +2543,9 @@ class LocalNodeService {
2531
2543
  if (!receipt || !(0, core_1.verifyPrivateMessageReceipt)(receipt)) {
2532
2544
  return;
2533
2545
  }
2546
+ if (meta?.peerId && receipt.from_agent_id) {
2547
+ this.privatePeerRoutes[receipt.from_agent_id] = meta.peerId;
2548
+ }
2534
2549
  if (receipt.to_agent_id !== this.identity?.agent_id) {
2535
2550
  return;
2536
2551
  }
@@ -2840,7 +2855,37 @@ class LocalNodeService {
2840
2855
  return;
2841
2856
  }
2842
2857
  this.privateMessagesPersistDirty = false;
2843
- await this.privateMessageRepo.set(this.privateMessages);
2858
+ await this.privateMessageRepo.set(this.buildPersistedPrivateMessages());
2859
+ }
2860
+ hydratePrivateMessageBodyCache(items) {
2861
+ if (!Array.isArray(items)) {
2862
+ return;
2863
+ }
2864
+ for (const item of items) {
2865
+ if (typeof item !== "object" || item === null) {
2866
+ continue;
2867
+ }
2868
+ const record = item;
2869
+ const messageId = String(record.message_id || "").trim();
2870
+ const localPlaintext = typeof record.local_plaintext === "string" ? record.local_plaintext : "";
2871
+ if (messageId && localPlaintext) {
2872
+ this.privateMessageBodyCache.set(messageId, localPlaintext);
2873
+ }
2874
+ }
2875
+ }
2876
+ buildPersistedPrivateMessages() {
2877
+ return this.privateMessages.map((message) => {
2878
+ const localPlaintext = message.from_agent_id === this.identity?.agent_id
2879
+ ? this.privateMessageBodyCache.get(message.message_id) || ""
2880
+ : "";
2881
+ if (!localPlaintext) {
2882
+ return { ...message };
2883
+ }
2884
+ return {
2885
+ ...message,
2886
+ local_plaintext: localPlaintext,
2887
+ };
2888
+ });
2844
2889
  }
2845
2890
  async flushPrivateMessageReceiptsPersist() {
2846
2891
  if (this.privateMessageReceiptsPersistTimer) {
@@ -3714,7 +3759,7 @@ async function main() {
3714
3759
  catch {
3715
3760
  // best effort after response has been sent
3716
3761
  }
3717
- }, 150);
3762
+ }, 1200);
3718
3763
  }));
3719
3764
  app.put("/api/profile", asyncRoute(async (req, res) => {
3720
3765
  const body = req.body;
@@ -139,6 +139,10 @@ const SOCIAL_MESSAGE_REPLAY_MAX_PER_BROADCAST = Number(process.env.SOCIAL_MESSAG
139
139
  const SOCIAL_MESSAGE_REPLAY_REFRESH_INTERVAL_MS = Number(
140
140
  process.env.SOCIAL_MESSAGE_REPLAY_REFRESH_INTERVAL_MS || 120_000
141
141
  );
142
+
143
+ type StoredPrivateMessageRecord = PrivateMessageRecord & {
144
+ local_plaintext?: string;
145
+ };
142
146
  const PROFILE_RELAY_REFRESH_INTERVAL_MS = Number(
143
147
  process.env.PROFILE_RELAY_REFRESH_INTERVAL_MS || 120_000
144
148
  );
@@ -1046,6 +1050,7 @@ export class LocalNodeService {
1046
1050
  private privateMessageReceipts: PrivateMessageReceiptRecord[] = [];
1047
1051
  private privateEncryptionKeyPair: PrivateEncryptionKeyPair | null = null;
1048
1052
  private privatePeerRoutes: Record<string, string> = {};
1053
+ private privatePeerEncryptionKeys: Record<string, string> = {};
1049
1054
  private privateMessageBodyCache = new Map<string, string>();
1050
1055
  private privateMessageDeliveryStatusCache = new Map<string, PrivateMessageView["delivery_status"]>();
1051
1056
  private messageGovernance: RuntimeMessageGovernance;
@@ -1987,12 +1992,13 @@ export class LocalNodeService {
1987
1992
  const peerProfile = this.directory.profiles[peerAgentId];
1988
1993
  const current = conversations.get(message.conversation_id);
1989
1994
  const nextLast = Math.max(current?.last_message_at || 0, message.created_at || 0) || null;
1995
+ const learnedPeerKey = this.privatePeerEncryptionKeys[peerAgentId] || "";
1990
1996
  conversations.set(message.conversation_id, {
1991
1997
  conversation_id: message.conversation_id,
1992
1998
  peer_agent_id: peerAgentId,
1993
1999
  peer_display_name: peerProfile?.display_name || peerAgentId,
1994
2000
  peer_avatar_url: peerProfile?.avatar_url || "",
1995
- peer_public_key: peerProfile?.private_encryption_public_key || "",
2001
+ peer_public_key: learnedPeerKey || peerProfile?.private_encryption_public_key || "",
1996
2002
  last_message_at: nextLast,
1997
2003
  unread_count: current?.unread_count || 0,
1998
2004
  });
@@ -2017,8 +2023,8 @@ export class LocalNodeService {
2017
2023
  }
2018
2024
  return !normalizedConversationId || message.conversation_id === normalizedConversationId;
2019
2025
  })
2020
- .sort((a, b) => a.created_at - b.created_at)
2021
- .slice(-resolvedLimit)
2026
+ .sort((a, b) => b.created_at - a.created_at)
2027
+ .slice(0, resolvedLimit)
2022
2028
  .map((message) => ({
2023
2029
  message_id: message.message_id,
2024
2030
  conversation_id: message.conversation_id,
@@ -2043,7 +2049,9 @@ export class LocalNodeService {
2043
2049
  return { sent: false, reason: "missing_identity_or_private_key" };
2044
2050
  }
2045
2051
  const toAgentId = String(input.to_agent_id || "").trim();
2046
- const recipientKey = String(input.recipient_encryption_public_key || "").trim();
2052
+ const learnedRecipientKey = this.privatePeerEncryptionKeys[toAgentId] || "";
2053
+ const profileRecipientKey = this.directory.profiles[toAgentId]?.private_encryption_public_key || "";
2054
+ const recipientKey = String(learnedRecipientKey || input.recipient_encryption_public_key || profileRecipientKey || "").trim();
2047
2055
  const body = String(input.body || "").trim();
2048
2056
  if (toAgentId === this.identity.agent_id) {
2049
2057
  return { sent: false, reason: "self_private_message_not_allowed" };
@@ -2763,7 +2771,9 @@ export class LocalNodeService {
2763
2771
  };
2764
2772
  this.socialMessages = this.normalizeSocialMessages(await this.socialMessageRepo.get());
2765
2773
  this.socialMessageObservations = this.normalizeSocialMessageObservations(await this.socialMessageObservationRepo.get());
2766
- this.privateMessages = this.normalizePrivateMessages(await this.privateMessageRepo.get());
2774
+ const storedPrivateMessages = await this.privateMessageRepo.get();
2775
+ this.hydratePrivateMessageBodyCache(storedPrivateMessages);
2776
+ this.privateMessages = this.normalizePrivateMessages(storedPrivateMessages);
2767
2777
  this.privateMessageReceipts = this.normalizePrivateMessageReceipts(await this.privateMessageReceiptRepo.get());
2768
2778
  this.directory = ingestProfileRecord(this.directory, { type: "profile", profile: this.profile });
2769
2779
  this.compactCacheInMemory();
@@ -2870,7 +2880,7 @@ export class LocalNodeService {
2870
2880
  return;
2871
2881
  }
2872
2882
  }
2873
- if (meta?.peerId && record.profile.agent_id) {
2883
+ if (meta?.peerId && record.profile.agent_id && !this.privatePeerRoutes[record.profile.agent_id]) {
2874
2884
  this.privatePeerRoutes[record.profile.agent_id] = meta.peerId;
2875
2885
  }
2876
2886
 
@@ -2892,7 +2902,7 @@ export class LocalNodeService {
2892
2902
  return;
2893
2903
  }
2894
2904
  }
2895
- if (meta?.peerId && record.agent_id) {
2905
+ if (meta?.peerId && record.agent_id && !this.privatePeerRoutes[record.agent_id]) {
2896
2906
  this.privatePeerRoutes[record.agent_id] = meta.peerId;
2897
2907
  }
2898
2908
 
@@ -2911,7 +2921,7 @@ export class LocalNodeService {
2911
2921
  await this.log("warn", `Rejected social message with invalid signature (${record.message_id.slice(0, 10)})`);
2912
2922
  return;
2913
2923
  }
2914
- if (meta?.peerId && record.agent_id) {
2924
+ if (meta?.peerId && record.agent_id && !this.privatePeerRoutes[record.agent_id]) {
2915
2925
  this.privatePeerRoutes[record.agent_id] = meta.peerId;
2916
2926
  }
2917
2927
  if (this.hasSocialMessage(record.message_id)) {
@@ -2962,6 +2972,12 @@ export class LocalNodeService {
2962
2972
  if (!record || !verifyPrivateMessage(record)) {
2963
2973
  return;
2964
2974
  }
2975
+ if (meta?.peerId && record.from_agent_id) {
2976
+ this.privatePeerRoutes[record.from_agent_id] = meta.peerId;
2977
+ }
2978
+ if (record.from_agent_id && record.sender_encryption_public_key) {
2979
+ this.privatePeerEncryptionKeys[record.from_agent_id] = record.sender_encryption_public_key;
2980
+ }
2965
2981
  if (record.to_agent_id !== this.identity?.agent_id || this.hasPrivateMessage(record.message_id)) {
2966
2982
  return;
2967
2983
  }
@@ -2975,6 +2991,9 @@ export class LocalNodeService {
2975
2991
  if (!receipt || !verifyPrivateMessageReceipt(receipt)) {
2976
2992
  return;
2977
2993
  }
2994
+ if (meta?.peerId && receipt.from_agent_id) {
2995
+ this.privatePeerRoutes[receipt.from_agent_id] = meta.peerId;
2996
+ }
2978
2997
  if (receipt.to_agent_id !== this.identity?.agent_id) {
2979
2998
  return;
2980
2999
  }
@@ -3323,7 +3342,40 @@ export class LocalNodeService {
3323
3342
  return;
3324
3343
  }
3325
3344
  this.privateMessagesPersistDirty = false;
3326
- await this.privateMessageRepo.set(this.privateMessages);
3345
+ await this.privateMessageRepo.set(this.buildPersistedPrivateMessages() as unknown as PrivateMessageRecord[]);
3346
+ }
3347
+
3348
+ private hydratePrivateMessageBodyCache(items: unknown): void {
3349
+ if (!Array.isArray(items)) {
3350
+ return;
3351
+ }
3352
+ for (const item of items) {
3353
+ if (typeof item !== "object" || item === null) {
3354
+ continue;
3355
+ }
3356
+ const record = item as Partial<StoredPrivateMessageRecord>;
3357
+ const messageId = String(record.message_id || "").trim();
3358
+ const localPlaintext = typeof record.local_plaintext === "string" ? record.local_plaintext : "";
3359
+ if (messageId && localPlaintext) {
3360
+ this.privateMessageBodyCache.set(messageId, localPlaintext);
3361
+ }
3362
+ }
3363
+ }
3364
+
3365
+ private buildPersistedPrivateMessages(): StoredPrivateMessageRecord[] {
3366
+ return this.privateMessages.map((message) => {
3367
+ const localPlaintext =
3368
+ message.from_agent_id === this.identity?.agent_id
3369
+ ? this.privateMessageBodyCache.get(message.message_id) || ""
3370
+ : "";
3371
+ if (!localPlaintext) {
3372
+ return { ...message };
3373
+ }
3374
+ return {
3375
+ ...message,
3376
+ local_plaintext: localPlaintext,
3377
+ };
3378
+ });
3327
3379
  }
3328
3380
 
3329
3381
  private async flushPrivateMessageReceiptsPersist(): Promise<void> {
@@ -4306,7 +4358,7 @@ export async function main() {
4306
4358
  } catch {
4307
4359
  // best effort after response has been sent
4308
4360
  }
4309
- }, 150);
4361
+ }, 1200);
4310
4362
  })
4311
4363
  );
4312
4364
 
@@ -1 +1 @@
1
- 2026.3.20-beta.16
1
+ 2026.3.20-beta.18
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "silicaclaw-broadcast",
3
- "version": "2026.3.20-beta.16",
3
+ "version": "2026.3.20-beta.18",
4
4
  "display_name": "SilicaClaw Broadcast",
5
5
  "description": "Official OpenClaw skill for a bounded local SilicaClaw broadcast workflow: read public broadcasts, publish public broadcasts, and optionally forward owner-relevant summaries through OpenClaw's native channel.",
6
6
  "entrypoints": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@silicaclaw/cli",
3
- "version": "2026.3.20-16",
3
+ "version": "2026.3.20-18",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"