@bolloon/bolloon-agent 0.3.26 → 0.3.28

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.
@@ -1689,7 +1689,72 @@
1689
1689
  v3GlobalEventSource.onmessage = (e) => {
1690
1690
  try {
1691
1691
  const msg = JSON.parse(e.data);
1692
- if (msg.type === "remote-chat-reply") {
1692
+ if (msg.type === "remote-chat-sent") {
1693
+ console.log("[v3] \u6536\u5230 remote-chat-sent:", msg.channelId, "|", String(msg.text || "").slice(0, 30));
1694
+ try {
1695
+ const pk = msg.fromPublicKey;
1696
+ const cid = msg.channelId;
1697
+ if (pk && cid && msg.text) {
1698
+ const key = `bolloon.rcmCache.${pk}.${cid}`;
1699
+ let arr = [];
1700
+ try {
1701
+ const raw = localStorage.getItem(key);
1702
+ if (raw) arr = JSON.parse(raw);
1703
+ } catch {
1704
+ arr = [];
1705
+ }
1706
+ if (!Array.isArray(arr)) arr = [];
1707
+ const entry = { type: "user", content: msg.text, timestamp: (/* @__PURE__ */ new Date()).toISOString(), source: "local-sent" };
1708
+ const dup = arr.some((m) => m.type === "user" && m.content === entry.content && m.source === "local-sent");
1709
+ if (!dup) {
1710
+ arr.push(entry);
1711
+ try {
1712
+ localStorage.setItem(key, JSON.stringify(arr.slice(-200)));
1713
+ } catch {
1714
+ }
1715
+ }
1716
+ }
1717
+ } catch {
1718
+ }
1719
+ const log = document.getElementById("rcm-log");
1720
+ if (!log) return;
1721
+ const modal = document.getElementById("remote-chat-modal");
1722
+ const openChannelId = modal ? modal.dataset.channelId : null;
1723
+ if (openChannelId && openChannelId !== msg.channelId) return;
1724
+ const prefix = `\u{1F464} \u6211 \u2192 \u8FDC\u7AEF${msg.peerName ? ` ${msg.peerName}` : ""}
1725
+
1726
+ `;
1727
+ addMessage2(prefix + (msg.text || ""), "user", false, log);
1728
+ if (msg.queued) {
1729
+ const qEl = document.createElement("div");
1730
+ qEl.className = "remote-chat-sysmsg remote-chat-sysmsg-info";
1731
+ qEl.textContent = "\u{1F4E4} \u5BF9\u65B9\u4E0D\u5728\u7EBF, \u6D88\u606F\u5DF2\u5165\u961F, \u4E0A\u7EBF\u540E\u81EA\u52A8\u9001\u8FBE";
1732
+ log.appendChild(qEl);
1733
+ }
1734
+ log.scrollTop = log.scrollHeight;
1735
+ } else if (msg.type === "remote-chat-step") {
1736
+ const log = document.getElementById("rcm-log");
1737
+ if (!log) return;
1738
+ const modal = document.getElementById("remote-chat-modal");
1739
+ const openChannelId = modal ? modal.dataset.channelId : null;
1740
+ if (openChannelId && openChannelId !== msg.channelId) return;
1741
+ let liveEl = document.getElementById("rcm-thinking-live-local");
1742
+ if (!liveEl) {
1743
+ liveEl = document.createElement("div");
1744
+ liveEl.id = "rcm-thinking-live-local";
1745
+ liveEl.className = "remote-chat-sysmsg remote-chat-sysmsg-info";
1746
+ log.appendChild(liveEl);
1747
+ }
1748
+ if (msg.stepType === "step_start") {
1749
+ liveEl.textContent = `\u{1F527} \u672C\u5730\u667A\u80FD\u4F53\u6B63\u5728\u8C03\u7528\u5DE5\u5177: ${msg.tool || "..."}`;
1750
+ } else if (msg.stepType === "step_done") {
1751
+ liveEl.textContent = `\u2705 \u672C\u5730\u5DE5\u5177\u8C03\u7528\u5B8C\u6210: ${msg.tool || ""}`;
1752
+ } else if (msg.stepType === "step_error") {
1753
+ liveEl.textContent = `\u274C \u672C\u5730\u5DE5\u5177\u8C03\u7528\u5931\u8D25: ${msg.tool || ""}`;
1754
+ liveEl.className = "remote-chat-sysmsg remote-chat-sysmsg-error";
1755
+ }
1756
+ log.scrollTop = log.scrollHeight;
1757
+ } else if (msg.type === "remote-chat-reply") {
1693
1758
  const log = document.getElementById("rcm-log");
1694
1759
  const thinkingEl = document.getElementById("rcm-thinking");
1695
1760
  if (thinkingEl) thinkingEl.style.display = "none";
@@ -1793,15 +1858,25 @@
1793
1858
  }
1794
1859
  }
1795
1860
  } else if (msg.type === "cross-mention-received") {
1861
+ const log = document.getElementById("rcm-log");
1862
+ const rcmModal = document.getElementById("remote-chat-modal");
1863
+ const rcmOpenId = rcmModal ? rcmModal.dataset.channelId : null;
1864
+ if (log && (!msg.targetChannelId || !rcmOpenId || rcmOpenId === msg.targetChannelId)) {
1865
+ const fromTxt = msg.source === "ai-mention-remote" ? `\u8FDC\u7AEF\u667A\u80FD\u4F53 ${msg.originChannelName ? `(${msg.originChannelName})` : ""}` : `${msg.originChannelName} (\u672C\u5730)`;
1866
+ addMessage2(`\u{1F4E1} ${fromTxt}
1867
+
1868
+ ${msg.text || ""}`, "ai", false, log);
1869
+ log.scrollTop = log.scrollHeight;
1870
+ }
1796
1871
  const allModals = document.querySelectorAll('.rcm-mention-toast, [id^="rcm-log"]');
1797
- for (const log of allModals) {
1798
- if (!log.id) continue;
1872
+ for (const logEl of allModals) {
1873
+ if (!logEl.id) continue;
1799
1874
  const toast = document.createElement("div");
1800
1875
  toast.style.cssText = "margin:6px 0;padding:8px 10px;background:#fce7f3;border-left:3px solid #ec4899;border-radius:4px;font-size:12px;color:#831843;";
1801
1876
  const fromTxt = msg.source === "ai-mention-remote" ? `\u8FDC\u7AEF\u8282\u70B9 ${(msg.fromPublicKey || "").substring(0, 8)}\u2026 \u7684 ${msg.originChannelName}` : `${msg.originChannelName} (\u672C\u5730)`;
1802
1877
  toast.innerHTML = `\u{1F4E1} <b>${fromTxt}</b> @-mention \u2192 \u5F53\u524D channel: <i>${escapeHtml2((msg.text || "").slice(0, 100))}</i>${msg.text && msg.text.length > 100 ? "\u2026" : ""}`;
1803
- log.appendChild(toast);
1804
- log.scrollTop = log.scrollHeight;
1878
+ logEl.appendChild(toast);
1879
+ logEl.scrollTop = logEl.scrollHeight;
1805
1880
  }
1806
1881
  } else if (msg.type === "remote-channel-update") {
1807
1882
  const peerId = msg.peerId;
@@ -4971,7 +5046,7 @@ ${data.error || "channel not found"}`, "error");
4971
5046
  function openRemoteChannelChat(peerPublicKey, channelId, channelName) {
4972
5047
  document.getElementById("remote-chat-modal")?.remove();
4973
5048
  const html = `
4974
- <div id="remote-chat-modal" class="remote-chat-overlay">
5049
+ <div id="remote-chat-modal" class="remote-chat-overlay" data-channel-id="${escapeHtml2(channelId)}" data-peer-id="${escapeHtml2(peerPublicKey)}">
4975
5050
  <div class="remote-chat-shell">
4976
5051
  <div class="remote-chat-header">
4977
5052
  <div style="flex:1;min-width:0;">
@@ -5093,7 +5168,11 @@ ${data.error || "channel not found"}`, "error");
5093
5168
  const type = m.type === "user" ? "user" : "ai";
5094
5169
  let prefix = "";
5095
5170
  if (m.type === "user") {
5096
- if (m.source === "remote") {
5171
+ if (m.source === "local-sent") {
5172
+ prefix = `\u{1F464} \u6211 \u2192 \u8FDC\u7AEF
5173
+
5174
+ `;
5175
+ } else if (m.source === "remote") {
5097
5176
  prefix = `\u{1F310} \u8FDC\u7AEF\u8BBF\u5BA2${m.fromPublicKey ? " (" + m.fromPublicKey.substring(0, 8) + "\u2026)" : ""}
5098
5177
 
5099
5178
  `;
@@ -5103,9 +5182,19 @@ ${data.error || "channel not found"}`, "error");
5103
5182
  `;
5104
5183
  }
5105
5184
  } else {
5106
- prefix = `\u{1F916} A \u7684 LLM
5185
+ if (m.source === "remote-reply") {
5186
+ prefix = `\u{1F916} \u8FDC\u7AEF\u56DE\u590D
5187
+
5188
+ `;
5189
+ } else if (m.source === "ai-mention-remote") {
5190
+ prefix = `\u{1F4E1} \u8FDC\u7AEF\u667A\u80FD\u4F53 ${m.originChannelName ? `(${m.originChannelName})` : ""}
5107
5191
 
5108
5192
  `;
5193
+ } else {
5194
+ prefix = `\u{1F916} A \u7684 LLM
5195
+
5196
+ `;
5197
+ }
5109
5198
  }
5110
5199
  addMessage2(prefix + (m.content || ""), type, false, log);
5111
5200
  }
