@bolloon/bolloon-agent 0.6.4 → 0.6.6

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.
Files changed (173) hide show
  1. package/dist/agents/chain/chain-settlement.js +0 -1
  2. package/dist/agents/chat-segmenter.js +4 -16
  3. package/dist/agents/contacts/human-directory.js +155 -0
  4. package/dist/agents/contacts/tools.js +79 -0
  5. package/dist/agents/experience-review.js +0 -3
  6. package/dist/agents/goal-flywheel/goal-change.js +3 -4
  7. package/dist/agents/goal-resume.js +0 -4
  8. package/dist/agents/intent-classifier.js +1 -1
  9. package/dist/agents/iteration-budget.js +12 -0
  10. package/dist/agents/knowledge-organizer.js +0 -2
  11. package/dist/agents/lesson-to-skill.js +0 -1
  12. package/dist/agents/loop-review.js +2 -2
  13. package/dist/agents/memory-store.js +359 -0
  14. package/dist/agents/opportunity-inbox.js +147 -0
  15. package/dist/agents/opportunity-match.js +48 -7
  16. package/dist/agents/opportunity-web-search.js +222 -0
  17. package/dist/agents/p2p-chat-tools.js +1 -2
  18. package/dist/agents/parse-tool-call.js +9 -22
  19. package/dist/agents/pi-harness.js +1 -2
  20. package/dist/agents/pi-sdk-tools.js +180 -5
  21. package/dist/agents/pi-sdk.js +143 -9
  22. package/dist/agents/process-runner.js +28 -9
  23. package/dist/agents/react-loop.js +9 -17
  24. package/dist/agents/runner-resolver.js +4 -3
  25. package/dist/agents/skill-loader.js +22 -15
  26. package/dist/agents/skills-manager.js +1 -25
  27. package/dist/agents/workflow-pivot-loop.js +152 -61
  28. package/dist/agents/world-watcher.js +30 -1
  29. package/dist/bootstrap/context-collector.js +2 -4
  30. package/dist/bootstrap/context-hierarchy.js +13 -1
  31. package/dist/cli/ink-app.js +44 -17
  32. package/dist/cli/intent-command.js +22 -12
  33. package/dist/cli/loading-tui.js +8 -2
  34. package/dist/cli/log-gate.js +40 -8
  35. package/dist/cli/mention-data.js +1 -0
  36. package/dist/cli/reply-hygiene.js +22 -0
  37. package/dist/cli/trace-line.js +61 -2
  38. package/dist/electron-build/agents/agent-gateway.js.map +1 -1
  39. package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -1
  40. package/dist/electron-build/agents/chain/chain-settlement.js +0 -1
  41. package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -1
  42. package/dist/electron-build/agents/contacts/chain.js.map +1 -1
  43. package/dist/electron-build/agents/contacts/human-directory.js +204 -0
  44. package/dist/electron-build/agents/contacts/human-directory.js.map +1 -0
  45. package/dist/electron-build/agents/contacts/policy.js.map +1 -1
  46. package/dist/electron-build/agents/contacts/tools.js +112 -0
  47. package/dist/electron-build/agents/contacts/tools.js.map +1 -1
  48. package/dist/electron-build/agents/experience-review.js +0 -3
  49. package/dist/electron-build/agents/experience-review.js.map +1 -1
  50. package/dist/electron-build/agents/external-events.js.map +1 -1
  51. package/dist/electron-build/agents/goal-flywheel/goal-change.js +3 -4
  52. package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -1
  53. package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -1
  54. package/dist/electron-build/agents/goal-resume.js +0 -4
  55. package/dist/electron-build/agents/goal-resume.js.map +1 -1
  56. package/dist/electron-build/agents/intent-classifier.js +1 -1
  57. package/dist/electron-build/agents/lesson-to-skill.js +0 -1
  58. package/dist/electron-build/agents/lesson-to-skill.js.map +1 -1
  59. package/dist/electron-build/agents/loop-review.js +2 -2
  60. package/dist/electron-build/agents/loop-review.js.map +1 -1
  61. package/dist/electron-build/agents/memory-recall.js.map +1 -1
  62. package/dist/electron-build/agents/memory-store.js +404 -0
  63. package/dist/electron-build/agents/memory-store.js.map +1 -0
  64. package/dist/electron-build/agents/parse-tool-call.js +9 -22
  65. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  66. package/dist/electron-build/agents/pi-harness.js +1 -2
  67. package/dist/electron-build/agents/pi-harness.js.map +1 -1
  68. package/dist/electron-build/agents/pi-sdk-tools.js +180 -5
  69. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  70. package/dist/electron-build/agents/pi-sdk.js +143 -9
  71. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  72. package/dist/electron-build/agents/pre-tool-validator.js.map +1 -1
  73. package/dist/electron-build/agents/process-runner.js +28 -9
  74. package/dist/electron-build/agents/process-runner.js.map +1 -1
  75. package/dist/electron-build/agents/runner-resolver.js +4 -3
  76. package/dist/electron-build/agents/runner-resolver.js.map +1 -1
  77. package/dist/electron-build/agents/skill-loader.js +22 -16
  78. package/dist/electron-build/agents/skill-loader.js.map +1 -1
  79. package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -1
  80. package/dist/electron-build/agents/skills-manager.js +0 -24
  81. package/dist/electron-build/agents/skills-manager.js.map +1 -1
  82. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  83. package/dist/electron-build/agents/workflow-engine.js.map +1 -1
  84. package/dist/electron-build/agents/workflow-pivot-loop.js +155 -62
  85. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  86. package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -1
  87. package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -1
  88. package/dist/electron-build/bootstrap/context-collector.js +2 -4
  89. package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
  90. package/dist/electron-build/bootstrap/context-hierarchy.js +13 -1
  91. package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -1
  92. package/dist/electron-build/electron-preload.js.map +1 -1
  93. package/dist/electron-build/external-engines/delegate.js.map +1 -1
  94. package/dist/electron-build/kernel/channel-actor.js +31 -2
  95. package/dist/electron-build/kernel/channel-actor.js.map +1 -1
  96. package/dist/electron-build/kernel/model-runtime.js +8 -1
  97. package/dist/electron-build/kernel/model-runtime.js.map +1 -1
  98. package/dist/electron-build/llm/config-store.js +20 -0
  99. package/dist/electron-build/llm/config-store.js.map +1 -1
  100. package/dist/electron-build/llm/embedding-adapter.js +75 -0
  101. package/dist/electron-build/llm/embedding-adapter.js.map +1 -0
  102. package/dist/electron-build/llm/pi-ai.js +96 -35
  103. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  104. package/dist/electron-build/network/agent-network.js.map +1 -1
  105. package/dist/electron-build/orbitdb/memory-sync.js +76 -0
  106. package/dist/electron-build/orbitdb/memory-sync.js.map +1 -0
  107. package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -1
  108. package/dist/electron-build/security/tool-gate.js +2 -0
  109. package/dist/electron-build/security/tool-gate.js.map +1 -1
  110. package/dist/electron-build/setup/setup-store.js +34 -6
  111. package/dist/electron-build/setup/setup-store.js.map +1 -1
  112. package/dist/electron-build/social/heartbeat.js +18 -5
  113. package/dist/electron-build/social/heartbeat.js.map +1 -1
  114. package/dist/electron-build/utils/update-manager.js.map +1 -1
  115. package/dist/index.js +250 -12
  116. package/dist/ios/bolloon-web.json +3 -3
  117. package/dist/ios/client.js +150 -113
  118. package/dist/ios/icons/icon-1400x1400.png +0 -0
  119. package/dist/ios/icons/icon-1400x1400.webp +0 -0
  120. package/dist/ios/index.html +106 -107
  121. package/dist/ios/mobile-agent.js +346 -45
  122. package/dist/ios/mobile-core.js +7303 -4590
  123. package/dist/ios/mobile-harness.js +1004 -0
  124. package/dist/ios/mobile-orbitdb.js +194 -0
  125. package/dist/ios/mobile-persona.js +80 -0
  126. package/dist/ios/mobile-privacy.js +2 -3
  127. package/dist/ios/mobile.css +286 -21
  128. package/dist/ios/mobile.html +106 -107
  129. package/dist/ios/mobile.js +300 -132
  130. package/dist/ios/routes-world.js +58 -1
  131. package/dist/ios/server.js +3 -1
  132. package/dist/ios/style.css +49 -2
  133. package/dist/ios/sw.js +1 -1
  134. package/dist/ios/ui/message-renderer.js +4 -18
  135. package/dist/ios/ui/step-timeline.js +14 -1
  136. package/dist/kernel/channel-actor.js +30 -2
  137. package/dist/kernel/gate-scan.js +1 -1
  138. package/dist/kernel/index.js +33 -0
  139. package/dist/kernel/model-runtime.js +8 -1
  140. package/dist/kernel/plan.js +3 -3
  141. package/dist/kernel/roster.js +3 -3
  142. package/dist/llm/config-store.js +20 -0
  143. package/dist/llm/embedding-adapter.js +69 -0
  144. package/dist/llm/pi-ai.js +94 -34
  145. package/dist/orbitdb/memory-sync.js +70 -0
  146. package/dist/pi-ecosystem-judgment/adaptive-scan.js +1 -1
  147. package/dist/security/tool-gate.js +2 -0
  148. package/dist/setup/setup-store.js +34 -6
  149. package/dist/social/heartbeat.js +18 -5
  150. package/dist/web/bolloon-web.json +3 -3
  151. package/dist/web/client.js +150 -113
  152. package/dist/web/icons/icon-1400x1400.png +0 -0
  153. package/dist/web/icons/icon-1400x1400.webp +0 -0
  154. package/dist/web/index.html +52 -59
  155. package/dist/web/mobile-agent.js +351 -45
  156. package/dist/web/mobile-contacts.js +0 -4
  157. package/dist/web/mobile-core.js +7430 -4590
  158. package/dist/web/mobile-harness.js +1004 -0
  159. package/dist/web/mobile-orbitdb.js +194 -0
  160. package/dist/web/mobile-persona.js +80 -0
  161. package/dist/web/mobile-privacy.js +2 -3
  162. package/dist/web/mobile.css +286 -21
  163. package/dist/web/mobile.html +121 -107
  164. package/dist/web/mobile.js +409 -136
  165. package/dist/web/routes-contacts.js +94 -1
  166. package/dist/web/routes-world.js +58 -1
  167. package/dist/web/server.js +4 -2
  168. package/dist/web/style.css +49 -2
  169. package/dist/web/sw.js +1 -1
  170. package/dist/web/ui/message-renderer.js +4 -18
  171. package/dist/web/ui/step-timeline.js +14 -1
  172. package/package.json +3 -2
  173. package/scripts/build-web.ts +16 -1
