@bolloon/bolloon-agent 0.6.5 → 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 (169) 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-web-search.js +39 -0
  15. package/dist/agents/p2p-chat-tools.js +1 -2
  16. package/dist/agents/parse-tool-call.js +9 -22
  17. package/dist/agents/pi-harness.js +1 -2
  18. package/dist/agents/pi-sdk-tools.js +180 -5
  19. package/dist/agents/pi-sdk.js +143 -9
  20. package/dist/agents/process-runner.js +28 -9
  21. package/dist/agents/react-loop.js +9 -17
  22. package/dist/agents/runner-resolver.js +4 -3
  23. package/dist/agents/skill-loader.js +22 -15
  24. package/dist/agents/skills-manager.js +1 -25
  25. package/dist/agents/workflow-pivot-loop.js +152 -61
  26. package/dist/bootstrap/context-collector.js +2 -4
  27. package/dist/bootstrap/context-hierarchy.js +13 -1
  28. package/dist/cli/ink-app.js +44 -17
  29. package/dist/cli/intent-command.js +22 -12
  30. package/dist/cli/loading-tui.js +8 -2
  31. package/dist/cli/log-gate.js +40 -8
  32. package/dist/cli/mention-data.js +1 -0
  33. package/dist/cli/reply-hygiene.js +22 -0
  34. package/dist/cli/trace-line.js +61 -2
  35. package/dist/electron-build/agents/agent-gateway.js.map +1 -1
  36. package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -1
  37. package/dist/electron-build/agents/chain/chain-settlement.js +0 -1
  38. package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -1
  39. package/dist/electron-build/agents/contacts/chain.js.map +1 -1
  40. package/dist/electron-build/agents/contacts/human-directory.js +204 -0
  41. package/dist/electron-build/agents/contacts/human-directory.js.map +1 -0
  42. package/dist/electron-build/agents/contacts/policy.js.map +1 -1
  43. package/dist/electron-build/agents/contacts/tools.js +112 -0
  44. package/dist/electron-build/agents/contacts/tools.js.map +1 -1
  45. package/dist/electron-build/agents/experience-review.js +0 -3
  46. package/dist/electron-build/agents/experience-review.js.map +1 -1
  47. package/dist/electron-build/agents/external-events.js.map +1 -1
  48. package/dist/electron-build/agents/goal-flywheel/goal-change.js +3 -4
  49. package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -1
  50. package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -1
  51. package/dist/electron-build/agents/goal-resume.js +0 -4
  52. package/dist/electron-build/agents/goal-resume.js.map +1 -1
  53. package/dist/electron-build/agents/intent-classifier.js +1 -1
  54. package/dist/electron-build/agents/lesson-to-skill.js +0 -1
  55. package/dist/electron-build/agents/lesson-to-skill.js.map +1 -1
  56. package/dist/electron-build/agents/loop-review.js +2 -2
  57. package/dist/electron-build/agents/loop-review.js.map +1 -1
  58. package/dist/electron-build/agents/memory-recall.js.map +1 -1
  59. package/dist/electron-build/agents/memory-store.js +404 -0
  60. package/dist/electron-build/agents/memory-store.js.map +1 -0
  61. package/dist/electron-build/agents/parse-tool-call.js +9 -22
  62. package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
  63. package/dist/electron-build/agents/pi-harness.js +1 -2
  64. package/dist/electron-build/agents/pi-harness.js.map +1 -1
  65. package/dist/electron-build/agents/pi-sdk-tools.js +180 -5
  66. package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
  67. package/dist/electron-build/agents/pi-sdk.js +143 -9
  68. package/dist/electron-build/agents/pi-sdk.js.map +1 -1
  69. package/dist/electron-build/agents/pre-tool-validator.js.map +1 -1
  70. package/dist/electron-build/agents/process-runner.js +28 -9
  71. package/dist/electron-build/agents/process-runner.js.map +1 -1
  72. package/dist/electron-build/agents/runner-resolver.js +4 -3
  73. package/dist/electron-build/agents/runner-resolver.js.map +1 -1
  74. package/dist/electron-build/agents/skill-loader.js +22 -16
  75. package/dist/electron-build/agents/skill-loader.js.map +1 -1
  76. package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -1
  77. package/dist/electron-build/agents/skills-manager.js +0 -24
  78. package/dist/electron-build/agents/skills-manager.js.map +1 -1
  79. package/dist/electron-build/agents/task/task-runner.js.map +1 -1
  80. package/dist/electron-build/agents/workflow-engine.js.map +1 -1
  81. package/dist/electron-build/agents/workflow-pivot-loop.js +155 -62
  82. package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
  83. package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -1
  84. package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -1
  85. package/dist/electron-build/bootstrap/context-collector.js +2 -4
  86. package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
  87. package/dist/electron-build/bootstrap/context-hierarchy.js +13 -1
  88. package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -1
  89. package/dist/electron-build/electron-preload.js.map +1 -1
  90. package/dist/electron-build/external-engines/delegate.js.map +1 -1
  91. package/dist/electron-build/kernel/channel-actor.js +31 -2
  92. package/dist/electron-build/kernel/channel-actor.js.map +1 -1
  93. package/dist/electron-build/kernel/model-runtime.js +8 -1
  94. package/dist/electron-build/kernel/model-runtime.js.map +1 -1
  95. package/dist/electron-build/llm/config-store.js +20 -0
  96. package/dist/electron-build/llm/config-store.js.map +1 -1
  97. package/dist/electron-build/llm/embedding-adapter.js +75 -0
  98. package/dist/electron-build/llm/embedding-adapter.js.map +1 -0
  99. package/dist/electron-build/llm/pi-ai.js +96 -35
  100. package/dist/electron-build/llm/pi-ai.js.map +1 -1
  101. package/dist/electron-build/network/agent-network.js.map +1 -1
  102. package/dist/electron-build/orbitdb/memory-sync.js +76 -0
  103. package/dist/electron-build/orbitdb/memory-sync.js.map +1 -0
  104. package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -1
  105. package/dist/electron-build/security/tool-gate.js +2 -0
  106. package/dist/electron-build/security/tool-gate.js.map +1 -1
  107. package/dist/electron-build/setup/setup-store.js +34 -6
  108. package/dist/electron-build/setup/setup-store.js.map +1 -1
  109. package/dist/electron-build/social/heartbeat.js +18 -5
  110. package/dist/electron-build/social/heartbeat.js.map +1 -1
  111. package/dist/electron-build/utils/update-manager.js.map +1 -1
  112. package/dist/index.js +250 -12
  113. package/dist/ios/bolloon-web.json +2 -2
  114. package/dist/ios/client.js +74 -35
  115. package/dist/ios/icons/icon-1400x1400.png +0 -0
  116. package/dist/ios/icons/icon-1400x1400.webp +0 -0
  117. package/dist/ios/index.html +106 -107
  118. package/dist/ios/mobile-agent.js +346 -45
  119. package/dist/ios/mobile-core.js +7303 -4590
  120. package/dist/ios/mobile-harness.js +1004 -0
  121. package/dist/ios/mobile-orbitdb.js +194 -0
  122. package/dist/ios/mobile-persona.js +80 -0
  123. package/dist/ios/mobile-privacy.js +2 -3
  124. package/dist/ios/mobile.css +286 -21
  125. package/dist/ios/mobile.html +106 -107
  126. package/dist/ios/mobile.js +300 -132
  127. package/dist/ios/routes-world.js +4 -1
  128. package/dist/ios/server.js +3 -1
  129. package/dist/ios/style.css +13 -5
  130. package/dist/ios/ui/message-renderer.js +4 -18
  131. package/dist/ios/ui/step-timeline.js +14 -1
  132. package/dist/kernel/channel-actor.js +30 -2
  133. package/dist/kernel/gate-scan.js +1 -1
  134. package/dist/kernel/index.js +33 -0
  135. package/dist/kernel/model-runtime.js +8 -1
  136. package/dist/kernel/plan.js +3 -3
  137. package/dist/kernel/roster.js +3 -3
  138. package/dist/llm/config-store.js +20 -0
  139. package/dist/llm/embedding-adapter.js +69 -0
  140. package/dist/llm/pi-ai.js +94 -34
  141. package/dist/orbitdb/memory-sync.js +70 -0
  142. package/dist/pi-ecosystem-judgment/adaptive-scan.js +1 -1
  143. package/dist/security/tool-gate.js +2 -0
  144. package/dist/setup/setup-store.js +34 -6
  145. package/dist/social/heartbeat.js +18 -5
  146. package/dist/web/bolloon-web.json +3 -3
  147. package/dist/web/client.js +74 -35
  148. package/dist/web/icons/icon-1400x1400.png +0 -0
  149. package/dist/web/icons/icon-1400x1400.webp +0 -0
  150. package/dist/web/index.html +37 -25
  151. package/dist/web/mobile-agent.js +351 -45
  152. package/dist/web/mobile-contacts.js +0 -4
  153. package/dist/web/mobile-core.js +7430 -4590
  154. package/dist/web/mobile-harness.js +1004 -0
  155. package/dist/web/mobile-orbitdb.js +194 -0
  156. package/dist/web/mobile-persona.js +80 -0
  157. package/dist/web/mobile-privacy.js +2 -3
  158. package/dist/web/mobile.css +286 -21
  159. package/dist/web/mobile.html +121 -107
  160. package/dist/web/mobile.js +409 -136
  161. package/dist/web/routes-contacts.js +94 -1
  162. package/dist/web/routes-world.js +4 -1
  163. package/dist/web/server.js +4 -2
  164. package/dist/web/style.css +13 -5
  165. package/dist/web/sw.js +1 -1
  166. package/dist/web/ui/message-renderer.js +4 -18
  167. package/dist/web/ui/step-timeline.js +14 -1
  168. package/package.json +3 -2
  169. 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;
