@silicaclaw/cli 2026.3.20-14 → 2026.3.20-15

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,7 +2,7 @@
2
2
 
3
3
  ## v1.0 beta - 2026-03-20
4
4
 
5
- ### 2026.3.20-14
5
+ ### 2026.3.20-15
6
6
 
7
7
  - release build:
8
8
  - prepared another fresh latest-channel package build without publishing
package/VERSION CHANGED
@@ -1 +1 @@
1
- v2026.3.20-14
1
+ v2026.3.20-15
@@ -623,7 +623,6 @@ export declare class LocalNodeService {
623
623
  getIdentity(): AgentIdentity | null;
624
624
  getSocialMessages(limit?: number, options?: {
625
625
  agent_id?: string | null;
626
- offset?: number | null;
627
626
  }): {
628
627
  total: number;
629
628
  items: SocialMessageView[];
@@ -657,10 +656,7 @@ export declare class LocalNodeService {
657
656
  last_message_at: number | null;
658
657
  unread_count: number;
659
658
  }>;
660
- getPrivateMessages(conversationId: string, limit?: number, offset?: number): {
661
- total: number;
662
- items: PrivateMessageView[];
663
- };
659
+ getPrivateMessages(conversationId: string, limit?: number): PrivateMessageView[];
664
660
  sendPrivateMessage(input: {
665
661
  to_agent_id: string;
666
662
  recipient_encryption_public_key: string;
@@ -1580,7 +1580,6 @@ class LocalNodeService {
1580
1580
  }
1581
1581
  getSocialMessages(limit = 50, options) {
1582
1582
  const resolvedLimit = Math.max(1, Math.min(200, Number(limit) || 50));
1583
- const resolvedOffset = Math.max(0, Number(options?.offset) || 0);
1584
1583
  this.ensureLocalDirectoryBaseline();
1585
1584
  this.compactCacheInMemory();
1586
1585
  const agentId = String(options?.agent_id || "").trim();
@@ -1589,7 +1588,7 @@ class LocalNodeService {
1589
1588
  : this.socialMessages;
1590
1589
  return {
1591
1590
  total: filtered.length,
1592
- items: filtered.slice(resolvedOffset, resolvedOffset + resolvedLimit).map((message) => {
1591
+ items: filtered.slice(0, resolvedLimit).map((message) => {
1593
1592
  const profile = this.directory.profiles[message.agent_id];
1594
1593
  const lastSeenAt = this.directory.presence[message.agent_id] ?? 0;
1595
1594
  const observations = this.socialMessageObservations.filter((item) => item.message_id === message.message_id);
@@ -1651,12 +1650,11 @@ class LocalNodeService {
1651
1650
  }
1652
1651
  return Array.from(conversations.values()).sort((a, b) => (b.last_message_at || 0) - (a.last_message_at || 0));
1653
1652
  }
1654
- getPrivateMessages(conversationId, limit = PRIVATE_MESSAGE_QUERY_LIMIT, offset = 0) {
1653
+ getPrivateMessages(conversationId, limit = PRIVATE_MESSAGE_QUERY_LIMIT) {
1655
1654
  const normalizedConversationId = String(conversationId || "").trim();
1656
1655
  const resolvedLimit = Math.max(1, Math.min(PRIVATE_MESSAGE_QUERY_LIMIT, Number(limit) || PRIVATE_MESSAGE_QUERY_LIMIT));
1657
- const resolvedOffset = Math.max(0, Number(offset) || 0);
1658
1656
  const receiptsByMessageId = new Map(this.privateMessageReceipts.map((receipt) => [receipt.message_id, receipt.status]));
1659
- const filtered = this.privateMessages
1657
+ return this.privateMessages
1660
1658
  .filter((message) => {
1661
1659
  if (message.from_agent_id === message.to_agent_id) {
1662
1660
  return false;
@@ -1667,24 +1665,20 @@ class LocalNodeService {
1667
1665
  }
1668
1666
  return !normalizedConversationId || message.conversation_id === normalizedConversationId;
1669
1667
  })
1670
- .sort((a, b) => a.created_at - b.created_at);
1671
- const total = filtered.length;
1672
- const paged = filtered.slice(Math.max(0, total - resolvedOffset - resolvedLimit), Math.max(0, total - resolvedOffset));
1673
- return {
1674
- total,
1675
- items: paged.map((message) => ({
1676
- message_id: message.message_id,
1677
- conversation_id: message.conversation_id,
1678
- from_agent_id: message.from_agent_id,
1679
- to_agent_id: message.to_agent_id,
1680
- body: this.decryptPrivateMessageBody(message),
1681
- created_at: message.created_at,
1682
- is_self: message.from_agent_id === this.identity?.agent_id,
1683
- delivery_status: receiptsByMessageId.get(message.message_id) ||
1684
- this.privateMessageDeliveryStatusCache.get(message.message_id) ||
1685
- (message.from_agent_id === this.identity?.agent_id ? "fallback-sent" : "sent"),
1686
- })),
1687
- };
1668
+ .sort((a, b) => a.created_at - b.created_at)
1669
+ .slice(-resolvedLimit)
1670
+ .map((message) => ({
1671
+ message_id: message.message_id,
1672
+ conversation_id: message.conversation_id,
1673
+ from_agent_id: message.from_agent_id,
1674
+ to_agent_id: message.to_agent_id,
1675
+ body: this.decryptPrivateMessageBody(message),
1676
+ created_at: message.created_at,
1677
+ is_self: message.from_agent_id === this.identity?.agent_id,
1678
+ delivery_status: receiptsByMessageId.get(message.message_id) ||
1679
+ this.privateMessageDeliveryStatusCache.get(message.message_id) ||
1680
+ (message.from_agent_id === this.identity?.agent_id ? "fallback-sent" : "sent"),
1681
+ }));
1688
1682
  }
1689
1683
  async sendPrivateMessage(input) {
1690
1684
  if (!this.identity || !this.privateEncryptionKeyPair) {
@@ -1733,7 +1727,7 @@ class LocalNodeService {
1733
1727
  await this.publish(PRIVATE_MESSAGE_TOPIC, message);
1734
1728
  }
1735
1729
  this.privateMessageDeliveryStatusCache.set(message.message_id, reason);
1736
- const view = this.getPrivateMessages(message.conversation_id, PRIVATE_MESSAGE_QUERY_LIMIT, 0).items.find((item) => item.message_id === message.message_id);
1730
+ const view = this.getPrivateMessages(message.conversation_id).find((item) => item.message_id === message.message_id);
1737
1731
  if (view) {
1738
1732
  view.delivery_status = reason;
1739
1733
  }
@@ -3720,7 +3714,7 @@ async function main() {
3720
3714
  catch {
3721
3715
  // best effort after response has been sent
3722
3716
  }
3723
- }, 1200);
3717
+ }, 150);
3724
3718
  }));
3725
3719
  app.put("/api/profile", asyncRoute(async (req, res) => {
3726
3720
  const body = req.body;
@@ -3817,9 +3811,8 @@ async function main() {
3817
3811
  }));
3818
3812
  app.get("/api/messages", (req, res) => {
3819
3813
  const limit = Number(req.query.limit ?? 50);
3820
- const offset = Number(req.query.offset ?? 0);
3821
3814
  const agentId = String(req.query.agent_id ?? "").trim();
3822
- sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
3815
+ sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
3823
3816
  });
3824
3817
  app.get("/api/private/state", (_req, res) => {
3825
3818
  sendOk(res, node.getPrivateMessagingState());
@@ -3830,8 +3823,7 @@ async function main() {
3830
3823
  app.get("/api/private/messages", (req, res) => {
3831
3824
  const conversationId = String(req.query.conversation_id ?? "").trim();
3832
3825
  const limit = Number(req.query.limit ?? PRIVATE_MESSAGE_QUERY_LIMIT);
3833
- const offset = Number(req.query.offset ?? 0);
3834
- sendOk(res, node.getPrivateMessages(conversationId, limit, offset));
3826
+ sendOk(res, node.getPrivateMessages(conversationId, limit));
3835
3827
  });
3836
3828
  app.post("/api/private/messages/send", asyncRoute(async (req, res) => {
3837
3829
  const result = await node.sendPrivateMessage({
@@ -3858,9 +3850,8 @@ async function main() {
3858
3850
  });
3859
3851
  app.get("/api/openclaw/bridge/messages", (req, res) => {
3860
3852
  const limit = Number(req.query.limit ?? 50);
3861
- const offset = Number(req.query.offset ?? 0);
3862
3853
  const agentId = String(req.query.agent_id ?? "").trim();
3863
- sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
3854
+ sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
3864
3855
  });
3865
3856
  app.post("/api/openclaw/bridge/message", asyncRoute(async (req, res) => {
3866
3857
  const body = String(req.body?.body || "");
@@ -3976,16 +3967,9 @@ async function main() {
3976
3967
  let html = (0, fs_1.readFileSync)(staticIndexFile, "utf8");
3977
3968
  html = html.replace("</body>", `${renderBootstrapScript(payload)}\n</body>`);
3978
3969
  res.setHeader("Content-Type", "text/html; charset=utf-8");
3979
- res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
3980
3970
  res.send(html);
3981
3971
  });
3982
- app.use(express_1.default.static(staticDir, {
3983
- etag: false,
3984
- lastModified: false,
3985
- setHeaders: (res) => {
3986
- res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
3987
- },
3988
- }));
3972
+ app.use(express_1.default.static(staticDir));
3989
3973
  app.use((error, _req, res, _next) => {
3990
3974
  const message = error instanceof Error ? error.message : "Unknown error";
3991
3975
  sendError(res, 500, "INTERNAL_ERROR", message);
@@ -28,7 +28,6 @@ if (!root) {
28
28
  }
29
29
  root.innerHTML = appTemplate;
30
30
  const APP_UPDATE_SESSION_KEY = 'silicaclaw_pending_updated_version';
31
- const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
32
31
 
33
32
  const i18n = createI18n(TRANSLATIONS);
34
33
  const DEFAULT_LOCALE = i18n.DEFAULT_LOCALE;
@@ -53,9 +52,7 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
53
52
  summary.setAttribute('data-i18n-closed-label', t('labels.show'));
54
53
  summary.setAttribute('data-i18n-open-label', t('labels.hide'));
55
54
  });
56
- setText('.nav-section__label', t('common.workspace'), 0);
57
- setText('.nav-section__label', t('common.messages'), 1);
58
- setText('.nav-section__label', t('common.networkGroup'), 2);
55
+ setText('.nav-section__label', t('common.control'));
59
56
  setText('[data-tab="overview"] .tab-title', t('pageMeta.overview.title'));
60
57
  setText('[data-tab="overview"] .tab-copy', t('labels.overviewTabCopy'));
61
58
  setText('[data-tab="agent"] .tab-title', t('pageMeta.agent.title'));
@@ -123,12 +120,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
123
120
  document.getElementById('privateTargetIdLabel').textContent = t('social.agentId');
124
121
  document.getElementById('privateMessageSendBtn').textContent = t('actions.sendPrivateMessage');
125
122
  document.getElementById('privateRefreshBtn').textContent = t('actions.refreshPrivate');
126
- document.getElementById('socialMessagePrevPageBtn').textContent = t('overview.prevPage');
127
- document.getElementById('socialMessageNextPageBtn').textContent = t('overview.nextPage');
128
- document.getElementById('privateConversationPrevPageBtn').textContent = t('overview.prevPage');
129
- document.getElementById('privateConversationNextPageBtn').textContent = t('overview.nextPage');
130
- document.getElementById('privateMessagePrevPageBtn').textContent = t('overview.prevPage');
131
- document.getElementById('privateMessageNextPageBtn').textContent = t('overview.nextPage');
132
123
  document.getElementById('chatFeedHint').textContent = t('hints.chatFeedHint');
133
124
  document.getElementById('overviewGuideTitle').textContent = t('overview.guideTitle');
134
125
  document.getElementById('overviewGuideBody').textContent = t('overview.guideBody');
@@ -561,7 +552,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
561
552
  let activeTab = 'overview';
562
553
  let logsCache = [];
563
554
  let socialMessagesCache = [];
564
- let socialMessagePage = 1;
565
555
  let logLevelFilter = 'all';
566
556
  let socialTemplate = '';
567
557
  let socialModeDirty = false;
@@ -572,11 +562,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
572
562
  let privateState = null;
573
563
  let privateConversations = [];
574
564
  let privateMessages = [];
575
- let privateConversationPage = 1;
576
- const PRIVATE_CONVERSATION_PAGE_SIZE = 12;
577
- let privateMessagesTotal = 0;
578
- let privateMessagePage = 1;
579
- const PRIVATE_MESSAGE_PAGE_SIZE = 20;
580
565
  let privateTarget = null;
581
566
  let selectedPrivateConversationId = '';
582
567
  let overviewMode = 'lan';
@@ -585,29 +570,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
585
570
  const AGENTS_PAGE_SIZE = 10;
586
571
  const pageMeta = TRANSLATIONS[currentLocale].pageMeta || TRANSLATIONS[DEFAULT_LOCALE].pageMeta;
587
572
 
588
- function loadPagingState() {
589
- try {
590
- const raw = localStorage.getItem(PAGING_STATE_STORAGE_KEY);
591
- if (!raw) return null;
592
- return JSON.parse(raw);
593
- } catch {
594
- return null;
595
- }
596
- }
597
-
598
- function savePagingState() {
599
- try {
600
- localStorage.setItem(PAGING_STATE_STORAGE_KEY, JSON.stringify({
601
- socialMessagePage,
602
- privateConversationPage,
603
- privateMessagePage,
604
- selectedPrivateConversationId,
605
- }));
606
- } catch {
607
- // ignore localStorage failures
608
- }
609
- }
610
-
611
573
  async function api(path, options = {}) {
612
574
  const res = await fetch(path, { headers: { 'Content-Type': 'application/json' }, ...options });
613
575
  const json = await res.json().catch(() => null);
@@ -742,12 +704,8 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
742
704
  : 'Private messaging unavailable';
743
705
  document.getElementById('privateTargetName').value = privateTarget?.display_name || '';
744
706
  document.getElementById('privateTargetAgentId').value = privateTarget?.agent_id || '';
745
- const conversationTotalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
746
- const conversationCurrentPage = Math.min(privateConversationPage, conversationTotalPages);
747
- const conversationOffset = Math.max(0, (conversationCurrentPage - 1) * PRIVATE_CONVERSATION_PAGE_SIZE);
748
- const visibleConversations = privateConversations.slice(conversationOffset, conversationOffset + PRIVATE_CONVERSATION_PAGE_SIZE);
749
- document.getElementById('privateConversationList').innerHTML = visibleConversations.length
750
- ? visibleConversations.map((item) => `
707
+ document.getElementById('privateConversationList').innerHTML = privateConversations.length
708
+ ? privateConversations.map((item) => `
751
709
  <button class="agent-card" type="button" data-private-conversation="${escapeHtml(item.conversation_id)}">
752
710
  <div class="agent-card__avatar-fallback">${escapeHtml(((item.peer_display_name || item.peer_agent_id || '?')[0] || '?').toUpperCase())}</div>
753
711
  <div class="agent-card__main">
@@ -759,12 +717,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
759
717
  </button>
760
718
  `).join('')
761
719
  : `<div class="empty-state">No private conversations yet.</div>`;
762
- document.getElementById('privateConversationPageMeta').textContent = t('overview.pageStatus', {
763
- page: String(conversationCurrentPage),
764
- total: String(conversationTotalPages),
765
- });
766
- document.getElementById('privateConversationPrevPageBtn').disabled = conversationCurrentPage <= 1;
767
- document.getElementById('privateConversationNextPageBtn').disabled = conversationCurrentPage >= conversationTotalPages;
768
720
  document.getElementById('privateMessageList').innerHTML = privateMessages.length
769
721
  ? privateMessages.map((item) => `
770
722
  <div class="log-item">
@@ -779,14 +731,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
779
731
  </div>
780
732
  `).join('')
781
733
  : `<div class="empty-state">No private messages yet.</div>`;
782
- const totalPages = Math.max(1, Math.ceil((privateMessagesTotal || 0) / PRIVATE_MESSAGE_PAGE_SIZE));
783
- const currentPage = Math.min(privateMessagePage, totalPages);
784
- document.getElementById('privateMessagePageMeta').textContent = t('overview.pageStatus', {
785
- page: String(currentPage),
786
- total: String(totalPages),
787
- });
788
- document.getElementById('privateMessagePrevPageBtn').disabled = currentPage <= 1;
789
- document.getElementById('privateMessageNextPageBtn').disabled = currentPage >= totalPages;
790
734
  }
791
735
 
792
736
  async function refreshPrivate() {
@@ -796,8 +740,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
796
740
  ]);
797
741
  privateState = stateRes.data || null;
798
742
  privateConversations = Array.isArray(conversationsRes.data) ? conversationsRes.data : [];
799
- const conversationTotalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
800
- privateConversationPage = Math.min(privateConversationPage, conversationTotalPages);
801
743
  if ((!privateTarget || privateTarget.agent_id === privateState?.agent_id) && privateConversations[0]) {
802
744
  const first = privateConversations[0];
803
745
  privateTarget = {
@@ -816,22 +758,16 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
816
758
  };
817
759
  }
818
760
  if (selectedPrivateConversationId) {
819
- const offset = Math.max(0, (privateMessagePage - 1) * PRIVATE_MESSAGE_PAGE_SIZE);
820
- const messageRes = await api(`/api/private/messages?conversation_id=${encodeURIComponent(selectedPrivateConversationId)}&limit=${PRIVATE_MESSAGE_PAGE_SIZE}&offset=${offset}`);
821
- privateMessages = Array.isArray(messageRes.data?.items) ? messageRes.data.items : [];
822
- privateMessagesTotal = Number(messageRes.data?.total || 0);
761
+ const messageRes = await api(`/api/private/messages?conversation_id=${encodeURIComponent(selectedPrivateConversationId)}&limit=100`);
762
+ privateMessages = Array.isArray(messageRes.data) ? messageRes.data : [];
823
763
  } else {
824
764
  privateMessages = [];
825
- privateMessagesTotal = 0;
826
765
  }
827
766
  renderPrivate();
828
767
  }
829
768
 
830
769
  const renderSocialMessages = socialController.renderSocialMessages;
831
770
  const refreshMessages = socialController.refreshMessages;
832
- const nextSocialMessagesPage = socialController.nextSocialMessagesPage;
833
- const prevSocialMessagesPage = socialController.prevSocialMessagesPage;
834
- const setSocialMessagesPage = socialController.setSocialMessagesPage;
835
771
 
836
772
  const refreshNetwork = networkController.refreshNetwork;
837
773
  const refreshPeers = networkController.refreshPeers;
@@ -934,9 +870,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
934
870
  display_name: String(button.getAttribute('data-private-name') || ''),
935
871
  private_encryption_public_key: String(button.getAttribute('data-private-key') || ''),
936
872
  };
937
- privateConversationPage = 1;
938
- privateMessagePage = 1;
939
- savePagingState();
940
873
  selectedPrivateConversationId = [privateState?.agent_id || '', privateTarget.agent_id].sort().join(':');
941
874
  switchTab('private');
942
875
  });
@@ -953,8 +886,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
953
886
  private_encryption_public_key: selectedConversation.peer_public_key,
954
887
  };
955
888
  }
956
- privateMessagePage = 1;
957
- savePagingState();
958
889
  await refreshPrivate();
959
890
  });
960
891
 
@@ -962,36 +893,6 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
962
893
  await refreshPrivate();
963
894
  });
964
895
 
965
- document.getElementById('privateConversationPrevPageBtn').addEventListener('click', async () => {
966
- if (privateConversationPage <= 1) return;
967
- privateConversationPage -= 1;
968
- savePagingState();
969
- renderPrivate();
970
- });
971
-
972
- document.getElementById('privateConversationNextPageBtn').addEventListener('click', async () => {
973
- const totalPages = Math.max(1, Math.ceil((privateConversations.length || 0) / PRIVATE_CONVERSATION_PAGE_SIZE));
974
- if (privateConversationPage >= totalPages) return;
975
- privateConversationPage += 1;
976
- savePagingState();
977
- renderPrivate();
978
- });
979
-
980
- document.getElementById('privateMessagePrevPageBtn').addEventListener('click', async () => {
981
- if (privateMessagePage <= 1) return;
982
- privateMessagePage -= 1;
983
- savePagingState();
984
- await refreshPrivate();
985
- });
986
-
987
- document.getElementById('privateMessageNextPageBtn').addEventListener('click', async () => {
988
- const totalPages = Math.max(1, Math.ceil((privateMessagesTotal || 0) / PRIVATE_MESSAGE_PAGE_SIZE));
989
- if (privateMessagePage >= totalPages) return;
990
- privateMessagePage += 1;
991
- savePagingState();
992
- await refreshPrivate();
993
- });
994
-
995
896
  document.getElementById('privateMessageSendBtn').addEventListener('click', async () => {
996
897
  const body = String(document.getElementById('privateMessageInput').value || '').trim();
997
898
  if (!privateTarget?.agent_id || !privateTarget?.private_encryption_public_key) {
@@ -1018,37 +919,12 @@ const PAGING_STATE_STORAGE_KEY = 'silicaclaw_ui_paging_state';
1018
919
  });
1019
920
  document.getElementById('privateMessageInput').value = '';
1020
921
  setFeedback('privateFeedback', result.meta?.message || 'Private message sent.');
1021
- privateMessagePage = 1;
1022
- savePagingState();
1023
922
  await refreshPrivate();
1024
923
  } catch (error) {
1025
924
  setFeedback('privateFeedback', error instanceof Error ? error.message : 'Private message failed.', 'error');
1026
925
  }
1027
926
  });
1028
927
 
1029
- document.getElementById('socialMessagePrevPageBtn').addEventListener('click', async () => {
1030
- prevSocialMessagesPage();
1031
- socialMessagePage = Math.max(1, socialMessagePage - 1);
1032
- savePagingState();
1033
- await refreshMessages();
1034
- });
1035
-
1036
- document.getElementById('socialMessageNextPageBtn').addEventListener('click', async () => {
1037
- nextSocialMessagesPage();
1038
- socialMessagePage += 1;
1039
- savePagingState();
1040
- await refreshMessages();
1041
- });
1042
-
1043
- const persistedPagingState = loadPagingState();
1044
- if (persistedPagingState && typeof persistedPagingState === 'object') {
1045
- socialMessagePage = Math.max(1, Number(persistedPagingState.socialMessagePage) || 1);
1046
- privateConversationPage = Math.max(1, Number(persistedPagingState.privateConversationPage) || 1);
1047
- privateMessagePage = Math.max(1, Number(persistedPagingState.privateMessagePage) || 1);
1048
- selectedPrivateConversationId = String(persistedPagingState.selectedPrivateConversationId || '').trim();
1049
- setSocialMessagesPage(socialMessagePage);
1050
- }
1051
-
1052
928
  applyTheme(localStorage.getItem('silicaclaw_theme_mode') || 'dark');
1053
929
  hydrateCachedShell();
1054
930
  document.getElementById('brandUpdateBtn').addEventListener('click', () => {
@@ -26,11 +26,8 @@ export function createSocialController({
26
26
  }) {
27
27
  const SKILLS_SECTION_LIMIT = 4;
28
28
  const SKILLS_DIALOGUE_LIMIT = 1;
29
- const SOCIAL_MESSAGE_PAGE_SIZE = 20;
30
29
  let lastMessagesRenderKey = "";
31
30
  let lastLogsRenderKey = "";
32
- let socialMessagesPage = 1;
33
- let socialMessagesTotal = 0;
34
31
  const sectionRenderCache = new Map();
35
32
  let skillsQuery = "";
36
33
  let skillsFilter = "all";
@@ -168,18 +165,6 @@ export function createSocialController({
168
165
  seconds: String(Math.floor((governance.send_limit?.window_ms || 60000) / 1000)),
169
166
  })}`
170
167
  : t("overview.messageHint");
171
- const totalPages = Math.max(1, Math.ceil((socialMessagesTotal || 0) / SOCIAL_MESSAGE_PAGE_SIZE));
172
- const currentPage = Math.min(socialMessagesPage, totalPages);
173
- const updatePager = () => {
174
- const pageMetaEl = document.getElementById("socialMessagePageMeta");
175
- const prevBtn = document.getElementById("socialMessagePrevPageBtn");
176
- const nextBtn = document.getElementById("socialMessageNextPageBtn");
177
- if (pageMetaEl) {
178
- pageMetaEl.textContent = t("overview.pageStatus", { page: String(currentPage), total: String(totalPages) });
179
- }
180
- if (prevBtn) prevBtn.disabled = currentPage <= 1;
181
- if (nextBtn) nextBtn.disabled = currentPage >= totalPages;
182
- };
183
168
  if (!socialMessagesCache.length) {
184
169
  const nextMeta = t("overview.noMessagesMeta");
185
170
  const nextHtml = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
@@ -188,7 +173,6 @@ export function createSocialController({
188
173
  hintEl.textContent = governanceHint;
189
174
  metaEl.textContent = nextMeta;
190
175
  listEl.innerHTML = nextHtml;
191
- updatePager();
192
176
  lastMessagesRenderKey = renderKey;
193
177
  }
194
178
  return;
@@ -210,6 +194,7 @@ export function createSocialController({
210
194
  })}`
211
195
  : "";
212
196
  const nextMeta = `${baseMeta}${governanceMeta}`;
197
+
213
198
  if (!filteredMessages.length) {
214
199
  const nextHtml = `<div class="empty-state">${t("overview.noMessagesEmpty")}</div>`;
215
200
  const renderKey = JSON.stringify({ hint: governanceHint, meta: nextMeta, html: nextHtml });
@@ -217,7 +202,6 @@ export function createSocialController({
217
202
  hintEl.textContent = governanceHint;
218
203
  metaEl.textContent = nextMeta;
219
204
  listEl.innerHTML = nextHtml;
220
- updatePager();
221
205
  lastMessagesRenderKey = renderKey;
222
206
  }
223
207
  return;
@@ -301,16 +285,13 @@ export function createSocialController({
301
285
  hintEl.textContent = governanceHint;
302
286
  metaEl.textContent = nextMeta;
303
287
  listEl.innerHTML = nextHtml;
304
- updatePager();
305
288
  lastMessagesRenderKey = renderKey;
306
289
  }
307
290
 
308
291
  async function refreshMessages() {
309
- const offset = Math.max(0, (socialMessagesPage - 1) * SOCIAL_MESSAGE_PAGE_SIZE);
310
- const result = await api(`/api/messages?limit=${SOCIAL_MESSAGE_PAGE_SIZE}&offset=${offset}`);
292
+ const result = await api("/api/messages?limit=50");
311
293
  setSocialMessagesCache(Array.isArray(result.data?.items) ? result.data.items : []);
312
294
  setSocialMessageGovernance(result.data?.governance || null);
313
- socialMessagesTotal = Number(result.data?.total || 0);
314
295
  renderSocialMessages();
315
296
  }
316
297
 
@@ -898,14 +879,6 @@ export function createSocialController({
898
879
  refreshSocial,
899
880
  renderLogs,
900
881
  renderSocialMessages,
901
- nextSocialMessagesPage: () => {
902
- const totalPages = Math.max(1, Math.ceil((socialMessagesTotal || 0) / SOCIAL_MESSAGE_PAGE_SIZE));
903
- socialMessagesPage = Math.min(totalPages, socialMessagesPage + 1);
904
- },
905
- prevSocialMessagesPage: () => {
906
- socialMessagesPage = Math.max(1, socialMessagesPage - 1);
907
- },
908
- setSocialMessagesPage: (page) => { socialMessagesPage = Math.max(1, Number(page) || 1); },
909
882
  setSkillsFilter,
910
883
  setSkillsQuery,
911
884
  setLogLevelFilter,
@@ -394,8 +394,8 @@
394
394
  .nav-section {
395
395
  display: grid;
396
396
  align-content: start;
397
- gap: 8px;
398
- margin-bottom: 14px;
397
+ gap: 6px;
398
+ margin-bottom: 10px;
399
399
  }
400
400
  .nav-section:last-child {
401
401
  margin-bottom: 0;
@@ -418,7 +418,6 @@
418
418
  display: grid;
419
419
  align-content: start;
420
420
  gap: 4px;
421
- padding: 2px 0 0;
422
421
  }
423
422
  .nav button {
424
423
  position: relative;
@@ -23,94 +23,84 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
23
23
  <div class="sidebar-shell__body">
24
24
  <nav class="sidebar-nav nav">
25
25
  <section class="nav-section">
26
- <div class="nav-section__label">Workspace</div>
26
+ <div class="nav-section__label">Control</div>
27
27
  <div class="nav-section__items">
28
- <button class="tab nav-item active" data-tab="overview">
29
- <span class="tab-icon" aria-hidden="true">
30
- <svg viewBox="0 0 24 24">
31
- <line x1="12" x2="12" y1="20" y2="10"></line>
32
- <line x1="18" x2="18" y1="20" y2="4"></line>
33
- <line x1="6" x2="6" y1="20" y2="16"></line>
34
- </svg>
35
- </span>
36
- <span class="tab-labels"><span class="tab-title">Overview</span><span class="tab-copy">Agent health, visibility, and next steps</span></span>
37
- </button>
38
- <button class="tab nav-item" data-tab="profile">
39
- <span class="tab-icon" aria-hidden="true">
40
- <svg viewBox="0 0 24 24">
41
- <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
42
- <polyline points="14 2 14 8 20 8"></polyline>
43
- <line x1="16" x2="8" y1="13" y2="13"></line>
44
- <line x1="16" x2="8" y1="17" y2="17"></line>
45
- <line x1="10" x2="8" y1="9" y2="9"></line>
46
- </svg>
47
- </span>
48
- <span class="tab-labels"><span class="tab-title">Profile</span><span class="tab-copy">Public card, visibility, and saved identity</span></span>
49
- </button>
50
- <button class="tab nav-item" data-tab="skills">
51
- <span class="tab-icon" aria-hidden="true">
52
- <svg viewBox="0 0 24 24">
53
- <path d="M12 3l2.5 5 5.5.8-4 3.9.9 5.5-4.9-2.6-4.9 2.6.9-5.5-4-3.9 5.5-.8z"></path>
54
- </svg>
55
- </span>
56
- <span class="tab-labels"><span class="tab-title">Skills</span><span class="tab-copy">Bundled skills and OpenClaw-installed skills</span></span>
57
- </button>
58
- </div>
59
- </section>
60
- <section class="nav-section">
61
- <div class="nav-section__label">Messages</div>
62
- <div class="nav-section__items">
63
- <button class="tab nav-item" data-tab="chat">
64
- <span class="tab-icon" aria-hidden="true">
65
- <svg viewBox="0 0 24 24">
66
- <path d="M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
67
- <path d="M8 9h8"></path>
68
- <path d="M8 13h6"></path>
69
- </svg>
70
- </span>
71
- <span class="tab-labels"><span class="tab-title">Messages</span><span class="tab-copy">Public message composer and observed feed</span></span>
72
- </button>
73
- <button class="tab nav-item" data-tab="private">
74
- <span class="tab-icon" aria-hidden="true">
75
- <svg viewBox="0 0 24 24">
76
- <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
77
- <rect x="4" y="11" width="16" height="10" rx="2"></rect>
78
- <circle cx="12" cy="16" r="1"></circle>
79
- </svg>
80
- </span>
81
- <span class="tab-labels"><span class="tab-title">Private</span><span class="tab-copy">Private messages between visible agents</span></span>
82
- </button>
83
- </div>
84
- </section>
85
- <section class="nav-section">
86
- <div class="nav-section__label">Network</div>
87
- <div class="nav-section__items">
88
- <button class="tab nav-item" data-tab="agent">
89
- <span class="tab-icon" aria-hidden="true">
90
- <svg viewBox="0 0 24 24">
91
- <circle cx="12" cy="8" r="4"></circle>
92
- <path d="M5 20c1.6-3.8 4.2-5.7 7-5.7s5.4 1.9 7 5.7"></path>
93
- </svg>
94
- </span>
95
- <span class="tab-labels"><span class="tab-title">Agents</span><span class="tab-copy">Discovered public agents and live directory</span></span>
96
- </button>
97
- <button class="tab nav-item" data-tab="network">
98
- <span class="tab-icon" aria-hidden="true">
99
- <svg viewBox="0 0 24 24">
100
- <circle cx="12" cy="12" r="2"></circle>
101
- <path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path>
102
- </svg>
103
- </span>
104
- <span class="tab-labels"><span class="tab-title">Network</span><span class="tab-copy">Broadcast controls, peers, and diagnostics</span></span>
105
- </button>
106
- <button class="tab nav-item" data-tab="social">
107
- <span class="tab-icon" aria-hidden="true">
108
- <svg viewBox="0 0 24 24">
109
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
110
- </svg>
111
- </span>
112
- <span class="tab-labels"><span class="tab-title">Social</span><span class="tab-copy">Runtime mode, bridge status, and social.md</span></span>
113
- </button>
28
+ <button class="tab nav-item active" data-tab="overview">
29
+ <span class="tab-icon" aria-hidden="true">
30
+ <svg viewBox="0 0 24 24">
31
+ <line x1="12" x2="12" y1="20" y2="10"></line>
32
+ <line x1="18" x2="18" y1="20" y2="4"></line>
33
+ <line x1="6" x2="6" y1="20" y2="16"></line>
34
+ </svg>
35
+ </span>
36
+ <span class="tab-labels"><span class="tab-title">Overview</span><span class="tab-copy">Agent health, visibility, and next steps</span></span>
37
+ </button>
38
+ <button class="tab nav-item" data-tab="profile">
39
+ <span class="tab-icon" aria-hidden="true">
40
+ <svg viewBox="0 0 24 24">
41
+ <path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"></path>
42
+ <polyline points="14 2 14 8 20 8"></polyline>
43
+ <line x1="16" x2="8" y1="13" y2="13"></line>
44
+ <line x1="16" x2="8" y1="17" y2="17"></line>
45
+ <line x1="10" x2="8" y1="9" y2="9"></line>
46
+ </svg>
47
+ </span>
48
+ <span class="tab-labels"><span class="tab-title">Profile</span><span class="tab-copy">Public card, visibility, and saved identity</span></span>
49
+ </button>
50
+ <button class="tab nav-item" data-tab="chat">
51
+ <span class="tab-icon" aria-hidden="true">
52
+ <svg viewBox="0 0 24 24">
53
+ <path d="M21 15a2 2 0 0 1-2 2H8l-5 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
54
+ <path d="M8 9h8"></path>
55
+ <path d="M8 13h6"></path>
56
+ </svg>
57
+ </span>
58
+ <span class="tab-labels"><span class="tab-title">Messages</span><span class="tab-copy">Public message composer and observed feed</span></span>
59
+ </button>
60
+ <button class="tab nav-item" data-tab="private">
61
+ <span class="tab-icon" aria-hidden="true">
62
+ <svg viewBox="0 0 24 24">
63
+ <path d="M7 11V7a5 5 0 0 1 10 0v4"></path>
64
+ <rect x="4" y="11" width="16" height="10" rx="2"></rect>
65
+ <circle cx="12" cy="16" r="1"></circle>
66
+ </svg>
67
+ </span>
68
+ <span class="tab-labels"><span class="tab-title">Private</span><span class="tab-copy">Private messages between visible agents</span></span>
69
+ </button>
70
+ <button class="tab nav-item" data-tab="network">
71
+ <span class="tab-icon" aria-hidden="true">
72
+ <svg viewBox="0 0 24 24">
73
+ <circle cx="12" cy="12" r="2"></circle>
74
+ <path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14"></path>
75
+ </svg>
76
+ </span>
77
+ <span class="tab-labels"><span class="tab-title">Network</span><span class="tab-copy">Broadcast controls, peers, and diagnostics</span></span>
78
+ </button>
79
+ <button class="tab nav-item" data-tab="social">
80
+ <span class="tab-icon" aria-hidden="true">
81
+ <svg viewBox="0 0 24 24">
82
+ <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
83
+ </svg>
84
+ </span>
85
+ <span class="tab-labels"><span class="tab-title">Social</span><span class="tab-copy">Runtime mode, bridge status, and social.md</span></span>
86
+ </button>
87
+ <button class="tab nav-item" data-tab="skills">
88
+ <span class="tab-icon" aria-hidden="true">
89
+ <svg viewBox="0 0 24 24">
90
+ <path d="M12 3l2.5 5 5.5.8-4 3.9.9 5.5-4.9-2.6-4.9 2.6.9-5.5-4-3.9 5.5-.8z"></path>
91
+ </svg>
92
+ </span>
93
+ <span class="tab-labels"><span class="tab-title">Skills</span><span class="tab-copy">Bundled skills and OpenClaw-installed skills</span></span>
94
+ </button>
95
+ <button class="tab nav-item" data-tab="agent">
96
+ <span class="tab-icon" aria-hidden="true">
97
+ <svg viewBox="0 0 24 24">
98
+ <circle cx="12" cy="8" r="4"></circle>
99
+ <path d="M5 20c1.6-3.8 4.2-5.7 7-5.7s5.4 1.9 7 5.7"></path>
100
+ </svg>
101
+ </span>
102
+ <span class="tab-labels"><span class="tab-title">Agents</span><span class="tab-copy">Discovered public agents and live directory</span></span>
103
+ </button>
114
104
  </div>
115
105
  </section>
116
106
  </nav>
@@ -387,13 +377,6 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
387
377
  </div>
388
378
  </div>
389
379
  <div class="logs" id="socialMessageList"></div>
390
- <div class="agent-list__footer">
391
- <div class="agent-list__page" id="socialMessagePageMeta">Page 1 / 1</div>
392
- <div class="agent-list__pager">
393
- <button class="secondary" type="button" id="socialMessagePrevPageBtn">Prev</button>
394
- <button class="secondary" type="button" id="socialMessageNextPageBtn">Next</button>
395
- </div>
396
- </div>
397
380
  </div>
398
381
  </div>
399
382
  </div>
@@ -423,13 +406,6 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
423
406
  </div>
424
407
  </div>
425
408
  <div class="logs" id="privateConversationList"></div>
426
- <div class="agent-list__footer">
427
- <div class="agent-list__page" id="privateConversationPageMeta">Page 1 / 1</div>
428
- <div class="agent-list__pager">
429
- <button class="secondary" type="button" id="privateConversationPrevPageBtn">Prev</button>
430
- <button class="secondary" type="button" id="privateConversationNextPageBtn">Next</button>
431
- </div>
432
- </div>
433
409
  </div>
434
410
  <div class="card stack">
435
411
  <div class="overview-panel-header">
@@ -455,13 +431,6 @@ export const appTemplate = String.raw`<div class="app" id="appShell">
455
431
  </div>
456
432
  <div id="privateFeedback" class="feedback">Ready.</div>
457
433
  <div class="logs" id="privateMessageList"></div>
458
- <div class="agent-list__footer">
459
- <div class="agent-list__page" id="privateMessagePageMeta">Page 1 / 1</div>
460
- <div class="agent-list__pager">
461
- <button class="secondary" type="button" id="privateMessagePrevPageBtn">Prev</button>
462
- <button class="secondary" type="button" id="privateMessageNextPageBtn">Next</button>
463
- </div>
464
- </div>
465
434
  </div>
466
435
  </div>
467
436
  </div>
@@ -8,9 +8,6 @@ export const TRANSLATIONS = {
8
8
  },
9
9
  common: {
10
10
  control: 'Control',
11
- workspace: 'Workspace',
12
- messages: 'Messages',
13
- networkGroup: 'Network',
14
11
  version: 'Version',
15
12
  localConsole: 'Local Console',
16
13
  subtitle: 'Manage identity, discovery, and broadcasts',
@@ -629,9 +626,6 @@ export const TRANSLATIONS = {
629
626
  },
630
627
  common: {
631
628
  control: '控制',
632
- workspace: '工作区',
633
- messages: '消息',
634
- networkGroup: '网络',
635
629
  version: '版本',
636
630
  localConsole: '本地控制台',
637
631
  subtitle: '管理身份、发现与广播',
@@ -1899,7 +1899,7 @@ export class LocalNodeService {
1899
1899
  return this.identity;
1900
1900
  }
1901
1901
 
1902
- getSocialMessages(limit = 50, options?: { agent_id?: string | null; offset?: number | null }): {
1902
+ getSocialMessages(limit = 50, options?: { agent_id?: string | null }): {
1903
1903
  total: number;
1904
1904
  items: SocialMessageView[];
1905
1905
  governance: {
@@ -1911,7 +1911,6 @@ export class LocalNodeService {
1911
1911
  };
1912
1912
  } {
1913
1913
  const resolvedLimit = Math.max(1, Math.min(200, Number(limit) || 50));
1914
- const resolvedOffset = Math.max(0, Number(options?.offset) || 0);
1915
1914
  this.ensureLocalDirectoryBaseline();
1916
1915
  this.compactCacheInMemory();
1917
1916
  const agentId = String(options?.agent_id || "").trim();
@@ -1920,7 +1919,7 @@ export class LocalNodeService {
1920
1919
  : this.socialMessages;
1921
1920
  return {
1922
1921
  total: filtered.length,
1923
- items: filtered.slice(resolvedOffset, resolvedOffset + resolvedLimit).map((message) => {
1922
+ items: filtered.slice(0, resolvedLimit).map((message) => {
1924
1923
  const profile = this.directory.profiles[message.agent_id];
1925
1924
  const lastSeenAt = this.directory.presence[message.agent_id] ?? 0;
1926
1925
  const observations = this.socialMessageObservations.filter((item) => item.message_id === message.message_id);
@@ -2001,17 +2000,13 @@ export class LocalNodeService {
2001
2000
  return Array.from(conversations.values()).sort((a, b) => (b.last_message_at || 0) - (a.last_message_at || 0));
2002
2001
  }
2003
2002
 
2004
- getPrivateMessages(conversationId: string, limit = PRIVATE_MESSAGE_QUERY_LIMIT, offset = 0): {
2005
- total: number;
2006
- items: PrivateMessageView[];
2007
- } {
2003
+ getPrivateMessages(conversationId: string, limit = PRIVATE_MESSAGE_QUERY_LIMIT): PrivateMessageView[] {
2008
2004
  const normalizedConversationId = String(conversationId || "").trim();
2009
2005
  const resolvedLimit = Math.max(1, Math.min(PRIVATE_MESSAGE_QUERY_LIMIT, Number(limit) || PRIVATE_MESSAGE_QUERY_LIMIT));
2010
- const resolvedOffset = Math.max(0, Number(offset) || 0);
2011
2006
  const receiptsByMessageId = new Map(
2012
2007
  this.privateMessageReceipts.map((receipt) => [receipt.message_id, receipt.status] as const)
2013
2008
  );
2014
- const filtered = this.privateMessages
2009
+ return this.privateMessages
2015
2010
  .filter((message) => {
2016
2011
  if (message.from_agent_id === message.to_agent_id) {
2017
2012
  return false;
@@ -2022,12 +2017,9 @@ export class LocalNodeService {
2022
2017
  }
2023
2018
  return !normalizedConversationId || message.conversation_id === normalizedConversationId;
2024
2019
  })
2025
- .sort((a, b) => a.created_at - b.created_at);
2026
- const total = filtered.length;
2027
- const paged = filtered.slice(Math.max(0, total - resolvedOffset - resolvedLimit), Math.max(0, total - resolvedOffset));
2028
- return {
2029
- total,
2030
- items: paged.map((message) => ({
2020
+ .sort((a, b) => a.created_at - b.created_at)
2021
+ .slice(-resolvedLimit)
2022
+ .map((message) => ({
2031
2023
  message_id: message.message_id,
2032
2024
  conversation_id: message.conversation_id,
2033
2025
  from_agent_id: message.from_agent_id,
@@ -2039,8 +2031,7 @@ export class LocalNodeService {
2039
2031
  receiptsByMessageId.get(message.message_id) ||
2040
2032
  this.privateMessageDeliveryStatusCache.get(message.message_id) ||
2041
2033
  (message.from_agent_id === this.identity?.agent_id ? "fallback-sent" : "sent"),
2042
- })),
2043
- };
2034
+ }));
2044
2035
  }
2045
2036
 
2046
2037
  async sendPrivateMessage(input: {
@@ -2092,7 +2083,7 @@ export class LocalNodeService {
2092
2083
  await this.publish(PRIVATE_MESSAGE_TOPIC, message);
2093
2084
  }
2094
2085
  this.privateMessageDeliveryStatusCache.set(message.message_id, reason as PrivateMessageView["delivery_status"]);
2095
- const view = this.getPrivateMessages(message.conversation_id, PRIVATE_MESSAGE_QUERY_LIMIT, 0).items.find((item) => item.message_id === message.message_id);
2086
+ const view = this.getPrivateMessages(message.conversation_id).find((item) => item.message_id === message.message_id);
2096
2087
  if (view) {
2097
2088
  view.delivery_status = reason as PrivateMessageView["delivery_status"];
2098
2089
  }
@@ -4315,7 +4306,7 @@ export async function main() {
4315
4306
  } catch {
4316
4307
  // best effort after response has been sent
4317
4308
  }
4318
- }, 1200);
4309
+ }, 150);
4319
4310
  })
4320
4311
  );
4321
4312
 
@@ -4457,9 +4448,8 @@ export async function main() {
4457
4448
 
4458
4449
  app.get("/api/messages", (req, res) => {
4459
4450
  const limit = Number(req.query.limit ?? 50);
4460
- const offset = Number(req.query.offset ?? 0);
4461
4451
  const agentId = String(req.query.agent_id ?? "").trim();
4462
- sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
4452
+ sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
4463
4453
  });
4464
4454
 
4465
4455
  app.get("/api/private/state", (_req, res) => {
@@ -4473,8 +4463,7 @@ export async function main() {
4473
4463
  app.get("/api/private/messages", (req, res) => {
4474
4464
  const conversationId = String(req.query.conversation_id ?? "").trim();
4475
4465
  const limit = Number(req.query.limit ?? PRIVATE_MESSAGE_QUERY_LIMIT);
4476
- const offset = Number(req.query.offset ?? 0);
4477
- sendOk(res, node.getPrivateMessages(conversationId, limit, offset));
4466
+ sendOk(res, node.getPrivateMessages(conversationId, limit));
4478
4467
  });
4479
4468
 
4480
4469
  app.post(
@@ -4509,9 +4498,8 @@ export async function main() {
4509
4498
 
4510
4499
  app.get("/api/openclaw/bridge/messages", (req, res) => {
4511
4500
  const limit = Number(req.query.limit ?? 50);
4512
- const offset = Number(req.query.offset ?? 0);
4513
4501
  const agentId = String(req.query.agent_id ?? "").trim();
4514
- sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null, offset }));
4502
+ sendOk(res, node.getSocialMessages(limit, { agent_id: agentId || null }));
4515
4503
  });
4516
4504
 
4517
4505
  app.post(
@@ -4661,17 +4649,10 @@ export async function main() {
4661
4649
  let html = readFileSync(staticIndexFile, "utf8");
4662
4650
  html = html.replace("</body>", `${renderBootstrapScript(payload)}\n</body>`);
4663
4651
  res.setHeader("Content-Type", "text/html; charset=utf-8");
4664
- res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
4665
4652
  res.send(html);
4666
4653
  });
4667
4654
 
4668
- app.use(express.static(staticDir, {
4669
- etag: false,
4670
- lastModified: false,
4671
- setHeaders: (res) => {
4672
- res.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
4673
- },
4674
- }));
4655
+ app.use(express.static(staticDir));
4675
4656
 
4676
4657
  app.use((error: unknown, _req: Request, res: Response, _next: NextFunction) => {
4677
4658
  const message = error instanceof Error ? error.message : "Unknown error";
@@ -1 +1 @@
1
- 2026.3.20-beta.14
1
+ 2026.3.20-beta.15
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "silicaclaw-broadcast",
3
- "version": "2026.3.20-beta.14",
3
+ "version": "2026.3.20-beta.15",
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-14",
3
+ "version": "2026.3.20-15",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1071,7 +1071,9 @@ async function restartAll() {
1071
1071
  environment: baseEnv,
1072
1072
  });
1073
1073
  if (!localConsoleService.changed) {
1074
- restartLaunchAgent(LOCAL_CONSOLE_LABEL, localConsoleService.plistPath);
1074
+ stopLaunchAgent(LOCAL_CONSOLE_LABEL);
1075
+ await drainOwnedListener(LOCAL_CONSOLE_PORT, "local-console", 8000);
1076
+ startLaunchAgent(LOCAL_CONSOLE_LABEL, localConsoleService.plistPath);
1075
1077
  }
1076
1078
 
1077
1079
  if (shouldAutoStartSignaling) {
@@ -1087,7 +1089,9 @@ async function restartAll() {
1087
1089
  },
1088
1090
  });
1089
1091
  if (!signalingService.changed) {
1090
- restartLaunchAgent(SIGNALING_LABEL, signalingService.plistPath);
1092
+ stopLaunchAgent(SIGNALING_LABEL);
1093
+ await drainOwnedListener(4510, "signaling", 5000);
1094
+ startLaunchAgent(SIGNALING_LABEL, signalingService.plistPath);
1091
1095
  }
1092
1096
  } else {
1093
1097
  uninstallLaunchAgent(SIGNALING_LABEL);