@@ -33,6 +33,15 @@
33
33
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
34
 
35
35
  // src/web/ui/step-timeline.ts
36
+ function formatDuration(ms) {
37
+ if (!Number.isFinite(ms) || ms < 0) return "(\u8017\u65F6\u672A\u77E5)";
38
+ if (ms < 1e3) return `${ms}ms`;
39
+ const s = ms / 1e3;
40
+ if (s < 60) return `${s.toFixed(1)}s`;
41
+ const m = Math.floor(s / 60);
42
+ const rem = Math.round(s % 60);
43
+ return `${m}m${rem}s`;
44
+ }
36
45
  function readExpanded(channelId) {
37
46
  if (!channelId) return false;
38
47
  try {
@@ -152,7 +161,10 @@
152
161
  }
153
162
  node.setAttribute("data-status", step.status);
154
163
  const label = node.querySelector(".step-timeline-label");
155
- if (label) label.textContent = step.name;
164
+ if (label) {
165
+ const dur = step.durationMs !== void 0 && step.status !== "active" ? ` \xB7 ${formatDuration(step.durationMs)}` : "";
166
+ label.textContent = step.name + dur;
167
+ }
156
168
  const argsEl = node.querySelector(".step-timeline-args");
157
169
  if (argsEl) {
158
170
  const argStr = step.args && Object.keys(step.args).length > 0 ? JSON.stringify(step.args).slice(0, 60) : "";
@@ -296,6 +308,7 @@
296
308
  steps[targetIdx].status = data.success === false ? "error" : "done";
297
309
  if (data.output) steps[targetIdx].output = data.output;
298
310
  if (data.error) steps[targetIdx].error = data.error;
311
+ if (data.durationMs !== void 0) steps[targetIdx].durationMs = data.durationMs;
299
312
  } else {
300
313
  steps[targetIdx].status = "error";
301
314
  if (data.error) steps[targetIdx].error = data.error;
@@ -595,21 +608,10 @@
595
608
  if (remaining.trim()) {
596
609
  remaining = filterFillerText(remaining);
597
610
  }
598
- const finalIdx = remaining.indexOf("<final gen>");
599
- let beforeFinalText = remaining;
600
- if (finalIdx >= 0) {
601
- beforeFinalText = remaining.substring(0, finalIdx);
602
- }
603
- const textContent = beforeFinalText.trim();
611
+ const textContent = remaining.trim();
604
612
  if (textContent) {
605
613
  segments.push({ type: "text", content: textContent });
606
614
  }
607
- if (finalIdx >= 0) {
608
- const afterFinal = remaining.substring(finalIdx + "<final gen>".length).trim();
609
- if (afterFinal) {
610
- segments.push({ type: "final", content: afterFinal });
611
- }
612
- }
613
615
  return segments;
614
616
  }
615
617
  function extractAndPush(type, text, out, re) {
@@ -879,11 +881,7 @@
879
881
  div.className = `message message-${type}`;
880
882
  let cleanContent = content;
881
883
  if (type === "ai") {
882
- cleanContent = cleanContent.replace(/<think>[\s\S]*?<\/think>/g, "");
883
- const finalGenIdx = cleanContent.indexOf("<final gen>");
884
- if (finalGenIdx >= 0) {
885
- cleanContent = cleanContent.substring(0, finalGenIdx).trim();
886
- }
884
+ cleanContent = cleanContent.replace(/ thinking[\s\S]*?<\/think>/g, "");
887
885
  }
888
886
  const knownToolNames2 = ctx && ctx.knownToolNames || /* @__PURE__ */ new Set();
889
887
  const segments = segmentChatReply(cleanContent, { knownToolNames: knownToolNames2 });
@@ -1207,7 +1205,8 @@
1207
1205
  args: data.args,
1208
1206
  success: data.success,
1209
1207
  output: data.output,
1210
- error: data.error
1208
+ error: data.error,
1209
+ durationMs: data.durationMs
1211
1210
  });