@@ -3232,6 +3224,13 @@ ${data.error || "channel not found"}`, "error");
3232
3224
  async function sendMessage() {
3233
3225
  const text = input.value.trim();
3234
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
+ }
3235
3234
  setSendMode("abort");
3236
3235
  const container = messagesContainers.get(currentChannelId) || messagesEl;
3237
3236
  addMessage2(text, "user", true, container);
@@ -3528,6 +3527,8 @@ ${data.error || "channel not found"}`, "error");
3528
3527
  { cmd: "goal", desc: "\u6682\u505C\u76EE\u6807 (park_goal)", args: "\u76EE\u6807ID; \u539F\u56E0" },
3529
3528
  { cmd: "skill", desc: "\u6C89\u6DC0\u6280\u80FD (create_skill)", args: "\u6280\u80FD\u540D; \u63CF\u8FF0; \u6B65\u9AA4" },
3530
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: "" },
3531
3532
  { cmd: "help", desc: "\u663E\u793A\u53EF\u7528\u547D\u4EE4", args: "" }
3532
3533
  ];
3533
3534
  var slashDropdownEl = null;
@@ -3550,6 +3551,14 @@ ${data.error || "channel not found"}`, "error");
3550
3551
  slashBlockEnd = -1;
3551
3552
  }
3552
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
+ }
3553
3562
  const anchor = slashAnchor;
3554
3563
  const blockEnd = slashBlockEnd >= 0 ? slashBlockEnd : anchor + 1 + (getCurrentSlashQuery()?.query || "").length;
3555
3564
  if (anchor < 0 || anchor > input.value.length || input.value[anchor] !== "/") {
@@ -4084,6 +4093,9 @@ ${data.error || "channel not found"}`, "error");
4084
4093
  if (sidebarToggle) {
4085
4094
  sidebarToggle.addEventListener("click", toggleSidebar);
4086
4095
  }
