@bolloon/bolloon-agent 0.6.2 → 0.6.4
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/README.md +24 -17
- package/dist/agents/gateway-group.js +172 -4
- package/dist/agents/group-autopilot.js +29 -1
- package/dist/agents/intent-store.js +193 -0
- package/dist/agents/opportunity-match.js +260 -0
- package/dist/agents/pi-sdk-tools.js +22 -2
- package/dist/agents/pi-sdk.js +8 -1
- package/dist/agents/world-profile.js +49 -0
- package/dist/agents/world-watcher.js +75 -0
- package/dist/cli/commands/tasks.js +191 -1
- package/dist/cli/intent-command.js +338 -0
- package/dist/cli/mention-data.js +4 -1
- package/dist/cli/model-selector.js +84 -104
- package/dist/cli/protocol-envelope.js +2 -0
- package/dist/cli/setup-wizard.js +2 -10
- package/dist/cli/tui-select.js +99 -10
- package/dist/cli-entry.js +25 -9
- package/dist/electron-build/agents/gateway-group.js +177 -3
- package/dist/electron-build/agents/gateway-group.js.map +1 -1
- package/dist/electron-build/agents/group-autopilot.js +30 -2
- package/dist/electron-build/agents/group-autopilot.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk-tools.js +21 -1
- package/dist/electron-build/agents/pi-sdk-tools.js.map +1 -1
- package/dist/electron-build/agents/pi-sdk.js +8 -1
- package/dist/electron-build/agents/pi-sdk.js.map +1 -1
- package/dist/electron-build/llm/config-store.js +2 -2
- package/dist/electron-build/llm/config-store.js.map +1 -1
- package/dist/electron-build/llm/model-selection.js +16 -2
- package/dist/electron-build/llm/model-selection.js.map +1 -1
- package/dist/electron-build/llm/pi-ai.js +2 -2
- package/dist/electron-build/llm/pi-ai.js.map +1 -1
- package/dist/index.js +121 -3
- package/dist/ios/bolloon-web.json +3 -3
- package/dist/ios/client.js +232 -0
- package/dist/ios/index.html +10 -0
- package/dist/ios/mobile.css +80 -0
- package/dist/ios/mobile.html +10 -0
- package/dist/ios/mobile.js +54 -0
- package/dist/ios/routes-world.js +105 -0
- package/dist/ios/server.js +7 -0
- package/dist/ios/sw.js +1 -1
- package/dist/llm/config-store.js +2 -2
- package/dist/llm/model-selection.js +16 -2
- package/dist/llm/pi-ai.js +2 -2
- package/dist/web/bolloon-web.json +3 -3
- package/dist/web/client.js +232 -0
- package/dist/web/index.html +57 -0
- package/dist/web/mobile.css +80 -0
- package/dist/web/mobile.html +10 -0
- package/dist/web/mobile.js +54 -0
- package/dist/web/routes-world.js +105 -0
- package/dist/web/server.js +7 -0
- package/dist/web/sw.js +1 -1
- package/package.json +1 -1
package/dist/web/mobile.js
CHANGED
|
@@ -506,6 +506,59 @@
|
|
|
506
506
|
let currentCardIndex = 0;
|
|
507
507
|
let allAgentCards = [];
|
|
508
508
|
|
|
509
|
+
// === 世界机会流 (2026-10-05, leo Intent Network) ===
|
|
510
|
+
// 设计: 自动流入 (打开首页即拉), 不是按钮式被动响应。ignore/accept 走 feedback 校准环。
|
|
511
|
+
let worldFeedShown = false;
|
|
512
|
+
let worldFeedTimer = null;
|
|
513
|
+
async function loadWorldFeed() {
|
|
514
|
+
const feed = $('#world-feed');
|
|
515
|
+
const cards = $('#world-feed-cards');
|
|
516
|
+
if (!feed || !cards) return;
|
|
517
|
+
try {
|
|
518
|
+
const res = await fetch('/api/opportunities?min-score=0.2&limit=10');
|
|
519
|
+
if (!res.ok) return;
|
|
520
|
+
const data = await res.json();
|
|
521
|
+
const opps = (data.opportunities || []).slice(0, 6);
|
|
522
|
+
if (!opps.length) { feed.hidden = true; return; }
|
|
523
|
+
feed.hidden = false;
|
|
524
|
+
worldFeedShown = true;
|
|
525
|
+
cards.innerHTML = opps.map((o) => {
|
|
526
|
+
const pct = Math.round(o.score * 100);
|
|
527
|
+
const tag = o.reason === 'match' ? '与你匹配' : '世界变化';
|
|
528
|
+
return `<div class="world-feed-card">
|
|
529
|
+
<div class="world-feed-card-top">
|
|
530
|
+
<span class="world-feed-title">${escapeHtml(String(o.title || '').slice(0, 34))}</span>
|
|
531
|
+
<span class="world-feed-pct">${pct}%<small> ${tag}</small></span>
|
|
532
|
+
</div>
|
|
533
|
+
<div class="world-feed-summary">${escapeHtml(String(o.summary || '').slice(0, 60))}</div>
|
|
534
|
+
<div class="world-feed-actions">
|
|
535
|
+
<button class="world-feed-btn" data-wf-action="accept" data-wf-id="${o.id}">看看</button>
|
|
536
|
+
<button class="world-feed-btn ghost" data-wf-action="ignore" data-wf-id="${o.id}">忽略</button>
|
|
537
|
+
</div>
|
|
538
|
+
</div>`;
|
|
539
|
+
}).join('');
|
|
540
|
+
cards.querySelectorAll('[data-wf-action]').forEach((btn) => {
|
|
541
|
+
btn.addEventListener('click', async () => {
|
|
542
|
+
const id = btn.dataset.wfId;
|
|
543
|
+
const action = btn.dataset.wfAction;
|
|
544
|
+
try { await fetch('/api/opportunities/' + id + '/' + action, { method: 'POST' }); } catch (e) {}
|
|
545
|
+
const card = btn.closest('.world-feed-card');
|
|
546
|
+
if (card) {
|
|
547
|
+
card.style.opacity = '0.3';
|
|
548
|
+
card.style.filter = action === 'ignore' ? 'grayscale(1)' : 'none';
|
|
549
|
+
card.style.borderLeftColor = action === 'accept' ? '#c4d640' : 'transparent';
|
|
550
|
+
}
|
|
551
|
+
});
|
|
552
|
+
});
|
|
553
|
+
} catch (e) {
|
|
554
|
+
feed.hidden = true; // 拉不到就不显示世界区 (不阻塞首页)
|
|
555
|
+
}
|
|
556
|
+
// 持续流入: 世界区可见时每 45s 自动刷新 (AI 持续观察, 不是人刷新)
|
|
557
|
+
if (feed && !feed.hidden && worldFeedShown && !worldFeedTimer) {
|
|
558
|
+
worldFeedTimer = setInterval(() => { loadWorldFeed(); }, 45000);
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
|
|
509
562
|
// === 卡片封面 (docs/fig 导出 → src/web/covers, 每个 agent 唯一不重复) ===
|
|
510
563
|
let _coverList = null;
|
|
511
564
|
async function loadCovers() {
|
|
@@ -533,6 +586,7 @@
|
|
|
533
586
|
async function loadAgentCovers() {
|
|
534
587
|
const track = $('#card-track');
|
|
535
588
|
if (!track) return;
|
|
589
|
+
loadWorldFeed(); // 2026-10-05: 世界机会流自动流入 (不阻塞智能体卡片, fire-and-forget)
|
|
536
590
|
await loadCovers();
|
|
537
591
|
track.innerHTML = '<div class="card-loading">加载智能体卡片...</div>';
|
|
538
592
|
try {
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { setIntent, listIntents, getIntent, removeIntent } from '../agents/intent-store.js';
|
|
2
|
+
import { scanOpportunities, recordFeedback, readObservations } from '../agents/opportunity-match.js';
|
|
3
|
+
import { readProfile, setProfile } from '../agents/world-profile.js';
|
|
4
|
+
import { worldWatcherStatus } from '../agents/world-watcher.js';
|
|
5
|
+
function json(res, status, body) {
|
|
6
|
+
res.status(status).json(body);
|
|
7
|
+
}
|
|
8
|
+
function bodyOf(req) {
|
|
9
|
+
return req.body && typeof req.body === 'object' ? req.body : {};
|
|
10
|
+
}
|
|
11
|
+
/** 限制输入形状: 只收明确字段, 不整个吞前端对象 */
|
|
12
|
+
function intentInputFromBody(body) {
|
|
13
|
+
const b = bodyOf(body);
|
|
14
|
+
const out = {};
|
|
15
|
+
if (typeof b.text === 'string' && b.text.trim())
|
|
16
|
+
out.text = b.text.trim();
|
|
17
|
+
if (Array.isArray(b.tags))
|
|
18
|
+
out.tags = b.tags.filter((t) => typeof t === 'string').map(String).slice(0, 24);
|
|
19
|
+
if (b.priority !== undefined)
|
|
20
|
+
out.priority = Number(b.priority);
|
|
21
|
+
if (typeof b.budget === 'string')
|
|
22
|
+
out.budget = b.budget.trim();
|
|
23
|
+
if (b.deadline !== undefined) {
|
|
24
|
+
const d = typeof b.deadline === 'number' ? b.deadline : Date.parse(String(b.deadline));
|
|
25
|
+
if (!Number.isNaN(d))
|
|
26
|
+
out.deadline = d;
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
export function registerWorldRoutes(app) {
|
|
31
|
+
// ── intents ──────────────────────────────────────────────────────────────
|
|
32
|
+
app.get('/api/intents', async (_req, res) => {
|
|
33
|
+
const r = await listIntents();
|
|
34
|
+
if (!r.ok)
|
|
35
|
+
return json(res, 500, { error: r.error });
|
|
36
|
+
const order = { active: 0, paused: 1, done: 2 };
|
|
37
|
+
const rows = [...r.intents].sort((a, b) => order[a.status] - order[b.status] || b.createdAt - a.createdAt);
|
|
38
|
+
json(res, 200, { ok: true, count: rows.length, intents: rows });
|
|
39
|
+
});
|
|
40
|
+
app.post('/api/intents', async (req, res) => {
|
|
41
|
+
const input = intentInputFromBody(req.body);
|
|
42
|
+
if (!input.text)
|
|
43
|
+
return json(res, 400, { ok: false, error: 'text 必填 (声明你现在在做什么)' });
|
|
44
|
+
const r = await setIntent(input);
|
|
45
|
+
if (!r.ok)
|
|
46
|
+
return json(res, 400, { ok: false, error: r.error });
|
|
47
|
+
json(res, r.created ? 201 : 200, { ok: true, created: r.created, intent: r.intent });
|
|
48
|
+
});
|
|
49
|
+
app.get('/api/intents/:id', async (req, res) => {
|
|
50
|
+
const r = await getIntent(req.params.id);
|
|
51
|
+
if (!r.ok)
|
|
52
|
+
return json(res, 500, { error: r.error });
|
|
53
|
+
if (!r.intent)
|
|
54
|
+
return json(res, 404, { ok: false, error: `没有意图 ${req.params.id}` });
|
|
55
|
+
json(res, 200, { ok: true, intent: r.intent });
|
|
56
|
+
});
|
|
57
|
+
app.delete('/api/intents/:id', async (req, res) => {
|
|
58
|
+
const done = req.query.done === '1' || req.query.done === 'true';
|
|
59
|
+
const r = await removeIntent(req.params.id, done);
|
|
60
|
+
if (!r.ok)
|
|
61
|
+
return json(res, 404, { ok: false, error: r.error });
|
|
62
|
+
json(res, 200, { ok: true, removed: r.removed, archived: done });
|
|
63
|
+
});
|
|
64
|
+
// ── opportunities ────────────────────────────────────────────────────────
|
|
65
|
+
app.get('/api/opportunities', async (req, res) => {
|
|
66
|
+
const minScoreRaw = typeof req.query['min-score'] === 'string' ? req.query['min-score'] : '0.4';
|
|
67
|
+
const minScore = Number(minScoreRaw);
|
|
68
|
+
const limitRaw = typeof req.query.limit === 'string' ? req.query.limit : '50';
|
|
69
|
+
const limit = Math.max(1, Math.min(200, parseInt(limitRaw, 10) || 50));
|
|
70
|
+
const r = await scanOpportunities({ minScore: Number.isFinite(minScore) ? minScore : 0.4, limit });
|
|
71
|
+
if (!r.ok)
|
|
72
|
+
return json(res, 500, { ok: false, error: r.error });
|
|
73
|
+
json(res, 200, { ok: true, count: r.results.length, minScore, opportunities: r.results });
|
|
74
|
+
});
|
|
75
|
+
app.post('/api/opportunities/:id/ignore', async (req, res) => {
|
|
76
|
+
const sourceId = String(req.params.id).replace(/^opp_/, 'ann-');
|
|
77
|
+
const r = await recordFeedback('ignore', sourceId);
|
|
78
|
+
json(res, r.ok ? 200 : 400, { ok: r.ok, opportunityId: req.params.id, ignored: r.ok, error: r.error });
|
|
79
|
+
});
|
|
80
|
+
app.post('/api/opportunities/:id/accept', async (req, res) => {
|
|
81
|
+
const sourceId = String(req.params.id).replace(/^opp_/, 'ann-');
|
|
82
|
+
const r = await recordFeedback('accept', sourceId);
|
|
83
|
+
json(res, r.ok ? 200 : 400, { ok: r.ok, opportunityId: req.params.id, accepted: r.ok, error: r.error });
|
|
84
|
+
});
|
|
85
|
+
// ── 用户画像 (leo 三条分发规划③: 初始化收集 → 常驻意图推送) ─────────────────
|
|
86
|
+
app.get('/api/world/profile', async (_req, res) => {
|
|
87
|
+
const profile = await readProfile();
|
|
88
|
+
json(res, 200, { ok: true, profile });
|
|
89
|
+
});
|
|
90
|
+
app.put('/api/world/profile', async (req, res) => {
|
|
91
|
+
const b = bodyOf(req);
|
|
92
|
+
const r = await setProfile({
|
|
93
|
+
name: typeof b.name === 'string' ? b.name : undefined,
|
|
94
|
+
about: typeof b.about === 'string' ? b.about : undefined,
|
|
95
|
+
tags: Array.isArray(b.tags) ? b.tags.filter((t) => typeof t === 'string') : undefined,
|
|
96
|
+
});
|
|
97
|
+
json(res, r.ok ? 200 : 400, { ok: r.ok, profile: r.profile, error: r.error });
|
|
98
|
+
});
|
|
99
|
+
// ── 世界观察器状态 (自动触发机制可见性) ─────────────────────────────────────
|
|
100
|
+
app.get('/api/world/watch', async (_req, res) => {
|
|
101
|
+
const status = worldWatcherStatus();
|
|
102
|
+
const observations = await readObservations(20);
|
|
103
|
+
json(res, 200, { ok: true, watcher: status, recentObservations: observations });
|
|
104
|
+
});
|
|
105
|
+
}
|
package/dist/web/server.js
CHANGED
|
@@ -11,6 +11,8 @@ import { validateMessageInput, validateChannelInput, healthCheck, } from './inpu
|
|
|
11
11
|
import { segmentChatReply } from '../agents/chat-segmenter.js';
|
|
12
12
|
import { registerJudgmentsRoutes } from './routes-judgments.js';
|
|
13
13
|
import { registerLlmConfigRoutes } from './routes-llm-config.js';
|
|
14
|
+
import { startWorldWatcher } from '../agents/world-watcher.js';
|
|
15
|
+
import { registerWorldRoutes } from './routes-world.js';
|
|
14
16
|
import { registerExternalEngineRoutes } from './routes-external-engines.js';
|
|
15
17
|
import { registerTaskRoutes } from './routes-tasks.js';
|
|
16
18
|
// 2026-09-19: 联系方式 / 人工批准 / 手机—桌面配对 路由
|
|
@@ -6956,6 +6958,11 @@ fetchState();
|
|
|
6956
6958
|
registerOnchainTradeRoutes(app);
|
|
6957
6959
|
// 2026-07-06: LLM/Video/Audio 配置路由抽到 ./routes-llm-config.ts
|
|
6958
6960
|
registerLlmConfigRoutes(app);
|
|
6961
|
+
// 2026-10-05 (leo 四端 Intent Network P1): World API — 意图声明 + 机会匹配 (Web/Mobile 共用)
|
|
6962
|
+
registerWorldRoutes(app);
|
|
6963
|
+
// 2026-10-05 (leo 三条分发规划①): 自动触发机制 —— 世界观察器常驻,
|
|
6964
|
+
// 定时扫描 + 观察日志自动积累, 不依赖用户打开界面 (AI 主动看世界)。
|
|
6965
|
+
startWorldWatcher();
|
|
6959
6966
|
// 2026-07-22: 外部编码智能体 (codex/claude-code/opencode/openclaw/hermes/实验 API)
|
|
6960
6967
|
// 发现 + 配置为供应商 + 委派
|
|
6961
6968
|
registerExternalEngineRoutes(app);
|
package/dist/web/sw.js
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* 现在: 代码类资源 (html/js/css/json) 一律 **network-first** (拿不到网才回退缓存),
|
|
7
7
|
* 缓存名带版本号, activate 时清掉所有旧缓存; 图标等静态资源仍 cache-first。
|
|
8
8
|
*/
|
|
9
|
-
const CACHE = 'bolloon-mobile-v0.6.
|
|
9
|
+
const CACHE = 'bolloon-mobile-v0.6.4';
|
|
10
10
|
const SHELL = [
|
|
11
11
|
'./mobile.html',
|
|
12
12
|
'./mobile.css',
|