1212
1211
  }
1213
1212
  function flushStepEventBuffer(channelId, ctx) {
@@ -1547,6 +1546,8 @@
1547
1546
  var renderLoopStatusBar2 = (...args) => _getLS().renderLoopStatusBar?.(...args);
1548
1547
  var hideLoopStatusBar2 = (...args) => _getLS().hideLoopStatusBar?.(...args);
1549
1548
  var sidebarToggle = document.getElementById("sidebar-toggle");
1549
+ var sidebarCollapsedBar = document.getElementById("sidebar-collapsed-bar");
1550
+ var sidebarExpandBtn = document.getElementById("sidebar-expand-btn");
1550
1551
  var themeToggle = document.getElementById("theme-toggle");
1551
1552
  var channelList = document.getElementById("channel-list");
1552
1553
  var newChannelBtn = document.getElementById("new-channel-btn");
@@ -1720,13 +1721,16 @@
1720
1721
  isSidebarCollapsed = !isSidebarCollapsed;
1721
1722
  if (isSidebarCollapsed) {
1722
1723
  sidebar.classList.add("collapsed");
1724
+ if (sidebarCollapsedBar) sidebarCollapsedBar.classList.remove("hidden");
1723
1725
  } else {
1724
1726
  sidebar.classList.remove("collapsed");
1727
+ if (sidebarCollapsedBar) sidebarCollapsedBar.classList.add("hidden");
1725
1728
  }
1726
1729
  }
1727
1730
  function expandSidebar() {
1728
1731
  isSidebarCollapsed = false;
1729
1732
  sidebar.classList.remove("collapsed");
1733
+ if (sidebarCollapsedBar) sidebarCollapsedBar.classList.add("hidden");
1730
1734
  }