@@ -5123,12 +5212,33 @@ ${data.error || "channel not found"}`, "error");
5123
5212
  writeRcmCache(cacheMsgs);
5124
5213
  } catch {
5125
5214
  }
5215
+ try {
5216
+ const cachedAll = readRcmCache();
5217
+ const localSent = cachedAll.filter((m) => m.source === "local-sent");
5218
+ if (localSent.length > 0) {
5219
+ const remoteContents = new Set(msgs.map((m) => m.content));
5220
+ for (const m of localSent) {
5221
+ if (remoteContents.has(m.content)) continue;
5222
+ const dup = Array.from(log.querySelectorAll(".message-user")).some(
5223
+ (el) => (el.textContent || "").includes(m.content)
5224
+ );
5225
+ if (dup) continue;
5226
+ addMessage2(`\u{1F464} \u6211 \u2192 \u8FDC\u7AEF
5227
+
5228
+ ${m.content}`, "user", false, log);
5229
+ }
5230
+ setTimeout(() => {
5231
+ log.scrollTop = log.scrollHeight;
5232
+ }, 50);
5233
+ }
5234
+ } catch {
5235
+ }
5126
5236
  }
5127
5237
  const doSend = async () => {
5128
5238
  const text = inputEl.value.trim();
5129
5239
  if (!text) return;
5130
5240
  append(text, "user");
5131
- cacheRemoteMessage(peerPublicKey, channelId, { type: "user", content: text, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
5241
+ cacheRemoteMessage(peerPublicKey, channelId, { type: "user", content: text, timestamp: (/* @__PURE__ */ new Date()).toISOString(), source: "local-sent" });
5132
5242
  inputEl.value = "";
5133
5243
  sendBtn2.disabled = true;
5134
5244
  sendBtn2.textContent = "...";
@@ -24,13 +24,20 @@ export function getLastChannelsWriteAt() {
24
24
  }
25
25
  /** 对 channels 执行原子化的 read-modify-write, 自带互斥锁 */
26
26
  export async function updateChannels(fn) {
27
- channelsLock = channelsLock.then(async () => {
27
+ const run = channelsLock.then(async () => {
28
28
  const chs = await rawLoadChannels();
29
29
  const result = fn(chs);
30
30
  await rawSaveChannels(result);
31
31
  return result;
32
32
  });
33
- return channelsLock;
33
+ // 2026-08-02 fix: 失败不毒化锁链 — 之前 channelsLock = channelsLock.then(...),
34
+ // 某次 fn 抛错 → 整个链变 rejected → 之后所有 updateChannels 直接 reject,
35
+ // fn 不再执行 → UI 创建 channel 偶发"不落盘" (静默丢失).
36
+ channelsLock = run.catch((e) => {
37
+ console.error('[updateChannels] 失败 (已隔离, 不影响后续操作):', e?.message || e);
38
+ return undefined;
39
+ });
40
+ return run;
34
41
  }
35
42
  async function rawLoadChannels() {
36
43
  try {