4096
+ if (sidebarExpandBtn) {
4097
+ sidebarExpandBtn.addEventListener("click", expandSidebar);
4098
+ }
4087
4099
  if (newChannelBtn) {
4088
4100
  newChannelBtn.addEventListener("click", () => {
4089
4101
  createChannel("\u667A\u80FD\u4F53");
@@ -5002,7 +5014,9 @@ ${data.error || "channel not found"}`, "error");
5002
5014
  }
5003
5015
  var worldView = document.getElementById("world-view");
5004
5016
  var worldBtn = document.getElementById("world-btn");
5005
- var worldRefreshBtn = document.getElementById("world-refresh-btn");
5017
+ var worldIntentCol = document.getElementById("world-intent-col");
5018
+ var worldColToggle = document.getElementById("world-col-toggle");
5019
+ var worldColClose = document.getElementById("world-col-close");
5006
5020
  var worldIntentsList = document.getElementById("world-intents-list");
5007
5021
  var worldOpportunities = document.getElementById("world-opportunities");
5008
5022
  var worldStats = document.getElementById("world-stats");
@@ -5190,16 +5204,36 @@ ${data.error || "channel not found"}`, "error");
5190
5204
  if (tabWorld) tabWorld.addEventListener("click", () => switchPanel("world"));
5191
5205
  if (tabChat) tabChat.addEventListener("click", () => switchPanel("chat"));
5192
5206
  if (worldBtn) worldBtn.addEventListener("click", () => switchPanel("world"));
5193
- if (worldRefreshBtn) worldRefreshBtn.addEventListener("click", () => {
5194
- fetchWorldIntents();
5195
- refreshWorldOpportunities();
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";
5211
+ }
5212
+ if (worldColToggle) worldColToggle.addEventListener("click", () => {
5213
+ toggleWorldIntentCol(worldIntentCol ? worldIntentCol.style.display === "none" : false);
5196
5214
  });
5215
+ if (worldColClose) worldColClose.addEventListener("click", () => toggleWorldIntentCol(false));
5197
5216
  var worldInput = document.getElementById("world-input");
5198
5217
  var worldInputSend = document.getElementById("world-input-send");
5199
5218
  async function tellWorld() {
5200
5219
  const text = (worldInput && worldInput.value || "").trim();
5201
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");
5226
+ return;
5227
+ }
5202
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);
5235
+ }
5236
+ if (typeof showSimpleToast === "function") showSimpleToast("\u{1F30D} \u5DF2\u544A\u8BC9\u4E16\u754C\uFF1A" + text, "success");
5203
5237
  try {
5204
5238
  const res = await fetch("/api/intents", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ text, priority: 3 }) });