1731
1735
  async function loadChannels() {
1732
1736
  try {
@@ -1736,7 +1740,6 @@
1736
1740
  const text = await res.text().catch(() => "");
1737
1741
  if (!text.trim().startsWith("[") && !text.trim().startsWith("{")) {
1738
1742
  console.warn("[\u52A0\u8F7D\u9891\u9053] \u68C0\u6D4B\u5230 IPFS \u9759\u6001\u6A21\u5F0F (\u65E0\u540E\u7AEF server), \u529F\u80FD\u53D7\u9650");
1739
- showStaticModeNotice();
1740
1743
  return;
1741
1744
  }
1742
1745
  }
@@ -1762,17 +1765,6 @@
1762
1765
  console.error("[\u52A0\u8F7D\u9891\u9053] \u5931\u8D25:", err);
1763
1766
  }
1764
1767
  }
1765
- function showStaticModeNotice() {
1766
- if (document.getElementById("ipfs-static-notice")) return;
1767
- const notice = document.createElement("div");
1768
- notice.id = "ipfs-static-notice";
1769
- notice.style.cssText = "position:fixed;bottom:60px;left:50%;transform:translateX(-50%);z-index:9999;background:#1a1a18;border:1px solid #c4d640;color:#d8d8c8;padding:10px 16px;border-radius:8px;font-size:12px;box-shadow:0 4px 20px rgba(0,0,0,.5);max-width:560px;text-align:center;";
1770
- notice.innerHTML = `\u{1F4E1} <b style="color:#c4d640">IPFS \u9759\u6001\u6A21\u5F0F</b> \u2014 \u6B64\u9875\u9762\u901A\u8FC7 IPNS \u4ECE\u53BB\u4E2D\u5FC3\u5316\u7F51\u7EDC\u52A0\u8F7D.<br>\u5B8C\u6574\u529F\u80FD (\u5BF9\u8BDD/\u5DE5\u5177/\u5224\u65AD\u529B) \u9700\u8FDE\u63A5\u672C\u5730 Bolloon server: <code style="color:#c4d640">bolloon --web</code>`;
1771
- document.body.appendChild(notice);
1772
- setTimeout(() => {
1773
- notice.remove();
1774
- }, 15e3);
1775
- }
1776
1768
  var gwGroups = [];
1777
1769
  var gwNetworks = [];
1778
1770
  var gwActiveChat = null;
@@ -3111,6 +3103,8 @@ ${data.error || "channel not found"}`, "error");
3111
3103
  }, 1e4);
3112
3104
  eventSource.onerror = () => {
3113
3105
  console.warn("[SSE] \u9519\u8BEF, \u6D4F\u89C8\u5668\u81EA\u52A8\u91CD\u8FDE\u4E2D:", targetChannelId, "readyState=", eventSource.readyState);
3106
+ hideLoopStatusBar2();
3107
+ setSendMode("idle");
3114
3108
  if (eventSource.readyState === EventSource.CLOSED) {
3115
3109
  clearInterval(heartbeatTimer);
3116
3110
  eventSources.delete(targetChannelId);
@@ -3230,6 +3224,13 @@ ${data.error || "channel not found"}`, "error");
3230
3224
  async function sendMessage() {
3231
3225
  const text = input.value.trim();
3232
3226
  if (!text) return;
3227
+ if (text === "/world" || text === "/chat") {
3228
+ const panel = text === "/world" ? "world" : "chat";
3229
+ if (typeof switchPanel === "function") switchPanel(panel);
3230
+ input.value = "";
3231
+ if (typeof showSimpleToast === "function") showSimpleToast(panel === "world" ? "\u{1F30D} \u5DF2\u5207\u6362\u5230\u4E16\u754C\u6A21\u5F0F" : "\u{1F4AC} \u5DF2\u5207\u6362\u5230\u804A\u5929\u6A21\u5F0F");
3232
+ return;
3233
+ }
3233
3234
  setSendMode("abort");
3234
3235
  const container = messagesContainers.get(currentChannelId) || messagesEl;
3235
3236
  addMessage2(text, "user", true, container);
@@ -3263,10 +3264,16 @@ ${data.error || "channel not found"}`, "error");
3263
3264
  })
3264
3265
  });
3265
3266
  if (!res.ok) {
3266
- addMessage2("\u53D1\u9001\u5931\u8D25", "ai");
3267
+ hideLoopStatusBar2();
3268
+ const errBody = await res.json().catch(() => ({}));
3269
+ const reason = errBody?.error || errBody?.message || `HTTP ${res.status}`;
3270
+ addMessage2(`\u26A0\uFE0F \u53D1\u9001\u5931\u8D25: ${reason}`, "ai");
3267
3271
  setSendMode("idle");
3272
+ if (typeof showSimpleToast === "function") showSimpleToast("\u26A0\uFE0F " + String(reason).slice(0, 200));
3273
+ return;
3268
3274
  }
3269
3275
  } catch (err) {
3276
+ hideLoopStatusBar2();
3270
3277
  addMessage2("\u8FDE\u63A5\u9519\u8BEF", "ai");
3271
3278
  console.error("Send error", err);
3272
3279
  setSendMode("idle");
@@ -3520,6 +3527,8 @@ ${data.error || "channel not found"}`, "error");
3520
3527
  { cmd: "goal", desc: "\u6682\u505C\u76EE\u6807 (park_goal)", args: "\u76EE\u6807ID; \u539F\u56E0" },
