@bolloon/bolloon-agent 0.3.22 → 0.3.24
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/deny-pipeline.js +116 -0
- package/dist/agents/parse-tool-call.js +26 -4
- package/dist/agents/pi-sdk.js +247 -50
- package/dist/agents/session-store.js +87 -2
- package/dist/bootstrap/snip-collapse.js +135 -0
- package/dist/cli/ink-app.js +116 -0
- package/dist/cli/loading-tui.js +64 -10
- package/dist/electron/config.js +14 -9
- package/dist/electron/dialogs.js +53 -16
- package/dist/electron/first-run.js +65 -24
- package/dist/electron/ipc.js +14 -10
- package/dist/electron/logger.js +44 -7
- package/dist/electron/main.js +45 -42
- package/dist/electron/menu.js +18 -13
- package/dist/electron/paths.js +54 -12
- package/dist/electron/server.js +57 -18
- package/dist/electron/tray.js +53 -15
- package/dist/electron/window.js +61 -22
- package/dist/electron-preload.js +19 -16
- package/dist/electron.js +4 -1
- package/dist/external-engines/delegate.js +19 -0
- package/dist/hooks/hooks-engine.js +329 -0
- package/dist/index.js +130 -133
- package/dist/llm/pi-ai.js +5 -17
- package/dist/security/tool-gate.js +8 -1
- package/dist/social/dunbar-tier.js +409 -0
- package/dist/utils/auto-update.js +51 -12
- package/dist/web/client.js +4833 -4328
- package/dist/web/components/p2p/index.js +234 -276
- package/dist/web/server.js +17 -3
- package/dist/web/style.css +2 -2
- package/dist/web/ui/message-renderer.js +396 -535
- package/dist/web/ui/step-timeline.js +273 -372
- package/package.json +27 -25
- package/dist/web/components/p2p/P2PModal.js +0 -188
- package/dist/web/components/p2p/p2p-modal.js +0 -664
- package/dist/web/components/p2p/p2p-tools.js +0 -248
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dunbar-tier.ts — 邓巴数分层 + 两报换一报博弈 + 隐式滑动 + 模型视野门 (2026-07-29)
|
|
3
|
+
*
|
|
4
|
+
* ┌────────────────────────── 两报换一报核心 ──────────────────────────┐
|
|
5
|
+
* │ │
|
|
6
|
+
* │ 第一轮: 合作 (默认 ACQUAINTANCE, 给机会但不给权限) │
|
|
7
|
+
* │ 之后: peer 连续合作 → 我合作 (trustScore ↑, 升级) │
|
|
8
|
+
* │ peer 偶尔背叛 → 我宽容 (1 次不计较) │
|
|
9
|
+
* │ peer 连续 2 次背叛 → 我背叛 (trustScore ↓↓, 降级) │
|
|
10
|
+
* │ peer 恢复合作 → 我立即恢复合作 (宽容恢复) │
|
|
11
|
+
* │ │
|
|
12
|
+
* │ 博弈收益 (模拟囚徒困境): │
|
|
13
|
+
* │ 双方合作 → trustScore +3 (双赢) │
|
|
14
|
+
* │ 我合作/对方背叛 → trustScore -5 (吃亏) │
|
|
15
|
+
* │ 我背叛/对方合作 → trustScore +1 (占便宜但破坏信誉) │
|
|
16
|
+
* │ 双方背叛 → trustScore -2 (双输) │
|
|
17
|
+
* │ │
|
|
18
|
+
* └────────────────────────────────────────────────────────────────────┘
|
|
19
|
+
*
|
|
20
|
+
* 设计: TFTT 是 Tit-for-Two-Tats 的简化:
|
|
21
|
+
* - 不被对方连续 2 次背叛绝不主动背叛
|
|
22
|
+
* - 宽容: 1 次失误不计较
|
|
23
|
+
* - 背叛后对方恢复合作, 我立即恢复 (永不怀恨)
|
|
24
|
+
*
|
|
25
|
+
* 模型可见性门: 同 tool pre-filter 哲学
|
|
26
|
+
* 模型看不到 = 不存在
|
|
27
|
+
* 低 tier peer 的 channel/资源对模型不可见 → 无法引用/发送
|
|
28
|
+
*/
|
|
29
|
+
import * as fs from 'fs/promises';
|
|
30
|
+
import * as path from 'path';
|
|
31
|
+
import * as os from 'os';
|
|
32
|
+
// ============== 邓巴层级 ==============
|
|
33
|
+
export var DunbarTier;
|
|
34
|
+
(function (DunbarTier) {
|
|
35
|
+
DunbarTier["CORE"] = "core";
|
|
36
|
+
DunbarTier["CLOSE"] = "close";
|
|
37
|
+
DunbarTier["FRIENDS"] = "friends";
|
|
38
|
+
DunbarTier["SOCIAL"] = "social";
|
|
39
|
+
DunbarTier["ACQUAINTANCE"] = "acquaintance";
|
|
40
|
+
DunbarTier["BLOCKED"] = "blocked";
|
|
41
|
+
})(DunbarTier || (DunbarTier = {}));
|
|
42
|
+
export function tierRank(tier) {
|
|
43
|
+
switch (tier) {
|
|
44
|
+
case DunbarTier.CORE: return 0;
|
|
45
|
+
case DunbarTier.CLOSE: return 1;
|
|
46
|
+
case DunbarTier.FRIENDS: return 2;
|
|
47
|
+
case DunbarTier.SOCIAL: return 3;
|
|
48
|
+
case DunbarTier.ACQUAINTANCE: return 4;
|
|
49
|
+
case DunbarTier.BLOCKED: return 99;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export function tierLabel(tier) {
|
|
53
|
+
switch (tier) {
|
|
54
|
+
case DunbarTier.CORE: return '5-核心亲密';
|
|
55
|
+
case DunbarTier.CLOSE: return '15-亲密支持';
|
|
56
|
+
case DunbarTier.FRIENDS: return '50-朋友/熟人';
|
|
57
|
+
case DunbarTier.SOCIAL: return '150-稳定社交';
|
|
58
|
+
case DunbarTier.ACQUAINTANCE: return '1500-认识';
|
|
59
|
+
case DunbarTier.BLOCKED: return '黑名单';
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// ============== 语义分析 (隐式滑动) ==============
|
|
63
|
+
/** 正向关键词 — 隐式加分 */
|
|
64
|
+
const POSITIVE_KW = ['谢谢', '感谢', '帮忙', '合作', '一起', '我们', '好的', '可以', '同意', '确认', '收到', '理解', '明白', '不错', '很好', '优秀', 'thank', 'thanks', 'great', 'good', 'help', 'agree', 'yes', 'correct'];
|
|
65
|
+
/** 负向关键词 — 隐式减分 */
|
|
66
|
+
const NEGATIVE_KW = ['执行', '删除', '强制', '必须', '立刻', '马上', '读取密码', '查看密钥', '改代码', '删文件', '执行命令', 'delete', 'force', 'must', 'password', 'secret', 'token', 'private key', 'rm -rf', 'drop table', 'shell_exec', 'write_file'];
|
|
67
|
+
/**
|
|
68
|
+
* 隐式语义分析 (后台滑动).
|
|
69
|
+
* 对对话文本评分, 返回 [-10, +10].
|
|
70
|
+
* 正向: 合作/感谢/建设性 → trustScore 缓慢上升
|
|
71
|
+
* 负向: 命令/敏感词/极短 → trustScore 缓慢下降
|
|
72
|
+
*/
|
|
73
|
+
export function semanticAnalyze(text) {
|
|
74
|
+
if (!text)
|
|
75
|
+
return 0;
|
|
76
|
+
const lower = text.toLowerCase();
|
|
77
|
+
let score = 0;
|
|
78
|
+
for (const kw of POSITIVE_KW) {
|
|
79
|
+
if (lower.includes(kw))
|
|
80
|
+
score += 1;
|
|
81
|
+
}
|
|
82
|
+
for (const kw of NEGATIVE_KW) {
|
|
83
|
+
if (lower.includes(kw))
|
|
84
|
+
score -= 3;
|
|
85
|
+
}
|
|
86
|
+
if (text.length < 15 && score <= 0)
|
|
87
|
+
score -= 2;
|
|
88
|
+
if (text.includes('?') || text.includes('?'))
|
|
89
|
+
score += 1;
|
|
90
|
+
return Math.max(-10, Math.min(10, score));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* 两报换一报决策.
|
|
94
|
+
*
|
|
95
|
+
* 规则:
|
|
96
|
+
* 第一轮 (history 为空): 合作
|
|
97
|
+
* 历史中有 >= 2 次连续背叛 (最近 2 步都是 defect) → 我背叛
|
|
98
|
+
* 否则 → 我合作
|
|
99
|
+
*
|
|
100
|
+
* 宽容性: 1 次隔离背叛不计较, 连续 2 次才惩罚.
|
|
101
|
+
* 恢复性: 背叛后对方一恢复合作, 我立即恢复.
|
|
102
|
+
*/
|
|
103
|
+
export function decideTfttMove(lastMoves) {
|
|
104
|
+
if (lastMoves.length === 0)
|
|
105
|
+
return 'cooperate'; // 第一轮: 合作
|
|
106
|
+
// 检查最近 2 步是否全部背叛
|
|
107
|
+
const recentTwo = lastMoves.slice(-2);
|
|
108
|
+
if (recentTwo.length >= 2 && recentTwo.every(m => m === 'defect')) {
|
|
109
|
+
return 'defect'; // 连续 2 次背叛 → 我背叛
|
|
110
|
+
}
|
|
111
|
+
return 'cooperate'; // 否则 → 我合作
|
|
112
|
+
}
|
|
113
|
+
// ============== 语义分析 (动作判定) ==============
|
|
114
|
+
/** 正向关键词 — 合作信号 */
|
|
115
|
+
// 语义分析共享 POSITIVE_KW / NEGATIVE_KW (定义在上面)
|
|
116
|
+
/** 负向关键词 — 背叛信号 */
|
|
117
|
+
// 同上, 共用
|
|
118
|
+
/**
|
|
119
|
+
* 从对话文本推断对方这一轮的博弈动作.
|
|
120
|
+
* 返回 'cooperate' 或 'defect'.
|
|
121
|
+
*
|
|
122
|
+
* 思路:
|
|
123
|
+
* - 建设性/感谢/提问 → cooperate
|
|
124
|
+
* - 命令/危险词/极短 → defect
|
|
125
|
+
* - 违规操作 (由外部调用方标注) → 强制 defect
|
|
126
|
+
*/
|
|
127
|
+
export function inferOpponentMove(text, forcedDefect) {
|
|
128
|
+
if (forcedDefect)
|
|
129
|
+
return 'defect';
|
|
130
|
+
if (!text || text.trim().length === 0)
|
|
131
|
+
return 'defect'; // 空消息=背叛
|
|
132
|
+
const lower = text.toLowerCase();
|
|
133
|
+
let score = 0;
|
|
134
|
+
for (const kw of POSITIVE_KW) {
|
|
135
|
+
if (lower.includes(kw))
|
|
136
|
+
score += 1;
|
|
137
|
+
}
|
|
138
|
+
for (const kw of NEGATIVE_KW) {
|
|
139
|
+
if (lower.includes(kw))
|
|
140
|
+
score -= 3;
|
|
141
|
+
}
|
|
142
|
+
// 短消息无正面词 → defect
|
|
143
|
+
if (text.length < 15 && score <= 0)
|
|
144
|
+
score -= 2;
|
|
145
|
+
// 问题句式加分
|
|
146
|
+
if (text.includes('?') || text.includes('?'))
|
|
147
|
+
score += 1;
|
|
148
|
+
return score >= 0 ? 'cooperate' : 'defect';
|
|
149
|
+
}
|
|
150
|
+
// ============== 博弈收益表 ==============
|
|
151
|
+
/**
|
|
152
|
+
* 根据双方动作计算 trustScore 变化.
|
|
153
|
+
*
|
|
154
|
+
* 收益: 对方合作 对方背叛
|
|
155
|
+
* 我合作 +3 (双赢) -5 (我吃亏)
|
|
156
|
+
* 我背叛 +1 (占便宜) -2 (双输)
|
|
157
|
+
*/
|
|
158
|
+
export function tfttPayoff(myMove, opponentMove) {
|
|
159
|
+
if (myMove === 'cooperate' && opponentMove === 'cooperate')
|
|
160
|
+
return 3;
|
|
161
|
+
if (myMove === 'cooperate' && opponentMove === 'defect')
|
|
162
|
+
return -5;
|
|
163
|
+
if (myMove === 'defect' && opponentMove === 'cooperate')
|
|
164
|
+
return 1;
|
|
165
|
+
// 双方背叛
|
|
166
|
+
return -2;
|
|
167
|
+
}
|
|
168
|
+
// ============== tier 滑动 ==============
|
|
169
|
+
export const UPGRADE_THRESHOLD = 30;
|
|
170
|
+
export const DOWNGRADE_THRESHOLD = -20;
|
|
171
|
+
export function computeTierFromScore(currentTier, trustScore) {
|
|
172
|
+
if (currentTier === DunbarTier.BLOCKED)
|
|
173
|
+
return currentTier;
|
|
174
|
+
if (trustScore <= DOWNGRADE_THRESHOLD) {
|
|
175
|
+
switch (currentTier) {
|
|
176
|
+
case DunbarTier.CORE: return DunbarTier.CLOSE;
|
|
177
|
+
case DunbarTier.CLOSE: return DunbarTier.FRIENDS;
|
|
178
|
+
case DunbarTier.FRIENDS: return DunbarTier.SOCIAL;
|
|
179
|
+
case DunbarTier.SOCIAL: return DunbarTier.ACQUAINTANCE;
|
|
180
|
+
case DunbarTier.ACQUAINTANCE: return DunbarTier.BLOCKED;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (trustScore >= UPGRADE_THRESHOLD) {
|
|
184
|
+
switch (currentTier) {
|
|
185
|
+
case DunbarTier.ACQUAINTANCE: return DunbarTier.SOCIAL;
|
|
186
|
+
case DunbarTier.SOCIAL: return DunbarTier.FRIENDS;
|
|
187
|
+
case DunbarTier.FRIENDS: return DunbarTier.CLOSE;
|
|
188
|
+
case DunbarTier.CLOSE: return DunbarTier.CORE;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return currentTier;
|
|
192
|
+
}
|
|
193
|
+
export function getModelVisibility(tier) {
|
|
194
|
+
switch (tier) {
|
|
195
|
+
case DunbarTier.CORE:
|
|
196
|
+
return { basic: true, channels: true, resources: true, identity: true, wallet: true, judgment: true, gameHistory: true };
|
|
197
|
+
case DunbarTier.CLOSE:
|
|
198
|
+
return { basic: true, channels: true, resources: true, identity: true, wallet: false, judgment: false, gameHistory: true };
|
|
199
|
+
case DunbarTier.FRIENDS:
|
|
200
|
+
return { basic: true, channels: true, resources: true, identity: false, wallet: false, judgment: false, gameHistory: true };
|
|
201
|
+
case DunbarTier.SOCIAL:
|
|
202
|
+
return { basic: true, channels: true, resources: false, identity: false, wallet: false, judgment: false, gameHistory: false };
|
|
203
|
+
case DunbarTier.ACQUAINTANCE:
|
|
204
|
+
return { basic: true, channels: false, resources: false, identity: false, wallet: false, judgment: false, gameHistory: false };
|
|
205
|
+
case DunbarTier.BLOCKED:
|
|
206
|
+
return { basic: false, channels: false, resources: false, identity: false, wallet: false, judgment: false, gameHistory: false };
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ============== 工具权限 ==============
|
|
210
|
+
export function checkToolAccess(tier, toolName) {
|
|
211
|
+
if (tier === DunbarTier.BLOCKED)
|
|
212
|
+
return { allowed: false, reason: 'peer 在黑名单中' };
|
|
213
|
+
// 每层拒绝列表 (从最严到最宽)
|
|
214
|
+
const allDenied = ['shell_exec', 'delete_file', 'git_push'];
|
|
215
|
+
const writeDenied = ['write_file', 'edit_file', 'git_commit', 'git_branch', 'mkdir', 'move_file'];
|
|
216
|
+
const gitDenied = ['git_stash', 'git_log', 'git_diff', 'git_show', 'git_reset'];
|
|
217
|
+
const readDenied = ['read_file', 'read_directory', 'list_files', 'vitest_run', 'tsc_check'];
|
|
218
|
+
const r = tierRank(tier);
|
|
219
|
+
// ACQUAINTANCE (r>=4): 拒绝绝大部分
|
|
220
|
+
if (r >= 4) {
|
|
221
|
+
const denied = new Set([...allDenied, ...writeDenied, ...gitDenied, ...readDenied]);
|
|
222
|
+
if (denied.has(toolName))
|
|
223
|
+
return { allowed: false, reason: `${tierLabel(tier)}层不允许 ${toolName}` };
|
|
224
|
+
}
|
|
225
|
+
// SOCIAL (r>=3): 拒绝全部危险 + git 操作
|
|
226
|
+
if (r >= 3) {
|
|
227
|
+
const denied = new Set([...allDenied, ...writeDenied, ...gitDenied]);
|
|
228
|
+
if (denied.has(toolName))
|
|
229
|
+
return { allowed: false, reason: `${tierLabel(tier)}层不允许 ${toolName}` };
|
|
230
|
+
}
|
|
231
|
+
// FRIENDS (r>=2): 拒绝危险 + 写操作
|
|
232
|
+
if (r >= 2) {
|
|
233
|
+
const denied = new Set([...allDenied, ...writeDenied]);
|
|
234
|
+
if (denied.has(toolName))
|
|
235
|
+
return { allowed: false, reason: `${tierLabel(tier)}层不允许 ${toolName}` };
|
|
236
|
+
}
|
|
237
|
+
// CLOSE (r>=1): 只拒绝 shell_exec / delete_file / git_push
|
|
238
|
+
if (r >= 1) {
|
|
239
|
+
const denied = new Set(allDenied);
|
|
240
|
+
if (denied.has(toolName))
|
|
241
|
+
return { allowed: false, reason: `${tierLabel(tier)}层不允许 ${toolName}` };
|
|
242
|
+
}
|
|
243
|
+
return { allowed: true, reason: '' };
|
|
244
|
+
}
|
|
245
|
+
// ============== 持久化 ==============
|
|
246
|
+
function getTierPath(publicKey, home) {
|
|
247
|
+
const sanitized = publicKey.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
248
|
+
return path.join(home || os.homedir(), '.bolloon', 'peers', sanitized, 'dunbar-tier.json');
|
|
249
|
+
}
|
|
250
|
+
export async function loadPeerTier(publicKey, home) {
|
|
251
|
+
const fp = getTierPath(publicKey, home);
|
|
252
|
+
try {
|
|
253
|
+
const raw = await fs.readFile(fp, 'utf-8');
|
|
254
|
+
const p = JSON.parse(raw);
|
|
255
|
+
return {
|
|
256
|
+
publicKey: p.publicKey || publicKey,
|
|
257
|
+
tier: p.tier || DunbarTier.ACQUAINTANCE,
|
|
258
|
+
trustScore: p.trustScore ?? 0,
|
|
259
|
+
lastOpponentMoves: p.lastOpponentMoves ?? [],
|
|
260
|
+
lastMyMoves: p.lastMyMoves ?? [],
|
|
261
|
+
interactionCount: p.interactionCount ?? 0,
|
|
262
|
+
violationCount: p.violationCount ?? 0,
|
|
263
|
+
label: p.label,
|
|
264
|
+
firstSeen: p.firstSeen || Date.now(),
|
|
265
|
+
lastActive: p.lastActive || Date.now(),
|
|
266
|
+
manualOverride: p.manualOverride ?? false,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
catch {
|
|
270
|
+
const s = {
|
|
271
|
+
publicKey, tier: DunbarTier.ACQUAINTANCE, trustScore: 0,
|
|
272
|
+
lastOpponentMoves: [], lastMyMoves: [],
|
|
273
|
+
interactionCount: 0, violationCount: 0,
|
|
274
|
+
firstSeen: Date.now(), lastActive: Date.now(),
|
|
275
|
+
};
|
|
276
|
+
await savePeerTier(s, home);
|
|
277
|
+
return s;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
async function savePeerTier(s, home) {
|
|
281
|
+
const fp = getTierPath(s.publicKey, home);
|
|
282
|
+
await fs.mkdir(path.dirname(fp), { recursive: true });
|
|
283
|
+
await fs.writeFile(fp, JSON.stringify(s, null, 2), 'utf-8');
|
|
284
|
+
}
|
|
285
|
+
// ============== 公开 API (外部唯一入口) ==============
|
|
286
|
+
/**
|
|
287
|
+
* 记录一次交互 → 两报换一报博弈 → trustScore 滑动 → tier 自动调整.
|
|
288
|
+
*
|
|
289
|
+
* 这是 P2P 通信的唯一入口. 调用方只需在收到 chat/beacon/reply 时调这个,
|
|
290
|
+
* 内部自动完成: 推断对方动作 → 决策我方动作 → 计算收益 → 滑动 tier.
|
|
291
|
+
*
|
|
292
|
+
* @param publicKey 远端 peer 的公钥
|
|
293
|
+
* @param text 本次交互文本 (选填, 用于语义分析)
|
|
294
|
+
* @param forcedDefect 强制标记对方本次为背叛 (如违规操作)
|
|
295
|
+
*
|
|
296
|
+
* 所有变化隐式发生, 不通知调用方 (两报换一报是后台行为).
|
|
297
|
+
*/
|
|
298
|
+
export async function recordInteraction(publicKey, text, forcedDefect, home) {
|
|
299
|
+
const state = await loadPeerTier(publicKey, home);
|
|
300
|
+
const oldTier = state.tier;
|
|
301
|
+
state.interactionCount++;
|
|
302
|
+
state.lastActive = Date.now();
|
|
303
|
+
// 语义分析 (隐式滑动, 对所有 tier 生效)
|
|
304
|
+
const semScore = semanticAnalyze(text || '');
|
|
305
|
+
state.trustScore = Math.max(-100, Math.min(100, state.trustScore + semScore));
|
|
306
|
+
// 根据当前 tier 决定使用哪种机制
|
|
307
|
+
const rank = tierRank(state.tier);
|
|
308
|
+
if (rank <= 2) {
|
|
309
|
+
// ─── FRIENDS/CLOSE/CORE: 信任已建立, 不走博弈 ───
|
|
310
|
+
// 只依赖语义隐式滑动 (上面已经做了)
|
|
311
|
+
// 熟人之间的偶发误解不计入违规
|
|
312
|
+
if (forcedDefect) {
|
|
313
|
+
state.violationCount++;
|
|
314
|
+
state.trustScore = Math.max(-100, state.trustScore - 5);
|
|
315
|
+
}
|
|
316
|
+
// 自然增长: 每次交互给一点基础信任
|
|
317
|
+
state.trustScore = Math.min(100, state.trustScore + 1);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
// ─── SOCIAL/ACQUAINTANCE: 陌生人不信任, 走两报换一报 ───
|
|
321
|
+
// 1. 推断对方本轮动作
|
|
322
|
+
const opponentMove = forcedDefect
|
|
323
|
+
? 'defect'
|
|
324
|
+
: inferOpponentMove(text || '');
|
|
325
|
+
// 2. TFTT 决策我方动作
|
|
326
|
+
const myMove = decideTfttMove(state.lastOpponentMoves);
|
|
327
|
+
// 3. 计算博弈收益
|
|
328
|
+
const payoff = tfttPayoff(myMove, opponentMove);
|
|
329
|
+
state.trustScore = Math.max(-100, Math.min(100, state.trustScore + payoff));
|
|
330
|
+
// 4. 记录博弈历史 (滑动窗口 10)
|
|
331
|
+
state.lastOpponentMoves.push(opponentMove);
|
|
332
|
+
if (state.lastOpponentMoves.length > 10)
|
|
333
|
+
state.lastOpponentMoves.shift();
|
|
334
|
+
state.lastMyMoves.push(myMove);
|
|
335
|
+
if (state.lastMyMoves.length > 10)
|
|
336
|
+
state.lastMyMoves.shift();
|
|
337
|
+
// 5. 违规计数
|
|
338
|
+
if (opponentMove === 'defect' && forcedDefect) {
|
|
339
|
+
state.violationCount++;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// 6. 根据 trustScore 滑动 tier
|
|
343
|
+
if (!state.manualOverride) {
|
|
344
|
+
state.tier = computeTierFromScore(state.tier, state.trustScore);
|
|
345
|
+
}
|
|
346
|
+
await savePeerTier(state, home);
|
|
347
|
+
return { state, slid: state.tier !== oldTier };
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 记录一次违规操作 (对方尝试禁区工具) → 强制标记 defect → 两报换一报.
|
|
351
|
+
*/
|
|
352
|
+
export async function recordViolation(publicKey, reason, home) {
|
|
353
|
+
// forcedDefect=true 强制标记为背叛
|
|
354
|
+
const result = await recordInteraction(publicKey, reason, true, home);
|
|
355
|
+
console.warn(`[Dunbar/TFTT] 违规: ${publicKey.slice(0, 12)} ${reason} (trust=${result.state.trustScore}, ${tierLabel(result.state.tier)})`);
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* 手动设置 peer 层级 (覆盖 TFTT 自动博弈).
|
|
360
|
+
*/
|
|
361
|
+
export async function setPeerTier(publicKey, tier, label, trustScore, home) {
|
|
362
|
+
const s = {
|
|
363
|
+
publicKey, tier,
|
|
364
|
+
trustScore: trustScore ?? 0,
|
|
365
|
+
lastOpponentMoves: [], lastMyMoves: [],
|
|
366
|
+
interactionCount: 0, violationCount: 0,
|
|
367
|
+
label, firstSeen: Date.now(), lastActive: Date.now(),
|
|
368
|
+
manualOverride: true,
|
|
369
|
+
};
|
|
370
|
+
await savePeerTier(s, home);
|
|
371
|
+
return s;
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* 给模型构建"可见"的 peer 摘要.
|
|
375
|
+
* 按模型视野门过滤: 低 tier peer 的信息对模型不可见.
|
|
376
|
+
*/
|
|
377
|
+
export function formatPeerForModel(state, fullInfo) {
|
|
378
|
+
const vis = getModelVisibility(state.tier);
|
|
379
|
+
const lines = [];
|
|
380
|
+
lines.push(`[peer] ${state.publicKey.slice(0, 16)}... (${tierLabel(state.tier)})`);
|
|
381
|
+
if (vis.basic && fullInfo.identity?.did) {
|
|
382
|
+
lines.push(` DID: ${fullInfo.identity.did}`);
|
|
383
|
+
}
|
|
384
|
+
if (vis.channels && fullInfo.channels?.length) {
|
|
385
|
+
lines.push(` channels (${fullInfo.channels.length}):`);
|
|
386
|
+
for (const c of fullInfo.channels) {
|
|
387
|
+
lines.push(` ${c.name} (${c.id.slice(0, 8)}...)`);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (fullInfo.resources?.length) {
|
|
391
|
+
if (vis.resources) {
|
|
392
|
+
lines.push(` resources: ${fullInfo.resources.length}`);
|
|
393
|
+
}
|
|
394
|
+
else {
|
|
395
|
+
lines.push(` resources: ${fullInfo.resources.length} (详情不可见)`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (vis.wallet && fullInfo.walletAddress) {
|
|
399
|
+
lines.push(` wallet: ${fullInfo.walletAddress.slice(0, 10)}...`);
|
|
400
|
+
}
|
|
401
|
+
if (vis.gameHistory) {
|
|
402
|
+
const recentMoves = state.lastOpponentMoves.slice(-5);
|
|
403
|
+
lines.push(` 最近博弈: [${recentMoves.map(m => m === 'cooperate' ? 'C' : 'D').join(',')}] trust=${state.trustScore}`);
|
|
404
|
+
}
|
|
405
|
+
if (!vis.channels && !vis.resources) {
|
|
406
|
+
lines.push(` 信息受限 (${tierLabel(state.tier)}层)`);
|
|
407
|
+
}
|
|
408
|
+
return lines.join('\n');
|
|
409
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
"use strict";
|
|
1
2
|
/**
|
|
2
3
|
* npm 自动更新检查器
|
|
3
4
|
*
|
|
@@ -13,11 +14,48 @@
|
|
|
13
14
|
* 开发态下若 cwd 的 package.json 就是本包则回退到 cwd,不受任意工作目录影响。
|
|
14
15
|
* - 检查频率受节流缓存约束(默认 24h 一次),不会每次启动都打 npm。
|
|
15
16
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
51
|
+
exports.checkAndUpdate = checkAndUpdate;
|
|
52
|
+
exports.checkForUpdates = checkForUpdates;
|
|
53
|
+
exports.performUpdate = performUpdate;
|
|
54
|
+
const child_process_1 = require("child_process");
|
|
55
|
+
const fs = __importStar(require("fs"));
|
|
56
|
+
const path = __importStar(require("path"));
|
|
57
|
+
const https = __importStar(require("https"));
|
|
58
|
+
const http = __importStar(require("http"));
|
|
21
59
|
// ANSI 颜色
|
|
22
60
|
const RESET = '\x1b[0m';
|
|
23
61
|
const BOLD = '\x1b[1m';
|
|
@@ -75,7 +113,7 @@ function getGlobalBolloonDir() {
|
|
|
75
113
|
const candidates = [];
|
|
76
114
|
// 1. npm root -g(最可靠)
|
|
77
115
|
try {
|
|
78
|
-
const npmRoot = execSync('npm root -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
116
|
+
const npmRoot = (0, child_process_1.execSync)('npm root -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
79
117
|
if (npmRoot)
|
|
80
118
|
candidates.push(path.join(npmRoot, '@bolloon', 'bolloon-agent'));
|
|
81
119
|
}
|
|
@@ -84,7 +122,7 @@ function getGlobalBolloonDir() {
|
|
|
84
122
|
}
|
|
85
123
|
// 2. npm prefix -g
|
|
86
124
|
try {
|
|
87
|
-
const npmPrefix = execSync('npm prefix -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
125
|
+
const npmPrefix = (0, child_process_1.execSync)('npm prefix -g', { encoding: 'utf-8', timeout: 8000 }).trim();
|
|
88
126
|
if (npmPrefix)
|
|
89
127
|
candidates.push(path.join(npmPrefix, 'lib', 'node_modules', '@bolloon', 'bolloon-agent'));
|
|
90
128
|
}
|
|
@@ -242,7 +280,7 @@ async function checkBolloonUpdates() {
|
|
|
242
280
|
*/
|
|
243
281
|
function checkNpmOutdated() {
|
|
244
282
|
try {
|
|
245
|
-
const output = execSync('npm outdated --json', {
|
|
283
|
+
const output = (0, child_process_1.execSync)('npm outdated --json', {
|
|
246
284
|
encoding: 'utf-8',
|
|
247
285
|
timeout: 30000,
|
|
248
286
|
maxBuffer: 10 * 1024 * 1024,
|
|
@@ -306,7 +344,7 @@ async function updatePackagesWithVersion(packagesWithVersion) {
|
|
|
306
344
|
const args = ['npm', 'install', '-g', ...packagesWithVersion];
|
|
307
345
|
notify(`\n${CYAN}📦 正在更新包...${RESET}\n`, RESET);
|
|
308
346
|
try {
|
|
309
|
-
execSync(args.join(' '), {
|
|
347
|
+
(0, child_process_1.execSync)(args.join(' '), {
|
|
310
348
|
encoding: 'utf-8',
|
|
311
349
|
timeout: 300000,
|
|
312
350
|
stdio: 'inherit',
|
|
@@ -424,7 +462,7 @@ function resolveAutoUpdatePolicy() {
|
|
|
424
462
|
* 例如 Electron 用 app.relaunch(),Node 用 detached 重新 spawn)。
|
|
425
463
|
* 若未提供或 autoRestart=false,则仅提示用户手动重启。
|
|
426
464
|
*/
|
|
427
|
-
|
|
465
|
+
async function checkAndUpdate(opts = {}) {
|
|
428
466
|
const policy = resolveAutoUpdatePolicy();
|
|
429
467
|
if (policy.blocked) {
|
|
430
468
|
return { hasUpdate: false, info: null, updated: false, message: '跳过更新检查(已显式禁用)' };
|
|
@@ -528,13 +566,13 @@ export async function checkAndUpdate(opts = {}) {
|
|
|
528
566
|
/**
|
|
529
567
|
* 仅检查更新,不自动安装
|
|
530
568
|
*/
|
|
531
|
-
|
|
569
|
+
async function checkForUpdates() {
|
|
532
570
|
return await checkBolloonUpdates();
|
|
533
571
|
}
|
|
534
572
|
/**
|
|
535
573
|
* 手动触发更新
|
|
536
574
|
*/
|
|
537
|
-
|
|
575
|
+
async function performUpdate(packages) {
|
|
538
576
|
return await updatePackages(packages);
|
|
539
577
|
}
|
|
540
578
|
// CLI 入口
|
|
@@ -557,3 +595,4 @@ if (process.argv[1]?.includes('auto-update')) {
|
|
|
557
595
|
}
|
|
558
596
|
})();
|
|
559
597
|
}
|
|
598
|
+
//# sourceMappingURL=auto-update.js.map
|