5205
5239
  if (!res.ok) {
@@ -5207,6 +5241,11 @@ ${data.error || "channel not found"}`, "error");
5207
5241
  return;
5208
5242
  }
5209
5243
  await fetchWorldIntents();
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);
5248
+ }
5210
5249
  await refreshWorldOpportunities();
5211
5250
  } catch (e) {
5212
5251
  console.info("[world] \u58F0\u660E\u5931\u8D25", e);
@@ -42,120 +42,120 @@
42
42
  </div>
43
43
  </header>
44
44
 
45
- <!-- 主页面: 双频滑动卡片 -->
46
- <main class="page-container" id="page-main" data-tab="main" hidden>
47
- <!-- 2026-10-05 (leo Intent Network): 世界机会流 — 自动流入, 不是按钮式被动响应 -->
48
- <div class="world-feed" id="world-feed" hidden>
49
- <div class="world-feed-header">
50
- <span>✦ 世界送来</span>
51
- <span class="world-feed-sub">自动匹配 · 持续校准</span>
45
+ <!-- 主页面: 世界机会流双栏卡片 (2026-10-07: 首页 = 对应桌面世界模式, opportunity 双栏) -->
46
+ <main class="page-container" id="page-main" data-tab="main" hidden>
47
+ <!-- 世界机会流 — 双栏卡片, 自动流入 (与桌面世界模式同数据源) -->
48
+ <div class="world-feed" id="world-feed">
49
+ <div class="world-feed-header">
50
+ <span>✦ 世界送来</span>
51
+ <span class="world-feed-sub">自动匹配 · 持续校准</span>
52
+ </div>
53
+ <div class="world-feed-cards" id="world-feed-cards">
54
+ <div class="world-feed-loading">世界正在观察…</div>
55
+ </div>
52
56
  </div>
53
- <div class="world-feed-cards" id="world-feed-cards">
54
- <div class="world-feed-loading">世界正在观察…</div>
57
+ <!-- 2026-10-07: 首页底部输入框 — 告诉世界你在做什么 (对应桌面世界模式输入框, Bolloon 风格) -->
58
+ <div class="world-input-bar" id="world-input-bar">
59
+ <input type="text" id="world-input" placeholder="告诉世界你在做什么 / 需要什么… (Enter 自动匹配)" autocomplete="off" />
60
+ <button id="world-input-send" class="world-input-send">→ 世界</button>
55
61
  </div>
56
- </div>
57
- <!-- 水平滑动卡片轨道 -->
58
- <div class="card-carousel" id="card-carousel">
59
- <div class="card-track" id="card-track">
60
- <!-- 卡片由 JS 动态加载 -->
62
+ <!-- 卡片详情浮层 (垂直展开) -->
63
+ <div class="card-detail" id="card-detail" hidden>
64
+ <div class="detail-header">
65
+ <button class="icon-btn" id="detail-back">←</button>
66
+ <h2 id="detail-name"></h2>
67
+ </div>
68
+ <div class="detail-body" id="detail-body"></div>
61
69
  </div>
62
- </div>
63
- <!-- 卡片指示器 -->
64
- <div class="card-indicator" id="card-indicator"></div>
65
- <!-- 卡片详情浮层 (垂直展开) -->
66
- <div class="card-detail" id="card-detail" hidden>
67
- <div class="detail-header">
68
- <button class="icon-btn" id="detail-back">←</button>
69
- <h2 id="detail-name"></h2>
70
+ </main>
71
+ <!-- 好友 tab: 通讯录风格 (2026-10-07: 微信通讯录式布局 — 分组入口 + 字母索引联系人) -->
72
+ <section class="page" id="page-friends" data-tab="friends" hidden>
73
+ <!-- 分组快捷入口 (微信式: 新的朋友/群聊/标签/附近/扫码) -->
74
+ <div class="list">
75
+ <div class="list-item" id="item-add-friend"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="9" cy="8" r="3.2"/><path d="M3.5 19c.8-3 3-4.6 5.5-4.6S13.7 16 14.5 19"/><path d="M17 8v6M14 11h6"/></svg></span><span>新的朋友</span><span class="list-arrow">›</span></div>
76
+ <div class="list-item" id="item-p2p"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/><path d="M12 3.5c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5c-2.3-2.3-3.5-5.3-3.5-8.5S9.7 5.8 12 3.5z"/></svg></span><span>群聊</span><span class="list-arrow">›</span></div>
77
+ <div class="list-item" id="item-scan-net"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 8V6a2 2 0 0 1 2-2h2M16 4h2a2 2 0 0 1 2 2v2M20 16v2a2 2 0 0 1-2 2h-2M8 20H6a2 2 0 0 1-2-2v-2"/><path d="M4 12h16"/></svg></span><span>扫码入网 / 加好友</span></div>
78
+ <div class="list-item" id="item-nearby"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 12a8 8 0 0 1 16 0"/><path d="M7.5 12a4.5 4.5 0 0 1 9 0"/><circle cx="12" cy="12" r="1.4"/><path d="M12 13.6V19"/></svg></span><span>附近设备</span><span class="list-arrow">›</span></div>
79
+ <div class="list-item" id="item-p2p-id"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><rect x="3" y="5.5" width="18" height="13" rx="2.5"/><circle cx="8.5" cy="11" r="2"/><path d="M5.6 15.8a3.2 3.2 0 0 1 5.8 0M13.5 10h5M13.5 13.5h5"/></svg></span><span>我的 P2P ID</span></div>
70
80
  </div>
71
- <div class="detail-body" id="detail-body"></div>
72
- </div>
73
- </main>
74
- <!-- 好友 tab: 好友与 P2P (原来散在网络页, 现已归拢; 网络页不再重复这些) -->
75
- <section class="page" id="page-friends" data-tab="friends" hidden>
76
- <div class="list">
77
- <div class="list-item" id="item-add-friend"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="9" cy="8" r="3.2"/><path d="M3.5 19c.8-3 3-4.6 5.5-4.6S13.7 16 14.5 19"/><path d="M17 8v6M14 11h6"/></svg></span><span>连接好友</span><span class="list-arrow">›</span></div>
78
- <div class="list-item" id="item-nearby"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 12a8 8 0 0 1 16 0"/><path d="M7.5 12a4.5 4.5 0 0 1 9 0"/><circle cx="12" cy="12" r="1.4"/><path d="M12 13.6V19"/></svg></span><span>附近设备</span><span class="list-arrow">›</span></div>
79
- <div class="list-item" id="item-scan-net"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 8V6a2 2 0 0 1 2-2h2M16 4h2a2 2 0 0 1 2 2v2M20 16v2a2 2 0 0 1-2 2h-2M8 20H6a2 2 0 0 1-2-2v-2"/><path d="M4 12h16"/></svg></span><span>扫码入网 / 加好友</span></div>
80
- </div>
81
- <input type="file" id="qr-scan-input" accept="image/*" capture="environment" style="display:none">
82
- <div class="list">
83
- <div class="list-item" id="item-p2p"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/><path d="M12 3.5c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5c-2.3-2.3-3.5-5.3-3.5-8.5S9.7 5.8 12 3.5z"/></svg></span><span>P2P 好友</span></div>
84
- <div class="list-item" id="item-p2p-id"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><rect x="3" y="5.5" width="18" height="13" rx="2.5"/><circle cx="8.5" cy="11" r="2"/><path d="M5.6 15.8a3.2 3.2 0 0 1 5.8 0M13.5 10h5M13.5 13.5h5"/></svg></span><span>我的 P2P ID</span></div>
85
- </div>
86
- <div class="section-label">P2P 连接</div>
87
- <div class="list" id="p2p-status"></div>
88
- <div class="section-label">好友列表</div>
89
- <div class="list" id="contacts-list"></div>
90
- </section>
81
+ <input type="file" id="qr-scan-input" accept="image/*" capture="environment" style="display:none">
82
+ <div class="section-label">P2P 连接</div>
83
+ <div class="list" id="p2p-status"></div>
84
+ <div class="section-label">好友列表</div>
85
+ <!-- 通讯录式联系人列表: 按字母分组 + 右侧索引 (由 loadContacts 渲染) -->
86
+ <div class="contacts-wrap" id="contacts-wrap">
87
+ <div class="list" id="contacts-list"></div>
88
+ <div class="contacts-index" id="contacts-index"></div>
89
+ </div>
90
+ </section>
91
91
 
92
92
  </main>
93
93
 
94
- <!-- 网络 tab -->
95
- <section class="page" id="page-network" data-tab="network" hidden>
96
- <div class="list">
97
- <div class="list-item" id="item-join-global"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/><path d="M12 3.5c2.7 2.6 2.7 14.4 0 17M12 3.5c-2.7 2.6-2.7 14.4 0 17"/><circle cx="12" cy="12" r="1.2" fill="currentColor" stroke="none"/></svg></span><span>一键入网 · 全球智能体网络</span><span class="list-arrow">›</span></div>
98
- <div class="list-item" id="item-join-net"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M3.5 9.5a13 13 0 0 1 17 0M6.5 13a8.5 8.5 0 0 1 11 0M9.6 16.3a4 4 0 0 1 4.8 0"/><circle cx="12" cy="19.5" r="1.1" fill="currentColor" stroke="none"/></svg></span><span>加入网络</span><span class="list-arrow">›</span></div>
99
- </div>
100
- <div class="section-label">Agent 网络</div>
101
- <div class="list" id="net-members"></div>
102
- <div class="section-label">Agent 服务 (协议自动发现)</div>
103
- <div class="list" id="agent-services"></div>
104
- <div class="list">
105
- <div class="list-item" id="item-trade"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 7h16M4 12h10M4 17h7"/><path d="M17.5 14.5l2.5 2.5-2.5 2.5"/></svg></span><span style="flex:1">资源交易</span><span class="list-arrow">›</span></div>
106
- </div>
107
- <div class="section-label">微信息 (微支付)</div>
108
- <div class="list" id="x402-info-list"></div>
109
- <div class="section-label">智能体控制 (MCP / Skills)</div>
110
- <div class="list" id="agent-control"></div>
111
- <div class="section-label">待人工审批支付</div>
112
- <div class="list" id="approval-list"></div>
113
- <div class="section-label">A2UI 动态面板 (智能体生成)</div>
114
- <div id="a2ui-root"></div>
115
- </section>
116
-
117
- <!-- 任务 tab (2026-09-25): 与桌面/CLI 同一套任务闭环 —— 入群 · 发公告 · 看飞轮进度
118
- 分工不变: 手机 = 输入 · 展示待发内容 · 设备签名确认; 桌面 = 落盘 · 执行 · 等待回复 · 证据。
119
- 高风险动作 (入群/建群/发公告/发进群/留痕) 一律走确认页 → 设备签名 → 桌面验签。 -->
120
- <section class="page" id="page-tasks" data-tab="tasks" hidden>
121
- <div class="section-label" data-zh="本机授权与签名" data-en="This device: approval &amp; signing">本机授权与签名</div>
122
- <div class="list">
123
- <div class="list-item" id="tasks-signing-item"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><rect x="4.5" y="10.5" width="15" height="9.5" rx="2.5"/><path d="M8 10.5V7.5a4 4 0 0 1 8 0v3"/><path d="M12 14v3"/></svg></span><span id="tasks-signing" style="flex:1">检查中…</span></div>
124
- </div>
125
- <div class="sheet-text" id="tasks-hint" style="font-size:12px;color:var(--text-secondary);padding:0 14px 6px"></div>
126
- <div class="list" style="padding:0 14px 4px">
127
- <button class="sheet-choice" id="tasks-refresh" data-zh="刷新任务数据" data-en="Refresh task data" style="width:100%">刷新任务数据</button>
128
- </div>
129
-
130
- <div class="section-label" data-zh="群 (与 CLI task group 同一份记录)" data-en="Groups (same records as CLI task group)">群 (与 CLI task group 同一份记录)</div>
131
- <div class="list" id="tasks-groups"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
132
- <div style="padding:0 14px 8px">
133
- <input id="tasks-group-link" placeholder="群邀请链接 (orbitdb://…?type=group&amp;name=…)" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
134
- <button class="sheet-choice" id="tasks-group-join" data-zh="入群(需手机确认 + 签名)" data-en="Join group (needs on-device confirm + signature)" style="width:100%;margin-top:8px">入群(需手机确认 + 签名)</button>
135
- <input id="tasks-group-name" placeholder="新群名称" style="width:100%;margin-top:8px;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
136
- <button class="sheet-choice" id="tasks-group-create" data-zh="建群(需手机确认 + 签名)" data-en="Create group (needs on-device confirm + signature)" style="width:100%;margin-top:8px">建群(需手机确认 + 签名)</button>
137
- </div>
94
+ <!-- 任务 tab (2026-10-07: 对话框模式 — 仪表盘 + 消息流 + 底部派活输入框; 网络收成工具) -->
95
+ <section class="page task-chat-page" id="page-tasks" data-tab="tasks" hidden>
96
+ <!-- 仪表盘栏: 工具入口 + 状态 -->
97
+ <div class="task-dash">
98
+ <button class="task-dash-btn" id="task-dash-network" title="网络工具"><span class="task-dash-ico">🌐</span><span>网络</span></button>
99
+ <button class="task-dash-btn" id="task-dash-sign" title="本机签名状态"><span class="task-dash-ico">🔏</span><span id="tasks-signing">签名…</span></button>
100
+ <button class="task-dash-btn" id="tasks-refresh" title="刷新任务数据"><span class="task-dash-ico">⟳</span><span>刷新</span></button>
101
+ </div>
138
102
 
139
- <div class="section-label" data-zh="公告板 (与 CLI task board 同一份事实)" data-en="Task board (same facts as CLI task board)">公告板 (与 CLI task board 同一份事实)</div>
140
- <div class="list" id="tasks-board"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
141
- <div style="padding:0 14px 8px">
142
- <input id="tasks-pub-capability" placeholder="能力 (例如 research)" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
143
- <input id="tasks-pub-instruction" placeholder="任务正文 (一句能执行的委托)" style="width:100%;margin-top:8px;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
144
- <input id="tasks-pub-budget" placeholder="预算 (例如 0.05)" inputmode="decimal" style="width:100%;margin-top:8px;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
145
- <input id="tasks-pub-deadline" placeholder="截止 (+24h / +2h / +1d, 留空=桌面默认 24h)" style="width:100%;margin-top:8px;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
146
- <button class="sheet-choice" id="tasks-pub-submit" data-zh="发布公告(需手机确认 + 签名)" data-en="Publish announcement (needs on-device confirm + signature)" style="width:100%;margin-top:8px">发布公告(需手机确认 + 签名)</button>
147
- </div>
103
+ <!-- 对话框消息流 -->
104
+ <div class="task-chat" id="task-chat">
105
+ <div class="task-chat-empty" id="task-chat-empty">给 Bolloon 派个活… 例:查一下我的身份 / 汇总钱包状态</div>
106
+ </div>
148
107
 
149
- <div class="section-label" data-zh="群过程痕迹 (只读, 脱敏)" data-en="Group trail (read-only, redacted)">群过程痕迹 (只读, 脱敏)</div>
150
- <div class="list" id="tasks-trail-group-picker"></div>
151
- <div class="list" id="tasks-trail"><div class="sheet-text" style="padding:0 14px">选一个群看这一期的痕迹</div></div>
152
- <div style="padding:0 14px 8px">
153
- <button class="sheet-choice" id="tasks-trail-refresh" data-zh="读群痕迹" data-en="Read group trail" style="width:100%">读群痕迹</button>
154
- </div>
108
+ <!-- 底部派活输入框 -->
109
+ <div class="task-input-bar">
110
+ <input type="text" id="task-input" placeholder="给 Bolloon 派个活… (Enter 发送)" autocomplete="off" />
111
+ <button id="task-input-send">→ 派活</button>
112
+ </div>
155
113
 
156
- <div class="section-label" data-zh="飞轮进度 (只读, 与桌面同一状态)" data-en="Goal flywheel progress (read-only, same state as desktop)">飞轮进度 (只读, 与桌面同一状态)</div>
157
- <div class="list" id="tasks-flywheel"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
158
- </section>
114
+ <!-- 网络工具面板 (覆盖层, 默认隐藏; 点仪表盘「网络」打开) -->
115
+ <div class="task-network-panel" id="task-network-panel" hidden>
116
+ <div class="task-network-head">
117
+ <button id="task-network-back">←</button>
118
+ <span style="flex:1;font-weight:600">网络工具</span>
119
+ </div>
120
+ <div class="task-network-body">
121
+ <div class="list">
122
+ <div class="list-item" id="item-join-global"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/><path d="M12 3.5c2.7 2.6 2.7 14.4 0 17M12 3.5c-2.7 2.6-2.7 14.4 0 17"/><circle cx="12" cy="12" r="1.2" fill="currentColor" stroke="none"/></svg></span><span>一键入网 · 全球智能体网络</span><span class="list-arrow">›</span></div>
123
+ <div class="list-item" id="item-join-net"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M3.5 9.5a13 13 0 0 1 17 0M6.5 13a8.5 8.5 0 0 1 11 0M9.6 16.3a4 4 0 0 1 4.8 0"/><circle cx="12" cy="19.5" r="1.1" fill="currentColor" stroke="none"/></svg></span><span>加入网络</span><span class="list-arrow">›</span></div>
124
+ </div>
125
+ <div class="section-label">Agent 网络</div>
126
+ <div class="list" id="net-members"></div>
127
+ <div class="section-label">Agent 服务 (协议自动发现)</div>
128
+ <div class="list" id="agent-services"></div>
129
+ <div class="list"><div class="list-item" id="item-trade"><span class="list-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 7h16M4 12h10M4 17h7"/><path d="M17.5 14.5l2.5 2.5-2.5 2.5"/></svg></span><span style="flex:1">资源交易</span><span class="list-arrow">›</span></div></div>
130
+ <div class="section-label">微信息 (微支付)</div>
131
+ <div class="list" id="x402-info-list"></div>
132
+ <div class="section-label">智能体控制 (MCP / Skills)</div>
133
+ <div class="list" id="agent-control"></div>
134
+ <div class="section-label">待人工审批支付</div>
135
+ <div class="list" id="approval-list"></div>
136
+ <div class="section-label">A2UI 动态面板 (智能体生成)</div>
137
+ <div id="a2ui-root"></div>
138
+ <div class="section-label">本机授权与签名</div>
139
+ <div class="sheet-text" id="tasks-hint" style="font-size:12px;color:var(--text-secondary);padding:0 14px 6px"></div>
140
+ <div class="section-label">群 (与 CLI task group 同一份记录)</div>
141
+ <div class="list" id="tasks-groups"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
142
+ <div style="padding:0 14px 8px">
143
+ <input id="tasks-group-link" placeholder="群邀请链接 (orbitdb://…?type=group&amp;name=…)" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
144
+ <button class="sheet-choice" id="tasks-group-join" style="width:100%;margin-top:8px">入群(需手机确认 + 签名)</button>
145
+ <input id="tasks-group-name" placeholder="新群名称" style="width:100%;margin-top:8px;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" />
146
+ <button class="sheet-choice" id="tasks-group-create" style="width:100%;margin-top:8px">建群(需手机确认 + 签名)</button>
147
+ </div>
148
+ <div class="section-label">公告板 (与 CLI task board 同一份事实)</div>
149
+ <div class="board-grid" id="tasks-board"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
150
+ <div class="section-label">飞轮进度 (只读, 与桌面同一状态)</div>
151
+ <div class="list" id="tasks-flywheel"><div class="sheet-text" style="padding:0 14px">加载中…</div></div>
152
+ <div class="section-label">群过程痕迹 (只读, 脱敏)</div>
153
+ <div class="list" id="tasks-trail-group-picker"></div>
154
+ <div class="list" id="tasks-trail"><div class="sheet-text" style="padding:0 14px">选一个群看这一期的痕迹</div></div>
155
+ <div style="padding:0 14px 8px"><button class="sheet-choice" id="tasks-trail-refresh" style="width:100%">读群痕迹</button></div>
156
+ </div>
157
+ </div>
158
+ </section>
159
159
 
160
160
  <!-- 我 tab -->
161
161
  <section class="page" id="page-me" data-tab="me" hidden>
@@ -185,13 +185,12 @@
185
185
  <nav class="tabbar" id="tabbar">
186
186
  <button class="tab active" data-tab="main"><span class="tab-icon"><svg class="ico" viewBox="0 0 24 24"><rect x="3.5" y="3.5" width="7" height="7" rx="1.6"/><rect x="13.5" y="3.5" width="7" height="7" rx="1.6"/><rect x="3.5" y="13.5" width="7" height="7" rx="1.6"/><rect x="13.5" y="13.5" width="7" height="7" rx="1.6"/></svg></span><span>首页</span></button>
187
187
  <button class="tab" data-tab="friends"><span class="tab-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="9" cy="8" r="3.2"/><path d="M3.5 19c.8-3 3-4.6 5.5-4.6S13.7 16 14.5 19"/><path d="M17 8v6M14 11h6"/></svg></span><span>好友</span></button>
188
- <button class="tab" data-tab="network"><span class="tab-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5"/><path d="M3.5 12h17"/><path d="M12 3.5c2.3 2.3 3.5 5.3 3.5 8.5s-1.2 6.2-3.5 8.5c-2.3-2.3-3.5-5.3-3.5-8.5S9.7 5.8 12 3.5z"/></svg></span><span>网络</span></button>
188
+ <!-- 2026-10-06: 网络+任务合一, 任务 tab 内页内子切换 -->
189
189
  <button class="tab" data-tab="tasks"><span class="tab-icon"><svg class="ico" viewBox="0 0 24 24"><path d="M4 7.5h10M4 12h7M4 16.5h12"/><path d="M18 5.5l1.6 1.6L22 4.5"/><path d="M18 14.5l1.6 1.6L22 13.5"/></svg></span><span>任务</span></button>
190
190
  <button class="tab" data-tab="me"><span class="tab-icon"><svg class="ico" viewBox="0 0 24 24"><circle cx="12" cy="8.5" r="4"/><path d="M4.5 20.5a7.5 7.5 0 0 1 15 0"/></svg></span><span>我</span></button>
191
191
  </nav>
192
192
 
193
- <!-- 首页: 创建新会话栏 (底部中央, tab 上方) -->
194
- <button class="create-session-btn" id="btn-create-session" hidden>+ 创建新会话</button>
193
+ <!-- 首页: 创建新会话栏已移除 (2026-10-07: 首页改为世界机会流 + 底部输入框, 不再悬浮遮挡) -->
195
194
 
196
195
  <!-- 创建智能体: 底部滑入加载 sheet -->
197
196
  <!-- 2026-09-16: 首启隐私同意门 — 上架要求: 用户同意前不读取本机数据 / 不连网 / 不申请任何权限 -->