3521
3528
  { cmd: "skill", desc: "\u6C89\u6DC0\u6280\u80FD (create_skill)", args: "\u6280\u80FD\u540D; \u63CF\u8FF0; \u6B65\u9AA4" },
3522
3529
  { cmd: "add-friend", desc: "\u6DFB\u52A0 P2P \u597D\u53CB", args: "\u516C\u94A5; \u5907\u6CE8" },
3530
+ { cmd: "world", desc: "\u5207\u6362\u5230\u4E16\u754C\u6A21\u5F0F (\u673A\u4F1A\u6D41)", args: "" },
3531
+ { cmd: "chat", desc: "\u5207\u6362\u5230\u804A\u5929\u6A21\u5F0F (\u5BF9\u8BDD)", args: "" },
3523
3532
  { cmd: "help", desc: "\u663E\u793A\u53EF\u7528\u547D\u4EE4", args: "" }
3524
3533
  ];
3525
3534
  var slashDropdownEl = null;
@@ -3542,6 +3551,14 @@ ${data.error || "channel not found"}`, "error");
3542
3551
  slashBlockEnd = -1;
3543
3552
  }
3544
3553
  function applySlashCommand(cmdObj) {
3554
+ if (cmdObj.cmd === "world" || cmdObj.cmd === "chat") {
3555
+ const panel = cmdObj.cmd === "world" ? "world" : "chat";
3556
+ if (typeof switchPanel === "function") switchPanel(panel);
3557
+ input.value = "";
3558
+ closeSlashDropdown();
3559
+ if (typeof showSimpleToast === "function") showSimpleToast(panel === "world" ? "\u{1F30D} \u5DF2\u5207\u6362\u5230\u4E16\u754C\u6A21\u5F0F" : "\u{1F4AC} \u5DF2\u5207\u6362\u5230\u804A\u5929\u6A21\u5F0F");
3560
+ return;
3561
+ }
3545
3562
  const anchor = slashAnchor;
3546
3563
  const blockEnd = slashBlockEnd >= 0 ? slashBlockEnd : anchor + 1 + (getCurrentSlashQuery()?.query || "").length;
3547
3564
  if (anchor < 0 || anchor > input.value.length || input.value[anchor] !== "/") {
@@ -4076,6 +4093,9 @@ ${data.error || "channel not found"}`, "error");
4076
4093
  if (sidebarToggle) {
4077
4094
  sidebarToggle.addEventListener("click", toggleSidebar);
4078
4095
  }
