@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.
- package/dist/agents/chain/chain-settlement.js +0 -1
- package/dist/agents/chat-segmenter.js +4 -16
- package/dist/agents/contacts/human-directory.js +155 -0
- package/dist/agents/contacts/tools.js +79 -0
- package/dist/agents/experience-review.js +0 -3
- package/dist/agents/goal-flywheel/goal-change.js +3 -4
- package/dist/agents/goal-resume.js +0 -4
- package/dist/agents/intent-classifier.js +1 -1
- package/dist/agents/iteration-budget.js +12 -0
- package/dist/agents/knowledge-organizer.js +0 -2
- package/dist/agents/lesson-to-skill.js +0 -1
- package/dist/agents/loop-review.js +2 -2
- package/dist/agents/memory-store.js +359 -0
- package/dist/agents/opportunity-web-search.js +39 -0
- package/dist/agents/p2p-chat-tools.js +1 -2
- package/dist/agents/parse-tool-call.js +9 -22
- package/dist/agents/pi-harness.js +1 -2
- package/dist/agents/pi-sdk-tools.js +180 -5
- package/dist/agents/pi-sdk.js +143 -9
- package/dist/agents/process-runner.js +28 -9
- package/dist/agents/react-loop.js +9 -17
- package/dist/agents/runner-resolver.js +4 -3
- package/dist/agents/skill-loader.js +22 -15
- package/dist/agents/skills-manager.js +1 -25
- package/dist/agents/workflow-pivot-loop.js +152 -61
- package/dist/bootstrap/context-collector.js +2 -4
- package/dist/bootstrap/context-hierarchy.js +13 -1
- package/dist/cli/ink-app.js +44 -17
- package/dist/cli/intent-command.js +22 -12
- package/dist/cli/loading-tui.js +8 -2
- package/dist/cli/log-gate.js +40 -8
- package/dist/cli/mention-data.js +1 -0
- package/dist/cli/reply-hygiene.js +22 -0
- package/dist/cli/trace-line.js +61 -2
- package/dist/electron-build/agents/agent-gateway.js.map +1 -1
- package/dist/electron-build/agents/chain/chain-indexer.js.map +1 -1
- package/dist/electron-build/agents/chain/chain-settlement.js +0 -1
- package/dist/electron-build/agents/chain/chain-settlement.js.map +1 -1
- package/dist/electron-build/agents/contacts/chain.js.map +1 -1
- package/dist/electron-build/agents/contacts/human-directory.js +204 -0
- package/dist/electron-build/agents/contacts/human-directory.js.map +1 -0
- package/dist/electron-build/agents/contacts/policy.js.map +1 -1
- package/dist/electron-build/agents/contacts/tools.js +112 -0
- package/dist/electron-build/agents/contacts/tools.js.map +1 -1
- package/dist/electron-build/agents/experience-review.js +0 -3
- package/dist/electron-build/agents/experience-review.js.map +1 -1
- package/dist/electron-build/agents/external-events.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel/goal-change.js +3 -4
- package/dist/electron-build/agents/goal-flywheel/goal-change.js.map +1 -1
- package/dist/electron-build/agents/goal-flywheel-wiring.js.map +1 -1
- package/dist/electron-build/agents/goal-resume.js +0 -4
- package/dist/electron-build/agents/goal-resume.js.map +1 -1
- package/dist/electron-build/agents/intent-classifier.js +1 -1
- package/dist/electron-build/agents/lesson-to-skill.js +0 -1
- package/dist/electron-build/agents/lesson-to-skill.js.map +1 -1
- package/dist/electron-build/agents/loop-review.js +2 -2
- package/dist/electron-build/agents/loop-review.js.map +1 -1
- package/dist/electron-build/agents/memory-recall.js.map +1 -1
- package/dist/electron-build/agents/memory-store.js +404 -0
- package/dist/electron-build/agents/memory-store.js.map +1 -0
- package/dist/electron-build/agents/parse-tool-call.js +9 -22
- package/dist/electron-build/agents/parse-tool-call.js.map +1 -1
- package/dist/electron-build/agents/pi-harness.js +1 -2
- package/dist/electron-build/agents/pi-harness.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +180 -5
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +143 -9
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/agents/pre-tool-validator.js.map +1 -1
- package/dist/electron-build/agents/process-runner.js +28 -9
- package/dist/electron-build/agents/process-runner.js.map +1 -1
- package/dist/electron-build/agents/runner-resolver.js +4 -3
- package/dist/electron-build/agents/runner-resolver.js.map +1 -1
- package/dist/electron-build/agents/skill-loader.js +22 -16
- package/dist/electron-build/agents/skill-loader.js.map +1 -1
- package/dist/electron-build/agents/skill-supervisor-link.js.map +1 -1
- package/dist/electron-build/agents/skills-manager.js +0 -24
- package/dist/electron-build/agents/skills-manager.js.map +1 -1
- package/dist/electron-build/agents/task/task-runner.js.map +1 -1
- package/dist/electron-build/agents/workflow-engine.js.map +1 -1
- package/dist/electron-build/agents/workflow-pivot-loop.js +155 -62
- package/dist/electron-build/agents/workflow-pivot-loop.js.map +1 -1
- package/dist/electron-build/agents/x402/goal-run-bridge.js.map +1 -1
- package/dist/electron-build/bollharness-integration/gate-state-machine.js.map +1 -1
- package/dist/electron-build/bootstrap/context-collector.js +2 -4
- package/dist/electron-build/bootstrap/context-collector.js.map +1 -1
- package/dist/electron-build/bootstrap/context-hierarchy.js +13 -1
- package/dist/electron-build/bootstrap/context-hierarchy.js.map +1 -1
- package/dist/electron-build/electron-preload.js.map +1 -1
- package/dist/electron-build/external-engines/delegate.js.map +1 -1
- package/dist/electron-build/kernel/channel-actor.js +31 -2
- package/dist/electron-build/kernel/channel-actor.js.map +1 -1
- package/dist/electron-build/kernel/model-runtime.js +8 -1
- package/dist/electron-build/kernel/model-runtime.js.map +1 -1
- package/dist/electron-build/llm/config-store.js +20 -0
- package/dist/electron-build/llm/config-store.js.map +1 -1
- package/dist/electron-build/llm/embedding-adapter.js +75 -0
- package/dist/electron-build/llm/embedding-adapter.js.map +1 -0
- package/dist/electron-build/llm/pi-ai.js +96 -35
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/electron-build/network/agent-network.js.map +1 -1
- package/dist/electron-build/orbitdb/memory-sync.js +76 -0
- package/dist/electron-build/orbitdb/memory-sync.js.map +1 -0
- package/dist/electron-build/pi-ecosystem-judgment/adaptive-scan.js.map +1 -1
- package/dist/electron-build/security/tool-gate.js +2 -0
- package/dist/electron-build/security/tool-gate.js.map +1 -1
- package/dist/electron-build/setup/setup-store.js +34 -6
- package/dist/electron-build/setup/setup-store.js.map +1 -1
- package/dist/electron-build/social/heartbeat.js +18 -5
- package/dist/electron-build/social/heartbeat.js.map +1 -1
- package/dist/electron-build/utils/update-manager.js.map +1 -1
- package/dist/index.js +250 -12
- package/dist/ios/bolloon-web.json +2 -2
- package/dist/ios/client.js +74 -35
- package/dist/ios/icons/icon-1400x1400.png +0 -0
- package/dist/ios/icons/icon-1400x1400.webp +0 -0
- package/dist/ios/index.html +106 -107
- package/dist/ios/mobile-agent.js +346 -45
- package/dist/ios/mobile-core.js +7303 -4590
- package/dist/ios/mobile-harness.js +1004 -0
- package/dist/ios/mobile-orbitdb.js +194 -0
- package/dist/ios/mobile-persona.js +80 -0
- package/dist/ios/mobile-privacy.js +2 -3
- package/dist/ios/mobile.css +286 -21
- package/dist/ios/mobile.html +106 -107
- package/dist/ios/mobile.js +300 -132
- package/dist/ios/routes-world.js +4 -1
- package/dist/ios/server.js +3 -1
- package/dist/ios/style.css +13 -5
- package/dist/ios/ui/message-renderer.js +4 -18
- package/dist/ios/ui/step-timeline.js +14 -1
- package/dist/kernel/channel-actor.js +30 -2
- package/dist/kernel/gate-scan.js +1 -1
- package/dist/kernel/index.js +33 -0
- package/dist/kernel/model-runtime.js +8 -1
- package/dist/kernel/plan.js +3 -3
- package/dist/kernel/roster.js +3 -3
- package/dist/llm/config-store.js +20 -0
- package/dist/llm/embedding-adapter.js +69 -0
- package/dist/llm/pi-ai.js +94 -34
- package/dist/orbitdb/memory-sync.js +70 -0
- package/dist/pi-ecosystem-judgment/adaptive-scan.js +1 -1
- package/dist/security/tool-gate.js +2 -0
- package/dist/setup/setup-store.js +34 -6
- package/dist/social/heartbeat.js +18 -5
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +74 -35
- package/dist/web/icons/icon-1400x1400.png +0 -0
- package/dist/web/icons/icon-1400x1400.webp +0 -0
- package/dist/web/index.html +37 -25
- package/dist/web/mobile-agent.js +351 -45
- package/dist/web/mobile-contacts.js +0 -4
- package/dist/web/mobile-core.js +7430 -4590
- package/dist/web/mobile-harness.js +1004 -0
- package/dist/web/mobile-orbitdb.js +194 -0
- package/dist/web/mobile-persona.js +80 -0
- package/dist/web/mobile-privacy.js +2 -3
- package/dist/web/mobile.css +286 -21
- package/dist/web/mobile.html +121 -107
- package/dist/web/mobile.js +409 -136
- package/dist/web/routes-contacts.js +94 -1
- package/dist/web/routes-world.js +4 -1
- package/dist/web/server.js +4 -2
- package/dist/web/style.css +13 -5
- package/dist/web/sw.js +1 -1
- package/dist/web/ui/message-renderer.js +4 -18
- package/dist/web/ui/step-timeline.js +14 -1
- package/package.json +3 -2
- package/scripts/build-web.ts +16 -1
package/dist/ios/mobile.js
CHANGED
|
@@ -69,21 +69,66 @@
|
|
|
69
69
|
});
|
|
70
70
|
} catch (e) {}
|
|
71
71
|
|
|
72
|
-
const TITLES = { main: '首页', friends: '好友',
|
|
72
|
+
const TITLES = { main: '首页', friends: '好友', tasks: '任务', me: '我' };
|
|
73
73
|
let currentTab = 'main';
|
|
74
74
|
function switchTab(tab) {
|
|
75
75
|
currentTab = tab;
|
|
76
76
|
$$('.page, .page-container').forEach((p) => { p.hidden = p.dataset.tab !== tab; });
|
|
77
77
|
$$('.tab').forEach((t) => t.classList.toggle('active', t.dataset.tab === tab));
|
|
78
78
|
$('#topbar-title').textContent = TITLES[tab] || '会话';
|
|
79
|
-
const cs = $('#btn-create-session'); if (cs) cs.hidden = tab !== 'main';
|
|
80
79
|
const ta = $('#topbar-actions'); if (ta) ta.hidden = tab === 'me'; // 我 页不显示 加号/刷新
|
|
81
80
|
if (tab === 'friends') { loadContacts(); loadP2PStatus(); }
|
|
82
|
-
|
|
81
|
+
// 2026-10-06: 网络 + 任务合一 —— 任务 tab 同时加载网络与任务数据
|
|
82
|
+
if (tab === 'tasks') { loadAgentControl(); loadApprovals(); loadNetMembers(); loadAgentServices(); loadX402Info(); try { refreshAll(); } catch (e) {} }
|
|
83
83
|
if (tab === 'main') { loadAgentCovers(); }
|
|
84
84
|
window.__mobileTouch?.('tab', tab);
|
|
85
85
|
}
|
|
86
86
|
$$('.tab').forEach((t) => t.addEventListener('click', () => switchTab(t.dataset.tab)));
|
|
87
|
+
// 2026-10-07: 任务 tab 对话框模式 — 网络收成仪表盘工具 (覆盖层开关)
|
|
88
|
+
function openTaskNetwork() {
|
|
89
|
+
const p = $('#task-network-panel'); if (p) p.hidden = false;
|
|
90
|
+
// 打开时加载网络数据
|
|
91
|
+
try { loadAgentControl(); loadApprovals(); loadNetMembers(); loadAgentServices(); loadX402Info(); } catch (e) {}
|
|
92
|
+
}
|
|
93
|
+
function closeTaskNetwork() {
|
|
94
|
+
const p = $('#task-network-panel'); if (p) p.hidden = true;
|
|
95
|
+
}
|
|
96
|
+
const dashNet = $('#task-dash-network'); if (dashNet) dashNet.addEventListener('click', openTaskNetwork);
|
|
97
|
+
const netBack = $('#task-network-back'); if (netBack) netBack.addEventListener('click', closeTaskNetwork);
|
|
98
|
+
|
|
99
|
+
// 任务对话框: 派活 → runLocalAgent (手机自治循环, 回显到消息流)
|
|
100
|
+
async function sendTask() {
|
|
101
|
+
const input = $('#task-input');
|
|
102
|
+
if (!input) return;
|
|
103
|
+
const text = input.value.trim();
|
|
104
|
+
if (!text) return;
|
|
105
|
+
input.value = '';
|
|
106
|
+
appendTaskMsg('user', text);
|
|
107
|
+
const empty = $('#task-chat-empty'); if (empty) empty.hidden = true;
|
|
108
|
+
try {
|
|
109
|
+
const a = window.BolloonCore && window.BolloonCore.agent;
|
|
110
|
+
let reply;
|
|
111
|
+
if (a && a.runLocal) reply = await a.runLocal(text);
|
|
112
|
+
else if (a && a.runLoop) reply = await a.runLoop(text);
|
|
113
|
+
else reply = '手机 Agent 未就绪';
|
|
114
|
+
appendTaskMsg('ai', String(reply || '(无回复)'));
|
|
115
|
+
} catch (e) {
|
|
116
|
+
appendTaskMsg('ai', '[执行失败] ' + (e.message || e));
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
function appendTaskMsg(role, text) {
|
|
120
|
+
const chat = $('#task-chat');
|
|
121
|
+
if (!chat) return;
|
|
122
|
+
const el = document.createElement('div');
|
|
123
|
+
el.className = 'task-msg ' + (role === 'user' ? 'task-msg-user' : 'task-msg-ai');
|
|
124
|
+
el.textContent = text;
|
|
125
|
+
chat.appendChild(el);
|
|
126
|
+
chat.scrollTop = chat.scrollHeight;
|
|
127
|
+
}
|
|
128
|
+
const taskInput = $('#task-input');
|
|
129
|
+
const taskSend = $('#task-input-send');
|
|
130
|
+
if (taskSend) taskSend.addEventListener('click', sendTask);
|
|
131
|
+
if (taskInput) taskInput.addEventListener('keydown', (e) => { if (e.key === 'Enter') sendTask(); });
|
|
87
132
|
|
|
88
133
|
async function loadConversations() {
|
|
89
134
|
try {
|
|
@@ -114,26 +159,58 @@
|
|
|
114
159
|
}
|
|
115
160
|
|
|
116
161
|
async function loadContacts() {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
162
|
+
try {
|
|
163
|
+
let peers = [];
|
|
164
|
+
try { peers = await api.get('/api/peers'); } catch { peers = []; }
|
|
165
|
+
const list = $('#contacts-list');
|
|
166
|
+
if (!list) return;
|
|
167
|
+
list.innerHTML = '';
|
|
168
|
+
if (!Array.isArray(peers) || peers.length === 0) {
|
|
169
|
+
list.innerHTML = '<div style="padding:20px;text-align:center;color:var(--text-muted)">暂无好友</div>';
|
|
170
|
+
const idx = $('#contacts-index'); if (idx) idx.innerHTML = '';
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
// 通讯录式: 按首字母分组 (2026-10-07, 微信通讯录风格)
|
|
174
|
+
const groups = {};
|
|
175
|
+
peers.forEach((p) => {
|
|
176
|
+
const name = p.name || p.publicKey?.slice(0, 12) || '好友';
|
|
177
|
+
const letter = (name.charAt(0) || '#').toUpperCase();
|
|
178
|
+
const g = /[A-Z]/.test(letter) ? letter : '#';
|
|
179
|
+
if (!groups[g]) groups[g] = [];
|
|
180
|
+
groups[g].push({ name, peer: p });
|
|
181
|
+
});
|
|
182
|
+
const letters = Object.keys(groups).sort((a, b) => (a === '#' ? 1 : b === '#' ? -1 : a.localeCompare(b)));
|
|
183
|
+
letters.forEach((letter) => {
|
|
184
|
+
const groupEl = document.createElement('div');
|
|
185
|
+
groupEl.className = 'contacts-group';
|
|
186
|
+
groupEl.setAttribute('data-letter', letter);
|
|
187
|
+
const head = document.createElement('div');
|
|
188
|
+
head.className = 'contacts-group-head';
|
|
189
|
+
head.textContent = letter;
|
|
190
|
+
groupEl.appendChild(head);
|
|
191
|
+
groups[letter].forEach((c) => {
|
|
192
|
+
const el = document.createElement('div');
|
|
193
|
+
el.className = 'conv-item contact-row';
|
|
194
|
+
el.innerHTML = `<div class="conv-avatar contact-avatar">${escapeHtml(c.name.charAt(0))}</div>
|
|
195
|
+
<div class="conv-body"><div class="conv-name">${escapeHtml(c.name)}</div>
|
|
196
|
+
<div class="contact-sub">${c.peer.online ? '在线' : '离线'}${c.peer.publicKey ? ' · ' + escapeHtml(String(c.peer.publicKey).slice(0, 10)) + '…' : ''}</div></div>`;
|
|
197
|
+
groupEl.appendChild(el);
|
|
198
|
+
});
|
|
199
|
+
list.appendChild(groupEl);
|
|
200
|
+
});
|
|
201
|
+
// 右侧字母索引条
|
|
202
|
+
const idx = $('#contacts-index');
|
|
203
|
+
if (idx) {
|
|
204
|
+
idx.innerHTML = letters.map((l) => `<div class="contacts-index-letter" data-letter="${l}">${l === '#' ? '#' : l}</div>`).join('');
|
|
205
|
+
idx.querySelectorAll('.contacts-index-letter').forEach((el) => {
|
|
206
|
+
el.addEventListener('click', () => {
|
|
207
|
+
const target = list.querySelector(`.contacts-group[data-letter="${el.dataset.letter}"]`);
|
|
208
|
+
if (target) target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
|
209
|
+
});
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
} catch (e) { /* 忽略 */ }
|
|
213
|
+
}
|
|
137
214
|
|
|
138
215
|
// 智能体控制 = MCP 控制 + Skills 控制 (不是屏幕触控!)
|
|
139
216
|
// - MCP 工具: 真正的工具调用 (gateway_status / join / register …)
|
|
@@ -515,16 +592,20 @@
|
|
|
515
592
|
const cards = $('#world-feed-cards');
|
|
516
593
|
if (!feed || !cards) return;
|
|
517
594
|
try {
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
const data = await
|
|
521
|
-
const opps = (data.opportunities || []).slice(0, 6);
|
|
595
|
+
// 2026-10-05 (leo: 手机端 ↔ 电脑端互联): 走 api 层 → BolloonCore.resolve
|
|
596
|
+
// 转发电脑端 desktopUrl (不再裸 fetch 相对路径 — 手机上相对路径连手机自己, 永远空)
|
|
597
|
+
const data = await api.get('/api/opportunities?min-score=0.2&limit=10');
|
|
598
|
+
const opps = ((data && data.opportunities) || []).slice(0, 6);
|
|
522
599
|
if (!opps.length) { feed.hidden = true; return; }
|
|
523
600
|
feed.hidden = false;
|
|
524
601
|
worldFeedShown = true;
|
|
525
602
|
cards.innerHTML = opps.map((o) => {
|
|
526
603
|
const pct = Math.round(o.score * 100);
|
|
527
|
-
|
|
604
|
+
// 2026-10-05: 来源标签 (与电脑端同口径) — ✉ 外部投递 / 🔎 AI 搜索 / ○ 世界变化
|
|
605
|
+
let tag = '世界变化';
|
|
606
|
+
if (o.reason === 'inbox') tag = '✉ 外部投递';
|
|
607
|
+
else if (o.reason === 'search') tag = '🔎 AI 搜索';
|
|
608
|
+
else if (o.reason === 'match') tag = '与你匹配';
|
|
528
609
|
return `<div class="world-feed-card">
|
|
529
610
|
<div class="world-feed-card-top">
|
|
530
611
|
<span class="world-feed-title">${escapeHtml(String(o.title || '').slice(0, 34))}</span>
|
|
@@ -541,7 +622,7 @@
|
|
|
541
622
|
btn.addEventListener('click', async () => {
|
|
542
623
|
const id = btn.dataset.wfId;
|
|
543
624
|
const action = btn.dataset.wfAction;
|
|
544
|
-
try { await
|
|
625
|
+
try { await api.post('/api/opportunities/' + id + '/' + action, {}); } catch (e) {}
|
|
545
626
|
const card = btn.closest('.world-feed-card');
|
|
546
627
|
if (card) {
|
|
547
628
|
card.style.opacity = '0.3';
|
|
@@ -554,12 +635,41 @@
|
|
|
554
635
|
feed.hidden = true; // 拉不到就不显示世界区 (不阻塞首页)
|
|
555
636
|
}
|
|
556
637
|
// 持续流入: 世界区可见时每 45s 自动刷新 (AI 持续观察, 不是人刷新)
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
638
|
+
if (feed && !feed.hidden && worldFeedShown && !worldFeedTimer) {
|
|
639
|
+
worldFeedTimer = setInterval(() => { loadWorldFeed(); }, 45000);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// === 2026-10-07: 手机首页底部输入框 — 告诉世界你在做什么 (对应桌面世界模式) ===
|
|
644
|
+
async function tellWorld() {
|
|
645
|
+
const input = $('#world-input');
|
|
646
|
+
if (!input) return;
|
|
647
|
+
const text = input.value.trim();
|
|
648
|
+
if (!text) return;
|
|
649
|
+
input.value = '';
|
|
650
|
+
try {
|
|
651
|
+
// 声明意图 (与桌面同 API; 桌面不可达时走 BolloonCore.resolve 转发)
|
|
652
|
+
const res = await api.post('/api/intents', { text, priority: 3 });
|
|
653
|
+
if (!res || !res.ok) console.info('[world] 声明意图失败');
|
|
654
|
+
// 输入即主动搜索 (leo: 声明后立刻搜机会)
|
|
655
|
+
try { await api.post('/api/world/search', { topic: text }); } catch (e) {}
|
|
656
|
+
// 刷新机会流
|
|
657
|
+
await loadWorldFeed();
|
|
658
|
+
if (window.showToast) showToast('🌍 已告诉世界,正在为你匹配机会…');
|
|
659
|
+
} catch (e) {
|
|
660
|
+
console.info('[world] 声明失败', e);
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
function bindWorldInput() {
|
|
664
|
+
const input = $('#world-input');
|
|
665
|
+
const send = $('#world-input-send');
|
|
666
|
+
if (input) input.addEventListener('keydown', (e) => { if (e.key === 'Enter') tellWorld(); });
|
|
667
|
+
if (send) send.addEventListener('click', tellWorld);
|
|
668
|
+
}
|
|
669
|
+
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', bindWorldInput);
|
|
670
|
+
else bindWorldInput();
|
|
561
671
|
|
|
562
|
-
|
|
672
|
+
// === 卡片封面 (docs/fig 导出 → src/web/covers, 每个 agent 唯一不重复) ===
|
|
563
673
|
let _coverList = null;
|
|
564
674
|
async function loadCovers() {
|
|
565
675
|
if (_coverList) return _coverList;
|
|
@@ -1935,11 +2045,11 @@
|
|
|
1935
2045
|
<div class="conv-item" id="settings-data"><span class="list-icon">${ICONS.chip}</span><span style="flex:1;min-width:0"><span style="display:block">本机数据</span><span class="conv-preview" style="display:block">读取中…</span></span><span class="list-arrow">›</span></div>
|
|
1936
2046
|
<div class="conv-item" id="settings-desktop"><span class="list-icon">${ICONS.globe}</span><span>电脑端同步</span><span class="list-arrow">›</span></div>
|
|
1937
2047
|
<div class="conv-item" id="settings-chain"><span class="list-icon">${ICONS.chip}</span><span>链上配置 (RPC/网络)</span><span class="list-arrow">›</span></div>
|
|
1938
|
-
<div class="conv-item" id="settings-accessibility"><span class="list-icon">${ICONS.globe}</span><span>完全访问权限<span class="conv-preview" style="display:block" id="access-preview">默认开启</span></span><span class="list-arrow">›</span></div>
|
|
1939
2048
|
<div class="conv-item" id="settings-ipfs"><span class="list-icon">${ICONS.chip}</span><span>IPFS 存储</span><span class="list-arrow">›</span></div>
|
|
1940
2049
|
<div class="conv-item" id="settings-helia"><span class="list-icon">${ICONS.globe}</span><span>本机 IPFS 节点</span><span class="list-arrow">›</span></div>
|
|
1941
2050
|
<div class="conv-item" id="settings-selfcard"><span class="list-icon">${ICONS.chip}</span><span id="selfcard-text">显示本机卡片: 开</span></div>
|
|
1942
|
-
<div class="conv-item" id="settings-did"><span class="list-icon">${ICONS.idcard}</span><span>DID
|
|
2051
|
+
<div class="conv-item" id="settings-did"><span class="list-icon">${ICONS.idcard}</span><span>DID 与身份</span><span class="list-arrow">›</span></div>
|
|
2052
|
+
<div class="conv-item" id="settings-persona"><span class="list-icon">${ICONS.chip}</span><span>智能体性格</span><span class="list-arrow">›</span></div>
|
|
1943
2053
|
<div class="conv-item" id="settings-privacy"><span class="list-icon">${ICONS.chip}</span><span>隐私政策与个人信息</span><span class="list-arrow">›</span></div>
|
|
1944
2054
|
<div class="conv-item" id="settings-wipe"><span class="list-icon">${ICONS.trash}</span><span style="flex:1;min-width:0"><span style="display:block">清除本机数据(注销)</span><span class="conv-preview" style="display:block">删除本机身份、智能体、会话与钱包</span></span><span class="list-arrow">›</span></div>
|
|
1945
2055
|
<div class="conv-item" id="settings-filing"><span class="list-icon">${ICONS.idcard}</span><span id="filing-text">APP 备案号:备案办理中</span></div>
|
|
@@ -1968,48 +2078,6 @@
|
|
|
1968
2078
|
const next = currentTheme === 'auto' ? 'light' : (currentTheme === 'light' ? 'dark' : 'auto');
|
|
1969
2079
|
applyTheme(next, true);
|
|
1970
2080
|
});
|
|
1971
|
-
// 完全访问权限 (屏幕点按/滑动, 电脑端 phone.tap / phone.swipe 的前提) — 默认开启
|
|
1972
|
-
const ACCESS_AUTO_KEY = 'bolloon_access_auto_guided';
|
|
1973
|
-
const refreshAccessPreview = async () => {
|
|
1974
|
-
const el = document.querySelector('#access-preview');
|
|
1975
|
-
if (!el) return;
|
|
1976
|
-
const cap = window.Capacitor;
|
|
1977
|
-
const b = cap && cap.Plugins && cap.Plugins.RokidBridge;
|
|
1978
|
-
if (!b) { el.textContent = '仅手机 App'; return; }
|
|
1979
|
-
let st = {};
|
|
1980
|
-
try { st = (await b.touchStatus()) || {}; } catch (e) { st = {}; }
|
|
1981
|
-
el.textContent = st.ready ? '已开启' : '未开启 · 点这里开';
|
|
1982
|
-
};
|
|
1983
|
-
void refreshAccessPreview();
|
|
1984
|
-
// 默认开启: 首次启动自动把用户带到系统开关页 (只引导一次, 不反复打扰)
|
|
1985
|
-
void (async () => {
|
|
1986
|
-
try {
|
|
1987
|
-
const cap = window.Capacitor;
|
|
1988
|
-
const b = cap && cap.Plugins && cap.Plugins.RokidBridge;
|
|
1989
|
-
if (!b) return;
|
|
1990
|
-
let st = {};
|
|
1991
|
-
try { st = (await b.touchStatus()) || {}; } catch (e) { return; }
|
|
1992
|
-
if (st.ready) return;
|
|
1993
|
-
if (localStorage.getItem(ACCESS_AUTO_KEY) === '1') return;
|
|
1994
|
-
localStorage.setItem(ACCESS_AUTO_KEY, '1');
|
|
1995
|
-
await b.openAccessibilitySettings();
|
|
1996
|
-
} catch (e) {}
|
|
1997
|
-
})();
|
|
1998
|
-
$('#settings-accessibility').addEventListener('click', async () => {
|
|
1999
|
-
const cap = window.Capacitor;
|
|
2000
|
-
const bridge = cap && cap.Plugins && cap.Plugins.RokidBridge;
|
|
2001
|
-
if (!bridge) { alert('完全访问权限只在手机 App 里有:电脑端 / 网页版没有这项。'); return; }
|
|
2002
|
-
let st = {};
|
|
2003
|
-
try { st = (await bridge.touchStatus()) || {}; } catch (e) { st = {}; }
|
|
2004
|
-
if (st.ready) { alert('完全访问权限已开启:智能体可以在这台手机上点按、滑动屏幕(电脑端也能远程帮你点)。\n\n想关掉:系统设置 → 辅助功能 → 已开启的服务 → Bolloon Agent,关掉即可。'); return; }
|
|
2005
|
-
try {
|
|
2006
|
-
alert('马上打开系统设置,把「Bolloon Agent」的完全访问权限打开。\n(这一项在有些手机叫「辅助功能」,有些叫「无障碍」,指同一个开关;打开后一直有效,随时可以关掉。)');
|
|
2007
|
-
await bridge.openAccessibilitySettings();
|
|
2008
|
-
void refreshAccessPreview();
|
|
2009
|
-
}
|
|
2010
|
-
catch (e) { alert('没打开成功,请手动去:系统设置 → 辅助功能 → 已开启的服务 → Bolloon Agent,打开即可。'); }
|
|
2011
|
-
});
|
|
2012
|
-
|
|
2013
2081
|
// 本机数据: 直接读 IndexedDB 快照 (智能体/会话/消息都在本机, 重开 App 不会丢)
|
|
2014
2082
|
const dataEl = $('#settings-data');
|
|
2015
2083
|
if (dataEl) {
|
|
@@ -2063,7 +2131,121 @@
|
|
|
2063
2131
|
loadAgentCovers();
|
|
2064
2132
|
showToast(hidden ? '已显示本机卡片' : '已隐藏本机卡片');
|
|
2065
2133
|
});
|
|
2066
|
-
$('#settings-did').addEventListener('click', () =>
|
|
2134
|
+
$('#settings-did').addEventListener('click', () => void openIdentityPage());
|
|
2135
|
+
$('#settings-persona').addEventListener('click', () => void openPersonaPage());
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
// === 身份页 (DID / 公钥 / 签名测试 / 邮箱 / 手机号) ===
|
|
2139
|
+
// 2026-10-07: 完整身份系统 — DIAP did:key (与桌面同构) + 附带条件 (邮箱/手机号/备注)
|
|
2140
|
+
async function openIdentityPage() {
|
|
2141
|
+
if ($('#identity-page')) return;
|
|
2142
|
+
const page = mkOverlayPage('identity-page', 'DID 与身份', '<div style="font-size:12px;color:var(--text-muted)">读取中…</div>');
|
|
2143
|
+
const body = page.querySelector('#identity-page-body');
|
|
2144
|
+
try {
|
|
2145
|
+
const core = window.BolloonCore || (await import('./mobile-core.js')).core;
|
|
2146
|
+
const ag = core && core.agent;
|
|
2147
|
+
const st = await ag.identityStatus();
|
|
2148
|
+
body.innerHTML = `
|
|
2149
|
+
<div class="list">
|
|
2150
|
+
<div class="list-item"><span class="list-icon">${ICONS.idcard}</span><span style="flex:1"><span style="display:block">DID</span><span class="conv-preview" style="display:block;word-break:break-all">${escapeHtml(st.did || '未生成')}</span></span></div>
|
|
2151
|
+
<div class="list-item"><span class="list-icon">${ICONS.chip}</span><span style="flex:1"><span style="display:block">公钥 (Ed25519)</span><span class="conv-preview" style="display:block;word-break:break-all">${st.publicKey ? escapeHtml(st.publicKey.slice(0, 32) + '…') : '(老设备哈希身份,无签名)'}</span></span></div>
|
|
2152
|
+
<div class="list-item"><span class="list-icon">${ICONS.chip}</span><span style="flex:1"><span style="display:block">名字</span><span class="conv-preview" style="display:block">${escapeHtml(st.name || '未登录')}</span></span></div>
|
|
2153
|
+
</div>
|
|
2154
|
+
<div class="section-label">附带身份条件(仅存本机)</div>
|
|
2155
|
+
<div class="list">
|
|
2156
|
+
<div class="list-item"><span class="list-icon">✉️</span><span style="flex:1"><span style="display:block">邮箱</span><span class="conv-preview" id="id-email" style="display:block">${escapeHtml(st.email || '未设置')}</span></span></div>
|
|
2157
|
+
<div class="list-item"><span class="list-icon">📱</span><span style="flex:1"><span style="display:block">手机号</span><span class="conv-preview" id="id-phone" style="display:block">${escapeHtml(st.phone || '未设置')}</span></span></div>
|
|
2158
|
+
<div class="list-item"><span class="list-icon">📝</span><span style="flex:1"><span style="display:block">备注</span><span class="conv-preview" id="id-note" style="display:block">${escapeHtml(st.note || '未设置')}</span></span></div>
|
|
2159
|
+
<button class="sheet-choice" id="id-edit" style="width:100%;margin-top:8px">编辑邮箱 / 手机号 / 备注</button>
|
|
2160
|
+
</div>
|
|
2161
|
+
<div class="section-label">签名能力(DIAP Ed25519)</div>
|
|
2162
|
+
<div class="list">
|
|
2163
|
+
<button class="sheet-choice" id="id-sign-test" style="width:100%">测试签名(本地签 + 验,证明身份可签名)</button>
|
|
2164
|
+
</div>`;
|
|
2165
|
+
const editBtn = page.querySelector('#id-edit');
|
|
2166
|
+
if (editBtn) editBtn.addEventListener('click', () => {
|
|
2167
|
+
const email = prompt('邮箱:', st.email || '');
|
|
2168
|
+
if (email === null) return;
|
|
2169
|
+
const phone = prompt('手机号:', st.phone || '');
|
|
2170
|
+
if (phone === null) return;
|
|
2171
|
+
const note = prompt('备注:', st.note || '');
|
|
2172
|
+
if (note === null) return;
|
|
2173
|
+
void (async () => {
|
|
2174
|
+
const updated = await ag.updateProfile({ email, phone, note });
|
|
2175
|
+
const e = page.querySelector('#id-email'); if (e) e.textContent = updated.email || '未设置';
|
|
2176
|
+
const p = page.querySelector('#id-phone'); if (p) p.textContent = updated.phone || '未设置';
|
|
2177
|
+
const n = page.querySelector('#id-note'); if (n) n.textContent = updated.note || '未设置';
|
|
2178
|
+
showToast('✅ 身份条件已更新');
|
|
2179
|
+
})();
|
|
2180
|
+
});
|
|
2181
|
+
const signBtn = page.querySelector('#id-sign-test');
|
|
2182
|
+
if (signBtn) signBtn.addEventListener('click', () => {
|
|
2183
|
+
void (async () => {
|
|
2184
|
+
signBtn.textContent = '签名中…';
|
|
2185
|
+
try {
|
|
2186
|
+
const s = await ag.sign('bolloon-mobile-sign-test');
|
|
2187
|
+
const ok = await ag.verifySign('bolloon-mobile-sign-test', s.signature, s.publicKey);
|
|
2188
|
+
signBtn.textContent = ok ? `✅ 签名+验签通过 (${s.did.slice(0, 16)}…)` : '❌ 验签失败';
|
|
2189
|
+
} catch (e) {
|
|
2190
|
+
signBtn.textContent = '签名不可用: ' + String((e && e.message) || e).slice(0, 40);
|
|
2191
|
+
}
|
|
2192
|
+
})();
|
|
2193
|
+
});
|
|
2194
|
+
} catch (e) {
|
|
2195
|
+
body.innerHTML = '<div style="padding:20px;color:var(--text-muted)">身份读取失败: ' + escapeHtml(String((e && e.message) || e)) + '</div>';
|
|
2196
|
+
}
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
// === 智能体性格页 (Personal AI) ===
|
|
2200
|
+
// 2026-10-07: mobile-persona 配置文件 — 性格/价值观/兴趣/说话方式, 注入 agent 系统提示
|
|
2201
|
+
async function openPersonaPage() {
|
|
2202
|
+
if ($('#persona-page')) return;
|
|
2203
|
+
const page = mkOverlayPage('persona-page', '智能体性格', '<div style="font-size:12px;color:var(--text-muted)">读取中…</div>');
|
|
2204
|
+
const body = page.querySelector('#persona-page-body');
|
|
2205
|
+
try {
|
|
2206
|
+
const persona = await import('./mobile-persona.js');
|
|
2207
|
+
const p = persona.loadPersona();
|
|
2208
|
+
body.innerHTML = `
|
|
2209
|
+
<div style="padding:14px;font-size:12px;color:var(--text-muted);line-height:1.7">配置手机智能体的「性格文件」—— 会注入到每次对话的系统提示里,决定它怎么说话、看重什么、不做什么。存本机。</div>
|
|
2210
|
+
<div class="section-label">名字</div>
|
|
2211
|
+
<div style="padding:0 14px"><input id="ps-name" value="${escapeHtml(p.name)}" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2212
|
+
<div class="section-label">性格</div>
|
|
2213
|
+
<div style="padding:0 14px"><input id="ps-personality" value="${escapeHtml(p.personality)}" placeholder="例如:活泼、严谨、毒舌…" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2214
|
+
<div class="section-label">说话方式</div>
|
|
2215
|
+
<div style="padding:0 14px"><input id="ps-style" value="${escapeHtml(p.style)}" placeholder="例如:简洁中文,先结论后细节" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2216
|
+
<div class="section-label">价值观(逗号分隔)</div>
|
|
2217
|
+
<div style="padding:0 14px"><input id="ps-values" value="${escapeHtml(p.values.join('、'))}" placeholder="本地优先、隐私优先…" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2218
|
+
<div class="section-label">兴趣(逗号分隔)</div>
|
|
2219
|
+
<div style="padding:0 14px"><input id="ps-interests" value="${escapeHtml(p.interests.join('、'))}" placeholder="P2P、AI、知识系统…" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2220
|
+
<div class="section-label">不做的事(逗号分隔)</div>
|
|
2221
|
+
<div style="padding:0 14px"><input id="ps-boundaries" value="${escapeHtml(p.boundaries.join('、'))}" placeholder="不编造数据、不假装能做…" style="width:100%;padding:10px 12px;border-radius:10px;border:1px solid var(--border);background:transparent;color:var(--text);font-size:14px" /></div>
|
|
2222
|
+
<div style="padding:14px">
|
|
2223
|
+
<button class="sheet-choice" id="ps-save" style="width:100%">保存性格</button>
|
|
2224
|
+
<button class="sheet-choice sheet-cancel" id="ps-reset" style="width:100%;margin-top:8px">恢复默认</button>
|
|
2225
|
+
</div>`;
|
|
2226
|
+
const saveBtn = page.querySelector('#ps-save');
|
|
2227
|
+
if (saveBtn) saveBtn.addEventListener('click', () => {
|
|
2228
|
+
const v = (id) => { const el = page.querySelector(id); return el ? String(el.value || '').trim() : ''; };
|
|
2229
|
+
persona.savePersona({
|
|
2230
|
+
name: v('#ps-name'),
|
|
2231
|
+
personality: v('#ps-personality'),
|
|
2232
|
+
style: v('#ps-style'),
|
|
2233
|
+
values: v('#ps-values').split(/[、,]/).map((s) => s.trim()).filter(Boolean),
|
|
2234
|
+
interests: v('#ps-interests').split(/[、,]/).map((s) => s.trim()).filter(Boolean),
|
|
2235
|
+
boundaries: v('#ps-boundaries').split(/[、,]/).map((s) => s.trim()).filter(Boolean),
|
|
2236
|
+
});
|
|
2237
|
+
showToast('✅ 性格已保存,下次对话生效');
|
|
2238
|
+
});
|
|
2239
|
+
const resetBtn = page.querySelector('#ps-reset');
|
|
2240
|
+
if (resetBtn) resetBtn.addEventListener('click', () => {
|
|
2241
|
+
persona.resetPersona();
|
|
2242
|
+
showToast('已恢复默认性格');
|
|
2243
|
+
page.remove();
|
|
2244
|
+
void openPersonaPage();
|
|
2245
|
+
});
|
|
2246
|
+
} catch (e) {
|
|
2247
|
+
body.innerHTML = '<div style="padding:20px;color:var(--text-muted)">性格读取失败: ' + escapeHtml(String((e && e.message) || e)) + '</div>';
|
|
2248
|
+
}
|
|
2067
2249
|
}
|
|
2068
2250
|
|
|
2069
2251
|
// === 模型配置 (供应商 / 模型 / 地址) ===
|
|
@@ -2525,23 +2707,10 @@
|
|
|
2525
2707
|
}
|
|
2526
2708
|
}
|
|
2527
2709
|
|
|
2528
|
-
// 创建智能体:
|
|
2529
|
-
async function createSession() {
|
|
2530
|
-
showSheet('#create-sheet');
|
|
2531
|
-
try {
|
|
2532
|
-
await api.post('/api/channels/create', {});
|
|
2533
|
-
await new Promise((r) => setTimeout(r, 700));
|
|
2534
|
-
switchTab('main');
|
|
2535
|
-
loadAgentCovers();
|
|
2536
|
-
} catch (e) {
|
|
2537
|
-
alert('创建失败: ' + (e.message || e));
|
|
2538
|
-
} finally {
|
|
2539
|
-
hideSheet('#create-sheet');
|
|
2540
|
-
}
|
|
2541
|
-
}
|
|
2710
|
+
// 创建智能体: 已由首页世界流替代 (2026-10-07: createSession 无调用, 删除)
|
|
2542
2711
|
|
|
2543
|
-
|
|
2544
|
-
|
|
2712
|
+
// 添加好友: 弹出选择 sheet (扫码 / 手动)
|
|
2713
|
+
function addFriend() { showSheet('#addfriend-sheet'); }
|
|
2545
2714
|
async function addFriendManual() {
|
|
2546
2715
|
hideSheet('#addfriend-sheet');
|
|
2547
2716
|
const addr = prompt('输入好友地址 (multiaddr, 如 /ip4/10.0.2.2/tcp/54188/ws)', '');
|
|
@@ -2549,7 +2718,7 @@
|
|
|
2549
2718
|
try {
|
|
2550
2719
|
const r = await api.post('/api/peers/add', { addr });
|
|
2551
2720
|
alert(r && r.ok ? (r.connected ? '已连接好友' : '已记录好友地址, 连接中...') : ((r && r.error) || '添加失败'));
|
|
2552
|
-
if (currentTab === 'network') loadContacts();
|
|
2721
|
+
if (currentTab === 'network' || currentTab === 'tasks') loadContacts();
|
|
2553
2722
|
} catch (e) {
|
|
2554
2723
|
alert('添加失败: ' + (e.message || e));
|
|
2555
2724
|
}
|
|
@@ -2633,7 +2802,6 @@
|
|
|
2633
2802
|
$('#btn-add').addEventListener('click', addFriend);
|
|
2634
2803
|
const bIdx = $('#btn-index'); if (bIdx) bIdx.addEventListener('click', () => void openIndexPanel());
|
|
2635
2804
|
const bSearch = $('#btn-search'); if (bSearch) bSearch.addEventListener('click', () => void openSearch());
|
|
2636
|
-
const cs = $('#btn-create-session'); if (cs) cs.addEventListener('click', createSession);
|
|
2637
2805
|
const csScan = $('#choice-scan'); if (csScan) csScan.addEventListener('click', addFriendScan);
|
|
2638
2806
|
const csMan = $('#choice-manual'); if (csMan) csMan.addEventListener('click', addFriendManual);
|
|
2639
2807
|
const csCan = $('#choice-cancel'); if (csCan) csCan.addEventListener('click', () => hideSheet('#addfriend-sheet'));
|
|
@@ -2719,7 +2887,7 @@
|
|
|
2719
2887
|
const r2 = await (window.BolloonCore && window.BolloonCore.gateway && window.BolloonCore.gateway.join(text));
|
|
2720
2888
|
alert(r2 ? (r2.output || '已处理') : '这个二维码不是好友地址 (也不是入网链接)');
|
|
2721
2889
|
}
|
|
2722
|
-
if (currentTab === 'network') { loadContacts(); loadP2PStatus(); }
|
|
2890
|
+
if (currentTab === 'network' || currentTab === 'tasks') { loadContacts(); loadP2PStatus(); }
|
|
2723
2891
|
return;
|
|
2724
2892
|
}
|
|
2725
2893
|
const r = await (window.BolloonCore && window.BolloonCore.gateway && window.BolloonCore.gateway.join(text));
|
|
@@ -3120,7 +3288,7 @@
|
|
|
3120
3288
|
if (!msg || msg.type !== 'ui' || !msg.action) return;
|
|
3121
3289
|
const d = msg.data || {};
|
|
3122
3290
|
switch (msg.action) {
|
|
3123
|
-
case 'switchTab': if (d.tab && ['main', 'friends', '
|
|
3291
|
+
case 'switchTab': if (d.tab && ['main', 'friends', 'tasks', 'me'].includes(d.tab)) switchTab(d.tab); break;
|
|
3124
3292
|
case 'openSettings': openSettings(); break;
|
|
3125
3293
|
case 'showToast': alert(d.message || ''); break;
|
|
3126
3294
|
case 'goBack': closeCardDetail(); closeChat(); break;
|
|
@@ -3245,7 +3413,7 @@
|
|
|
3245
3413
|
return false;
|
|
3246
3414
|
}
|
|
3247
3415
|
function setupGestures() {
|
|
3248
|
-
const TAB_ORDER = ['main', 'friends', '
|
|
3416
|
+
const TAB_ORDER = ['main', 'friends', 'tasks', 'me'];
|
|
3249
3417
|
let sx = 0, sy = 0, st = 0, active = false, startTarget = null;
|
|
3250
3418
|
document.addEventListener('touchstart', (e) => {
|
|
3251
3419
|
if (e.touches.length !== 1) { active = false; return; }
|
|
@@ -3705,39 +3873,39 @@
|
|
|
3705
3873
|
|
|
3706
3874
|
// ── 渲染: 公告板 ──────────────────────────────────────────────────────────
|
|
3707
3875
|
function renderBoard() {
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
}
|
|
3716
|
-
items.forEach((it, i) => {
|
|
3717
|
-
const r = row(
|
|
3718
|
-
String(it.capability || '') + ' · ' + String(it.idShort || '') + ' · ' + String((it.statusLabel || {})[lang()] || ''),
|
|
3719
|
-
'',
|
|
3720
|
-
);
|
|
3721
|
-
const sub = line('', '');
|
|
3722
|
-
sub.style.fontSize = '12px';
|
|
3723
|
-
sub.style.color = 'var(--text-secondary)';
|
|
3724
|
-
sub.appendChild(document.createTextNode((lang() === 'en' ? 'budget ' : '预算 ') + safeText(it.budgetLabel || '—') + ' · '));
|
|
3725
|
-
sub.appendChild(document.createTextNode(lang() === 'en' ? 'closes ' : '截止 '));
|
|
3726
|
-
sub.appendChild(relDeltaNode(it.deadlineInMs));
|
|
3727
|
-
r.firstChild.appendChild(sub);
|
|
3728
|
-
if (it.claimable) {
|
|
3729
|
-
const b = mk('button', 'sheet-choice');
|
|
3730
|
-
b.style.flex = '0 0 auto';
|
|
3731
|
-
bilingual(b, '发进群', 'Post to group');
|
|
3732
|
-
b.setAttribute('data-announce', String(i));
|
|
3733
|
-
b.addEventListener('click', () => askAnnounceToGroup(i));
|
|
3734
|
-
r.appendChild(b);
|
|
3876
|
+
const list = $('#tasks-board');
|
|
3877
|
+
if (!list) return;
|
|
3878
|
+
clear(list);
|
|
3879
|
+
const d = state.board || {};
|
|
3880
|
+
const items = d.items || [];
|
|
3881
|
+
if (!items.length) {
|
|
3882
|
+
list.appendChild(line('sheet-text', lang() === 'en' ? 'Board is empty' : '板上暂时没有公告'));
|
|
3735
3883
|
}
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3884
|
+
// 2026-10-07: 双栏瀑布流卡片 (发现页风格)
|
|
3885
|
+
items.forEach((it, i) => {
|
|
3886
|
+
const card = mk('div', 'board-card');
|
|
3887
|
+
const cap = mk('div', 'board-card-cap');
|
|
3888
|
+
cap.textContent = String(it.capability || it.idShort || '任务');
|
|
3889
|
+
card.appendChild(cap);
|
|
3890
|
+
const status = mk('div', 'board-card-status');
|
|
3891
|
+
status.textContent = String((it.statusLabel || {})[lang()] || '');
|
|
3892
|
+
card.appendChild(status);
|
|
3893
|
+
const sub = mk('div', 'board-card-sub');
|
|
3894
|
+
sub.textContent = (lang() === 'en' ? 'budget ' : '预算 ') + safeText(it.budgetLabel || '—')
|
|
3895
|
+
+ ' · ' + (lang() === 'en' ? 'closes ' : '截止 ') + relDeltaText(it.deadlineInMs);
|
|
3896
|
+
card.appendChild(sub);
|
|
3897
|
+
if (it.claimable) {
|
|
3898
|
+
const b = mk('button', 'board-card-btn');
|
|
3899
|
+
b.textContent = lang() === 'en' ? 'Post to group' : '发进群';
|
|
3900
|
+
b.setAttribute('data-announce', String(i));
|
|
3901
|
+
b.addEventListener('click', () => askAnnounceToGroup(i));
|
|
3902
|
+
card.appendChild(b);
|
|
3903
|
+
}
|
|
3904
|
+
list.appendChild(card);
|
|
3905
|
+
});
|
|
3906
|
+
const notes = Array.isArray(d.notes) ? d.notes : [];
|
|
3907
|
+
if (notes.length) list.appendChild(line('sheet-text', notes.join(' · ')));
|
|
3908
|
+
}
|
|
3741
3909
|
|
|
3742
3910
|
// ── 渲染: 飞轮进度 (只读; 状态来自 goal-flywheel 的五类用户可见状态) ────────
|
|
3743
3911
|
function renderFlywheel() {
|
package/dist/ios/routes-world.js
CHANGED
|
@@ -12,7 +12,10 @@ function bodyOf(req) {
|
|
|
12
12
|
}
|
|
13
13
|
/** 限制输入形状: 只收明确字段, 不整个吞前端对象 */
|
|
14
14
|
function intentInputFromBody(body) {
|
|
15
|
-
|
|
15
|
+
// 2026-10-06 修 bug: 之前调 bodyOf(body) —— bodyOf 期望 Request 访问 .body,
|
|
16
|
+
// 传 body 对象本身时 req.body = body.body = undefined ⇒ 永远返回 {} ⇒ 所有字段都丢。
|
|
17
|
+
// body 此刻已是 express.json 解析后的对象, 直接校验即可。
|
|
18
|
+
const b = body && typeof body === 'object' ? body : {};
|
|
16
19
|
const out = {};
|
|
17
20
|
if (typeof b.text === 'string' && b.text.trim())
|
|
18
21
|
out.text = b.text.trim();
|
package/dist/ios/server.js
CHANGED
|
@@ -920,6 +920,7 @@ async function handleV3P2PMessage(parsed, conn, comm) {
|
|
|
920
920
|
output: event.output,
|
|
921
921
|
error: event.error,
|
|
922
922
|
args: event.args,
|
|
923
|
+
durationMs: event.durationMs, // 2026-10-12: 命令执行时间读秒
|
|
923
924
|
fromPublicKey: v3P2PRef?.getPublicKey() || ''
|
|
924
925
|
}
|
|
925
926
|
});
|
|
@@ -4941,7 +4942,7 @@ fetchState();
|
|
|
4941
4942
|
// (与邮箱串行一致), web 不再自己持 abortController。取到 agent 后立刻开户 (见下面 getAgentForChannel 之后)。
|
|
4942
4943
|
let abortSignal;
|
|
4943
4944
|
// 2026-07-04: pivot loop safety net — 防止 LLM hang (minimax M3
|
|
4944
|
-
// 偶尔反复 think
|
|
4945
|
+
// 偶尔反复 think 不输出结果, pivot 连 5 次无进展时会 hang 在
|
|
4945
4946
|
// quality 评估). setTimeout 让 LLM 客户端收 signal 主动 break.
|
|
4946
4947
|
// 2026-07-06: 30s 太短 — minimax M3 单次 LLM 约 10s, pivot 默认 moderate profile 30 iter
|
|
4947
4948
|
// 留 ~300s. 之前 30s 自动 abort 时, 一次"你好"调用就被截断, 用户看到 ❌ 循环异常 + 空内容.
|
|
@@ -6901,6 +6902,7 @@ fetchState();
|
|
|
6901
6902
|
output: event.output,
|
|
6902
6903
|
error: event.error,
|
|
6903
6904
|
args: event.args,
|
|
6905
|
+
durationMs: event.durationMs, // 2026-10-12: 命令执行时间读秒
|
|
6904
6906
|
}, channelId);
|
|
6905
6907
|
}
|
|
6906
6908
|
else if (event.type === 'error') {
|