4096
+ if (sidebarExpandBtn) {
4097
+ sidebarExpandBtn.addEventListener("click", expandSidebar);
4098
+ }
4079
4099
  if (newChannelBtn) {
4080
4100
  newChannelBtn.addEventListener("click", () => {
4081
4101
  createChannel("\u667A\u80FD\u4F53");
@@ -4994,27 +5014,18 @@ ${data.error || "channel not found"}`, "error");
4994
5014
  }
4995
5015
  var worldView = document.getElementById("world-view");
4996
5016
  var worldBtn = document.getElementById("world-btn");
4997
- var worldViewClose = document.getElementById("world-view-close");
4998
- var worldRefreshBtn = document.getElementById("world-refresh-btn");
4999
- var worldIntentInput = document.getElementById("world-intent-input");
5000
- var worldIntentPriority = document.getElementById("world-intent-priority");
5001
- var worldIntentBudget = document.getElementById("world-intent-budget");
5002
- var worldIntentSet = document.getElementById("world-intent-set");
5003
- var worldError = document.getElementById("world-intent-error");
5017
+ var worldIntentCol = document.getElementById("world-intent-col");
5018
+ var worldColToggle = document.getElementById("world-col-toggle");
5019
+ var worldColClose = document.getElementById("world-col-close");
5004
5020
  var worldIntentsList = document.getElementById("world-intents-list");
5005
5021
  var worldOpportunities = document.getElementById("world-opportunities");
5006
5022
  var worldStats = document.getElementById("world-stats");
5007
5023
  var worldBadge = document.getElementById("world-badge");
5008
- var worldProfileInput = document.getElementById("world-profile-input");
5009
- var worldProfileSet = document.getElementById("world-profile-set");
5010
5024
  var worldLoaded = false;
5011
5025
  var worldPollTimer = null;
5012
5026
  var WORLD_POLL_MS = 3e4;
5013
5027
  function worldStatus(msg) {
5014
- if (worldError) {
5015
- worldError.textContent = msg || "";
5016
- worldError.style.display = msg ? "" : "none";
5017
- }
5028
+ if (msg) console.info("[world]", msg);
5018
5029
  }
5019
5030
  async function fetchWorldIntents() {
5020
5031
  try {
@@ -5035,7 +5046,7 @@ ${data.error || "channel not found"}`, "error");
5035
5046
  function renderWorldIntents(intents) {
5036
5047
  if (!worldIntentsList) return;
5037
5048
  if (!intents.length) {
5038
- worldIntentsList.innerHTML = '<div class="form-hint" style="font-size:11px;">(\u8FD8\u6CA1\u6709\u610F\u56FE \u2014 \u53F3\u8FB9\u58F0\u660E\u4E00\u4E2A)</div>';
5049
+ worldIntentsList.innerHTML = '<div class="form-hint" style="font-size:11px;">(\u8FD8\u6CA1\u6709\u610F\u56FE \u2014 \u5728\u5E95\u90E8\u8F93\u5165\u6846\u544A\u8BC9\u4E16\u754C\u4F60\u5728\u505A\u4EC0\u4E48)</div>';
5039
5050
  return;
5040
5051
  }
5041
5052
  const active = intents.filter((i) => i.status === "active");
@@ -5082,12 +5093,30 @@ ${data.error || "channel not found"}`, "error");
5082
5093
  }
5083
5094
  const cards = opps.map((o) => {
5084
5095
  const pct = Math.round(o.score * 100);
5085
- return `<div class="world-opp-card" style="border:1px solid var(--border,#3a3a36);border-radius:12px;padding:12px 14px;margin-bottom:10px;background:var(--bg-card,#222220);">
5096
+ const inboxLine = o.inbox ? `<div style="font-size:11px;color:#8a8a80;margin-top:6px;border-top:1px dashed var(--border,#3a3a36);padding-top:6px;">
5097
+ \u2709 \u5916\u90E8\u6295\u9012 \xB7 \u6765\u81EA <span style="color:#c4d640;">${String(o.inbox.providerName || o.inbox.providerDid || "").slice(0, 40)}</span>
5098
+ \xB7 <span style="color:#6aaa6a;">\u2713 \u9A8C\u7B7E\u901A\u8FC7</span>
5099
+ ${(o.inbox.refs || []).length ? "\xB7 " + o.inbox.refs.map((r) => `<a href="${String(r).replace(/</g, "&lt;")}" target="_blank" rel="noopener" style="color:#7a9ad4;">[\u5F15\u7528]</a>`).join(" ") : ""}
5100
+ </div>` : "";
5101
+ const searchLine = o.search ? `<div style="font-size:11px;color:#8a8a80;margin-top:6px;border-top:1px dashed var(--border,#3a3a36);padding-top:6px;">
5102
+ \u{1F50E} AI \u4E3B\u52A8\u641C\u7D22 \xB7 <a href="${String(o.search.url || "").replace(/</g, "&lt;")}" target="_blank" rel="noopener" style="color:#7a9ad4;">\u6253\u5F00\u539F\u6587 \u2197</a>
5103
+ \xB7 <span style="color:#6aaa6a;">\u6765\u6E90 ${o.search.source}</span>
5104
+ </div>` : "";
5105
+ const detailLine = `<div class="world-opp-detail" style="display:none;margin-top:8px;padding-top:8px;border-top:1px solid var(--border,#3a3a36);font-size:12px;color:#b8b8b0;line-height:1.6;">
5106
+ <div style="font-weight:600;color:#f0f0ea;margin-bottom:4px;">\u5B8C\u6574\u5185\u5BB9</div>
5107
+ <div>${String(o.summary || "(\u65E0\u66F4\u591A\u5185\u5BB9)").replace(/</g, "&lt;")}</div>
5108
+ ${o.search?.url ? `<div style="margin-top:6px;">\u{1F517} <a href="${String(o.search.url).replace(/</g, "&lt;")}" target="_blank" rel="noopener" style="color:#7a9ad4;">${String(o.search.url).slice(0, 80)}</a></div>` : ""}
5109
+ <div style="margin-top:6px;color:#606058;">\u5339\u914D ${pct}% \xB7 \u6765\u6E90 ${o.source} \xB7 id ${o.id}</div>
5110
+ </div>`;
5111
+ return `<div class="world-opp-card" data-opp-card="${o.id}" style="border:1px solid var(--border,#3a3a36);border-radius:12px;padding:12px 14px;margin-bottom:10px;background:var(--bg-card,#222220);cursor:pointer;transition:border-color 0.2s;">
5086
5112
  <div style="display:flex;justify-content:space-between;align-items:center;">
5087
5113
  <b style="font-size:14px;">${String(o.title || "").slice(0, 60).replace(/</g, "&lt;")}</b>
5088
5114
  <span style="color:#c4d640;font-weight:700;font-size:14px;">${pct}%</span>
5089
5115
  </div>
5090
5116
  <div style="font-size:12px;color:#909088;margin-top:4px;">${String(o.summary || "").slice(0, 90).replace(/</g, "&lt;")}</div>
5117
+ ${inboxLine}
5118
+ ${searchLine}
5119
+ ${detailLine}
5091
5120
  <div style="margin-top:8px;display:flex;gap:8px;">
5092
5121
  <button class="btn-primary btn-sm" data-opp-action="accept" data-opp-id="${o.id}" style="font-size:11px;padding:3px 12px;">\u770B\u770B</button>
5093
5122
  <button class="btn-secondary btn-sm" data-opp-action="ignore" data-opp-id="${o.id}" style="font-size:11px;padding:3px 12px;">\u5FFD\u7565</button>
@@ -5096,26 +5125,39 @@ ${data.error || "channel not found"}`, "error");
5096
5125
  }).join("");
5097
5126
  worldOpportunities.innerHTML = cards;
5098
5127
  worldOpportunities.querySelectorAll("[data-opp-action]").forEach((btn) => {
5099
- btn.addEventListener("click", async () => {
5128
+ btn.addEventListener("click", async (ev) => {
5129
+ ev.stopPropagation();
5100
5130
  const id = btn.dataset.oppId;
5101
5131
  if (btn.dataset.oppAction === "ignore") {
5102
5132
  try {
5103
5133
  await fetch("/api/opportunities/" + id + "/ignore", { method: "POST" });
5104
5134
  } catch (e) {
5105
5135
  }
5106
- btn.closest(".world-opp-card").style.opacity = "0.3";
5107
- btn.closest(".world-opp-card").style.filter = "grayscale(1)";
5136
+ const card = btn.closest(".world-opp-card");
5137
+ if (card) {
5138
+ card.style.opacity = "0.3";
5139
+ card.style.filter = "grayscale(1)";
5140
+ }
5108
5141
  } else {
5109
5142
  try {
5110
5143
  await fetch("/api/opportunities/" + id + "/accept", { method: "POST" });
5111
5144
  } catch (e) {
5112
5145
  }
5113
- const srcId = id.replace(/^opp_/, "ann-");
5114
- worldStatus("\u5DF2\u8BB0\u4E0B: " + srcId);
5115
- btn.closest(".world-opp-card").style.borderColor = "#c4d640";
5146
+ toggleOppDetail(btn.closest(".world-opp-card"));
5116
5147
  }
5117
5148
  });
5118
5149
  });
5150
+ worldOpportunities.querySelectorAll("[data-opp-card]").forEach((card) => {
5151
+ card.addEventListener("click", () => toggleOppDetail(card));
5152
+ });
5153
+ }
5154
+ function toggleOppDetail(card) {
5155
+ if (!card) return;
5156
+ const detail = card.querySelector(".world-opp-detail");
5157
+ if (!detail) return;
5158
+ const shown = detail.style.display !== "none";
5159
+ detail.style.display = shown ? "none" : "block";
5160
+ card.style.borderColor = shown ? "var(--border,#3a3a36)" : "#c4d640";
5119
5161
  }
5120
5162
  async function loadWorld() {
5121
5163
  worldLoaded = true;
@@ -5136,88 +5178,83 @@ ${data.error || "channel not found"}`, "error");
5136
5178
  worldPollTimer = null;
5137
5179
  }
5138
5180
  }
5139
- function showWorldView() {
5140
- if (worldView) {
5141
- worldView.hidden = false;
5181
+ var tabWorld = document.getElementById("tab-world");
5182
+ var tabChat = document.getElementById("tab-chat");
5183
+ var loopStatusBar2 = document.getElementById("loop-status-bar");
5184
+ function switchPanel(panel) {
5185
+ if (!worldView || !messagesEl) return;
5186
+ const isWorld = panel === "world";
5187
+ worldView.style.display = isWorld ? "flex" : "none";
5188
+ if (messagesEl) messagesEl.hidden = isWorld;
5189
+ if (loopStatusBar2) loopStatusBar2.hidden = isWorld;
5190
+ if (inputArea) inputArea.hidden = isWorld;
5191
+ if (tabWorld) tabWorld.classList.toggle("active", isWorld);
5192
+ if (tabChat) tabChat.classList.toggle("active", !isWorld);
5193
+ if (isWorld) {
5142
5194
  if (!worldLoaded) loadWorld();
5143
5195
  else {
5144
5196
  fetchWorldIntents();
5145
5197
  refreshWorldOpportunities();
5146
5198
  }
5147
5199
  startWorldPoll();
5200
+ } else {
5201
+ stopWorldPoll();
5148
5202
  }
5149
5203
  }
5150
- function hideWorldView() {
5151
- if (worldView) worldView.hidden = true;
5152
- stopWorldPoll();
5204
+ if (tabWorld) tabWorld.addEventListener("click", () => switchPanel("world"));
5205
+ if (tabChat) tabChat.addEventListener("click", () => switchPanel("chat"));
5206
+ if (worldBtn) worldBtn.addEventListener("click", () => switchPanel("world"));
5207
+ function toggleWorldIntentCol(show) {
5208
+ if (!worldIntentCol) return;
5209
+ worldIntentCol.style.display = show ? "" : "none";
5210
+ if (worldColToggle) worldColToggle.textContent = show ? "\u25BE \u6211\u7684\u610F\u56FE" : "\u25B8 \u6211\u7684\u610F\u56FE";
5153
5211
  }
5154
- if (worldBtn) worldBtn.addEventListener("click", showWorldView);
5155
- if (worldViewClose) worldViewClose.addEventListener("click", hideWorldView);
5156
- if (worldRefreshBtn) worldRefreshBtn.addEventListener("click", () => {
5157
- fetchWorldIntents();
5158
- refreshWorldOpportunities();
5212
+ if (worldColToggle) worldColToggle.addEventListener("click", () => {
5213
+ toggleWorldIntentCol(worldIntentCol ? worldIntentCol.style.display === "none" : false);
5159
5214
  });
5160
- if (worldProfileSet) worldProfileSet.addEventListener("click", async () => {
5161
- const text = (worldProfileInput && worldProfileInput.value || "").trim();
5162
- if (!text) {
5163
- worldStatus("\u5148\u5199\u4E00\u53E5\u4F60\u662F\u8C01 / \u5728\u505A\u4EC0\u4E48");
5215
+ if (worldColClose) worldColClose.addEventListener("click", () => toggleWorldIntentCol(false));
5216
+ var worldInput = document.getElementById("world-input");
5217
+ var worldInputSend = document.getElementById("world-input-send");
5218
+ async function tellWorld() {
5219
+ const text = (worldInput && worldInput.value || "").trim();
5220
+ if (!text) return;
5221
+ if (text === "/chat" || text === "/world") {
5222
+ const panel = text === "/chat" ? "chat" : "world";
5223
+ if (typeof switchPanel === "function") switchPanel(panel);
5224
+ worldInput.value = "";
5225
+ if (typeof showSimpleToast === "function") showSimpleToast(panel === "world" ? "\u{1F30D} \u5DF2\u5207\u6362\u5230\u4E16\u754C\u6A21\u5F0F" : "\u{1F4AC} \u5DF2\u5207\u6362\u5230\u804A\u5929\u6A21\u5F0F");
5164
5226
  return;
5165
5227
  }
5166
- if (worldProfileSet) worldProfileSet.disabled = true;
5167
- try {
5168
- const res = await fetch("/api/world/profile", {
5169
- method: "PUT",
5170
- headers: { "Content-Type": "application/json" },
5171
- body: JSON.stringify({ name: text, about: text, tags: (text.match(/[\u4e00-\u9fff]{2,}|[a-z][a-z0-9_-]{1,}/gi) || []).slice(0, 6) })
5172
- });
5173
- if (!res.ok) {
5174
- const err = await res.json().catch(() => ({}));
5175
- worldStatus("\u753B\u50CF\u4FDD\u5B58\u5931\u8D25: " + (err.error || "HTTP " + res.status));
5176
- return;
5177
- }
5178
- worldStatus("\u753B\u50CF\u5DF2\u4FDD\u5B58 \u2014 \u4E16\u754C\u5F00\u59CB\u6309\u5B83\u63A8\u9001");
5179
- await refreshWorldOpportunities();
5180
- } catch (e) {
5181
- worldStatus("\u753B\u50CF\u4FDD\u5B58\u5931\u8D25: " + (e && e.message ? e.message : e));
5182
- } finally {
5183
- if (worldProfileSet) worldProfileSet.disabled = false;
5228
+ worldInput.value = "";
5229
+ if (worldOpportunities) {
5230
+ const echo = document.createElement("div");
5231
+ echo.className = "world-opp-card";
5232
+ echo.style.cssText = "border:1px dashed var(--accent,#c4d640);border-radius:12px;padding:10px 14px;margin-bottom:10px;background:var(--bg-card,#222220);font-size:13px;color:var(--text,#f0f0ea);";
5233
+ echo.innerHTML = `<div style="font-weight:700;margin-bottom:4px;">\u270D \u4F60\u544A\u8BC9\u4E16\u754C</div><div>${String(text).replace(/</g, "&lt;")}</div><div style="color:#909088;font-size:11px;margin-top:6px;">\u5DF2\u58F0\u660E\u610F\u56FE \xB7 \u6B63\u5728\u4E3A\u4F60\u5339\u914D\u673A\u4F1A\u2026</div>`;
5234
+ worldOpportunities.prepend(echo);
5184
5235
  }
5185
- });
5186
- if (worldIntentSet) worldIntentSet.addEventListener("click", async () => {
5187
- const text = (worldIntentInput && worldIntentInput.value || "").trim();
5188
- if (!text) {
5189
- worldStatus("\u5148\u5199\u4E00\u53E5\u4F60\u73B0\u5728\u5728\u505A\u4EC0\u4E48");
5190
- return;
5191
- }
5192
- if (worldIntentSet) worldIntentSet.disabled = true;
5236
+ if (typeof showSimpleToast === "function") showSimpleToast("\u{1F30D} \u5DF2\u544A\u8BC9\u4E16\u754C\uFF1A" + text, "success");
5193
5237
  try {
5194
- const body = { text, priority: Number(worldIntentPriority ? worldIntentPriority.value : 3) };
5195
- if (worldIntentBudget && worldIntentBudget.value.trim()) body.budget = worldIntentBudget.value.trim();
5196
- const res = await fetch("/api/intents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) });
5238
+ const res = await fetch("/api/intents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text, priority: 3 }) });
5197
5239
  if (!res.ok) {
5198
- const err = await res.json().catch(() => ({}));
5199
- worldStatus("\u58F0\u660E\u5931\u8D25: " + (err.error || "HTTP " + res.status));
5240
+ console.info("[world] \u58F0\u660E\u5931\u8D25 HTTP", res.status);
5200
5241
  return;
5201
5242
  }
5202
- if (worldIntentInput) worldIntentInput.value = "";
5203
- if (worldIntentBudget) worldIntentBudget.value = "";
5204
5243
  await fetchWorldIntents();
5205
- await refreshWorldOpportunities();
5206
- if (worldPollTimer) {
5207
- clearInterval(worldPollTimer);
5208
- startWorldPoll();
5244
+ try {
5245
+ await fetch("/api/world/search", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ topic: text }) });
5246
+ } catch (e) {
5247
+ console.info("[world] \u4E3B\u52A8\u641C\u7D22\u5931\u8D25", e);
5209
5248
  }
5249
+ await refreshWorldOpportunities();
5210
5250
  } catch (e) {
5211
- worldStatus("\u58F0\u660E\u5931\u8D25: " + (e && e.message ? e.message : e));
5212
- } finally {
5213
- if (worldIntentSet) worldIntentSet.disabled = false;
5251
+ console.info("[world] \u58F0\u660E\u5931\u8D25", e);
5214
5252
  }
5215
- });
5216
- if (worldIntentInput) {
5217
- worldIntentInput.addEventListener("keydown", (e) => {
5218
- if (e.key === "Enter" && worldIntentSet) worldIntentSet.click();
5219
- });
5220
5253
  }
5254
+ if (worldInputSend) worldInputSend.addEventListener("click", tellWorld);
5255
+ if (worldInput) worldInput.addEventListener("keydown", (e) => {
5256
+ if (e.key === "Enter") tellWorld();
5257
+ });
5221
5258
  (async function autoWorldBadge() {
5222
5259
  try {
5223
5260
  await refreshWorldOpportunities();