@bolloon/bolloon-agent 0.1.32 → 0.1.34
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 +7 -2
- package/dist/agents/pi-sdk.js +10 -1
- package/dist/bollharness-integration/index.js +8 -1
- package/dist/heartbeat/Watchdog.js +9 -1
- package/dist/llm/audio-config-store.js +199 -0
- package/dist/llm/config-store.js +20 -10
- package/dist/llm/pi-ai.js +2 -2
- package/dist/llm/video-config-store.js +31 -1
- package/dist/network/p2p-direct.js +59 -2
- package/dist/pi-ecosystem/index.js +10 -7
- package/dist/pi-ecosystem-judgment/decision.js +5 -2
- package/dist/social/heartbeat.js +19 -2
- package/dist/web/api-config.html +16 -4
- package/dist/web/client.js +1017 -137
- package/dist/web/index.html +10 -27
- package/dist/web/server.js +865 -52
- package/dist/web/style.css +370 -0
- package/package.json +2 -1
- package/src/agents/pi-sdk.ts +9 -1
- package/src/bollharness-integration/index.ts +8 -32
- package/src/heartbeat/Watchdog.ts +9 -1
- package/src/llm/audio-config-store.ts +6 -1
- package/src/llm/config-store.ts +21 -11
- package/src/llm/pi-ai.ts +2 -2
- package/src/llm/video-config-store.ts +7 -1
- package/src/network/p2p-direct.ts +59 -3
- package/src/social/ant-colony/index.js +19 -0
- package/src/social/heartbeat.ts +18 -2
- package/src/web/api-config.html +16 -4
- package/src/web/client.js +1017 -137
- package/src/web/index.html +10 -27
- package/src/web/server.ts +810 -47
- package/src/web/style.css +370 -0
- package/src/social/ant-colony/AdaptiveHeartbeat.ts +0 -131
- package/src/social/ant-colony/PheromoneEngine.ts +0 -302
- package/src/social/ant-colony/index.ts +0 -18
- package/src/social/ant-colony/types.ts +0 -94
package/README.md
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
# Bolloon -
|
|
1
|
+
# Bolloon - 判断力对齐协议
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
> **⚠️ 2026-06-10 重新校准**:本文档下方的"核心效果"是历史叙事。**Bolloon 的真正定义见 [docs/真正要做的事.md](docs/真正要做的事.md)**。
|
|
4
|
+
> 一句话:**Bolloon 是 A 和 B 对接判断力的协议层——让 A 和 B 在做重要决定前,把双方的 3 个反方观点互相看见。**
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 核心效果(历史叙事,即将更新)
|
|
4
9
|
|
|
5
10
|
> **两个有判断力的人的决策,被 Bolloon 记录后,异步进行合作,由 Harness 驱动自动化执行。**
|
|
6
11
|
|
package/dist/agents/pi-sdk.js
CHANGED
|
@@ -914,6 +914,7 @@ ${toolDefs}
|
|
|
914
914
|
const response = await llm.chat(context, systemPrompt);
|
|
915
915
|
const reply = response.reply.trim();
|
|
916
916
|
console.log(`[PiAgent] LLM 回复长度: ${reply.length}, 内容预览: "${reply.substring(0, 80)}..."`);
|
|
917
|
+
console.log(`[PiAgent] LLM 完整回复:\n${reply}`);
|
|
917
918
|
// 通知前端:收到 LLM 回复
|
|
918
919
|
if (onStream) {
|
|
919
920
|
onStream({ type: 'token', content: reply.substring(0, 100) });
|
|
@@ -1159,7 +1160,15 @@ Workspace root folder: ${this.cwd}
|
|
|
1159
1160
|
const marker = '<final gen>';
|
|
1160
1161
|
const markerIndex = content.indexOf(marker);
|
|
1161
1162
|
if (markerIndex !== -1) {
|
|
1162
|
-
|
|
1163
|
+
const after = content.substring(markerIndex + marker.length).trim();
|
|
1164
|
+
// v3 修复: 如果 <final gen> 之后是空, fallback 用 marker 之前的内容 (去掉 marker)
|
|
1165
|
+
// 否则 LLM 写了 <final gen> 在末尾时, 用户看到空回复 + error
|
|
1166
|
+
if (after) {
|
|
1167
|
+
content = after;
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
content = content.substring(0, markerIndex).trim();
|
|
1171
|
+
}
|
|
1163
1172
|
}
|
|
1164
1173
|
// 移除任何 tool call 标记
|
|
1165
1174
|
let cleaned = content
|
|
@@ -23,7 +23,14 @@ export { getJudgmentsForPath, getJudgmentsForFragment, getJudgmentsForContextReq
|
|
|
23
23
|
export { initializeMcpAdapter, discoverMcpServers, registerServer, registerTool, listTools, hasTool, getTool, executeTool, getToolCallLog, getAdapterStatus, } from '../pi-ecosystem-mcp/index.js';
|
|
24
24
|
export { createGoal, createGoalQueue, getCurrentGoal, startCurrentGoal, completeCurrentGoal, failCurrentGoal, cutoffCurrentGoal, pauseCurrentGoal, checkBudget, getGoalStats, getQueueSummary, loadGoals, clearGoals, compactQueue, loadTemplates, createFromTemplate, nudgeCurrentGoal, } from '../pi-ecosystem-goals/index.js';
|
|
25
25
|
export { createSubagent, startSubagent, delegateTask, getSubagent, listSubagents, listRunningSubagents, terminateSubagent, getStats as getSubagentStats, parallelDelegate, splitTask, } from '../pi-ecosystem-subagents/index.js';
|
|
26
|
-
|
|
26
|
+
// 2026-06-11: 蚁群模块 (pi-ecosystem-colony) 已被用户删除, 移除对应 re-export 防止启动失败
|
|
27
|
+
// export {
|
|
28
|
+
// registerAnt, antScouting, antWorking, antReviewing, antComplete, antFail, antAbort, antTick,
|
|
29
|
+
// createTask, dispatchTask, recordResult, getAnt, listAnts, listAntsByRole, listAntsBySignal,
|
|
30
|
+
// getActiveAnts, getTask, listTasks, getSignalHistory, getColonyStatus, getColonyDump,
|
|
31
|
+
// persistColony, loadColony, onColonyEvent, offColonyEvent,
|
|
32
|
+
// type Ant, type ColonyTask, type ColonySignalEvent, type ColonySignal, type AntRole,
|
|
33
|
+
// } from '../pi-ecosystem-colony/index.js';
|
|
27
34
|
// Judgment exports
|
|
28
35
|
export { createJudgment, getAllJudgments, getJudgmentsByType, getJudgmentsForContext, getCombinedJudgments, calculateConfidence, buildValueFunction, getValueFunction, getJudgmentStats, loadFragmentJudgments, } from '../pi-ecosystem-judgment/index.js';
|
|
29
36
|
// Distillation exports
|
|
@@ -30,11 +30,19 @@ export class Watchdog {
|
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
32
32
|
* 记录活动(调用后更新 lastActivityTime)
|
|
33
|
+
* 2026-06-10: 加 5s 去抖, 防 broadcast / SSE 高频路径炸 log + CPU
|
|
34
|
+
* - lastActivityTime 始终更新 (cheap)
|
|
35
|
+
* - 但 onLog 回调只在距上次 ≥5s 时触发, 避免每秒几十次 console.log
|
|
33
36
|
*/
|
|
37
|
+
_lastLogAt = 0;
|
|
34
38
|
recordActivity(component) {
|
|
35
39
|
this.state.lastActivityTime = Date.now();
|
|
36
40
|
if (component) {
|
|
37
|
-
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
if (now - this._lastLogAt >= 5000) {
|
|
43
|
+
this._lastLogAt = now;
|
|
44
|
+
this.callbacks.onLog?.(`[Watchdog] Activity from ${component}`);
|
|
45
|
+
}
|
|
38
46
|
}
|
|
39
47
|
}
|
|
40
48
|
/**
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Audio Generation API Configuration Store
|
|
3
|
+
*
|
|
4
|
+
* 音频模型配置:MiniMax 提供的 Speech(TTS+ASR)与 Music(文生音乐)。
|
|
5
|
+
* 与 LLM / 视频配置完全独立,持久化到 ~/.bolloon/audio-config.json。
|
|
6
|
+
*
|
|
7
|
+
* 复用 LLM 那一套 MINIMAX_API_KEY 即可(同源)。
|
|
8
|
+
* - TTS: POST /audio/speech (OpenAI 兼容,body 含 model/voice/input)
|
|
9
|
+
* - ASR: POST /audio/transcriptions
|
|
10
|
+
* - Music: POST /music_generation (MiniMax 自有端点)
|
|
11
|
+
*/
|
|
12
|
+
import * as fs from 'fs/promises';
|
|
13
|
+
import * as path from 'path';
|
|
14
|
+
const CONFIG_DIR = path.join(process.env.HOME || '/tmp', '.bolloon');
|
|
15
|
+
const CONFIG_PATH = path.join(CONFIG_DIR, 'audio-config.json');
|
|
16
|
+
export const DEFAULT_AUDIO_PROVIDER_CONFIGS = {
|
|
17
|
+
'minimax-speech': {
|
|
18
|
+
enabled: false,
|
|
19
|
+
apiKey: '',
|
|
20
|
+
baseUrl: 'https://api.minimaxi.com/v1',
|
|
21
|
+
model: 'speech-01',
|
|
22
|
+
voice: 'male-qn-jingying',
|
|
23
|
+
speed: 1.0,
|
|
24
|
+
format: 'mp3',
|
|
25
|
+
requiresApiKey: true
|
|
26
|
+
},
|
|
27
|
+
'minimax-music': {
|
|
28
|
+
enabled: false,
|
|
29
|
+
apiKey: '',
|
|
30
|
+
baseUrl: 'https://api.minimaxi.com/v1',
|
|
31
|
+
model: 'music-01',
|
|
32
|
+
mode: 'instrumental',
|
|
33
|
+
duration: 30,
|
|
34
|
+
requiresApiKey: true
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
export const AUDIO_PROVIDER_INFO = {
|
|
38
|
+
'minimax-speech': {
|
|
39
|
+
name: 'MiniMax Speech',
|
|
40
|
+
description: 'TTS 文生语音 / ASR 语音转写',
|
|
41
|
+
requiresApiKey: true,
|
|
42
|
+
docs: 'https://platform.minimaxi.com/document/T2A%20V2',
|
|
43
|
+
kind: 'speech'
|
|
44
|
+
},
|
|
45
|
+
'minimax-music': {
|
|
46
|
+
name: 'MiniMax Music',
|
|
47
|
+
description: '文生音乐 (纯音乐 / 带歌词)',
|
|
48
|
+
requiresApiKey: true,
|
|
49
|
+
docs: 'https://platform.minimaxi.com/document/Music%20Generation',
|
|
50
|
+
kind: 'music'
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
function getDefaultConfig() {
|
|
54
|
+
const envConfigs = {};
|
|
55
|
+
const sharedKey = process.env.MINIMAX_API_KEY || '';
|
|
56
|
+
if (sharedKey) {
|
|
57
|
+
envConfigs['minimax-speech'] = {
|
|
58
|
+
...DEFAULT_AUDIO_PROVIDER_CONFIGS['minimax-speech'],
|
|
59
|
+
enabled: true,
|
|
60
|
+
apiKey: sharedKey
|
|
61
|
+
};
|
|
62
|
+
envConfigs['minimax-music'] = {
|
|
63
|
+
...DEFAULT_AUDIO_PROVIDER_CONFIGS['minimax-music'],
|
|
64
|
+
enabled: true,
|
|
65
|
+
apiKey: sharedKey
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
const activeProvider = 'minimax-speech';
|
|
69
|
+
const providers = { ...DEFAULT_AUDIO_PROVIDER_CONFIGS };
|
|
70
|
+
for (const [provider, config] of Object.entries(envConfigs)) {
|
|
71
|
+
if (config) {
|
|
72
|
+
providers[provider] = config;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
activeProvider,
|
|
77
|
+
providers,
|
|
78
|
+
updatedAt: new Date().toISOString()
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
class AudioConfigStore {
|
|
82
|
+
config = null;
|
|
83
|
+
initialized = false;
|
|
84
|
+
async initialize() {
|
|
85
|
+
if (this.initialized)
|
|
86
|
+
return;
|
|
87
|
+
try {
|
|
88
|
+
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
89
|
+
const data = await fs.readFile(CONFIG_PATH, 'utf-8');
|
|
90
|
+
const loadedConfig = JSON.parse(data);
|
|
91
|
+
// 补齐缺失的供应商
|
|
92
|
+
const defaultProviders = Object.keys(DEFAULT_AUDIO_PROVIDER_CONFIGS);
|
|
93
|
+
for (const provider of defaultProviders) {
|
|
94
|
+
if (!loadedConfig.providers[provider]) {
|
|
95
|
+
loadedConfig.providers[provider] = { ...DEFAULT_AUDIO_PROVIDER_CONFIGS[provider] };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
const activeProvider = loadedConfig.activeProvider;
|
|
99
|
+
if (!activeProvider || !DEFAULT_AUDIO_PROVIDER_CONFIGS[activeProvider]) {
|
|
100
|
+
loadedConfig.activeProvider = 'minimax-speech';
|
|
101
|
+
}
|
|
102
|
+
this.config = loadedConfig;
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
this.config = getDefaultConfig();
|
|
106
|
+
await this.save();
|
|
107
|
+
}
|
|
108
|
+
this.initialized = true;
|
|
109
|
+
}
|
|
110
|
+
async save() {
|
|
111
|
+
if (!this.config)
|
|
112
|
+
return;
|
|
113
|
+
this.config.updatedAt = new Date().toISOString();
|
|
114
|
+
await fs.writeFile(CONFIG_PATH, JSON.stringify(this.config, null, 2));
|
|
115
|
+
}
|
|
116
|
+
async getConfig() {
|
|
117
|
+
await this.initialize();
|
|
118
|
+
return { ...this.config };
|
|
119
|
+
}
|
|
120
|
+
async getProvider(provider) {
|
|
121
|
+
await this.initialize();
|
|
122
|
+
return this.config?.providers[provider] || null;
|
|
123
|
+
}
|
|
124
|
+
async getActiveProvider() {
|
|
125
|
+
await this.initialize();
|
|
126
|
+
return this.config?.activeProvider || 'minimax-speech';
|
|
127
|
+
}
|
|
128
|
+
async getActiveProviderConfig() {
|
|
129
|
+
await this.initialize();
|
|
130
|
+
const provider = this.config?.activeProvider;
|
|
131
|
+
if (!provider)
|
|
132
|
+
return null;
|
|
133
|
+
return this.config?.providers[provider] || null;
|
|
134
|
+
}
|
|
135
|
+
async setActiveProvider(provider) {
|
|
136
|
+
await this.initialize();
|
|
137
|
+
if (!this.config?.providers[provider]) {
|
|
138
|
+
throw new Error(`Unknown audio provider: ${provider}`);
|
|
139
|
+
}
|
|
140
|
+
this.config.activeProvider = provider;
|
|
141
|
+
await this.save();
|
|
142
|
+
}
|
|
143
|
+
async updateProvider(provider, updates) {
|
|
144
|
+
await this.initialize();
|
|
145
|
+
if (!this.config?.providers[provider]) {
|
|
146
|
+
throw new Error(`Unknown audio provider: ${provider}`);
|
|
147
|
+
}
|
|
148
|
+
this.config.providers[provider] = {
|
|
149
|
+
...this.config.providers[provider],
|
|
150
|
+
...updates
|
|
151
|
+
};
|
|
152
|
+
await this.save();
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 测试连接:探测 /models 端点。
|
|
156
|
+
*/
|
|
157
|
+
async testProvider(provider) {
|
|
158
|
+
await this.initialize();
|
|
159
|
+
const config = this.config?.providers[provider];
|
|
160
|
+
if (!config)
|
|
161
|
+
return { success: false, error: 'Provider not configured' };
|
|
162
|
+
if (!config.enabled)
|
|
163
|
+
return { success: false, error: 'Provider is not enabled' };
|
|
164
|
+
if (config.requiresApiKey && !config.apiKey) {
|
|
165
|
+
return { success: false, error: 'API key is required' };
|
|
166
|
+
}
|
|
167
|
+
const startTime = Date.now();
|
|
168
|
+
try {
|
|
169
|
+
const response = await fetch(`${config.baseUrl.replace(/\/$/, '')}/models`, {
|
|
170
|
+
method: 'GET',
|
|
171
|
+
headers: { 'Authorization': `Bearer ${config.apiKey}` }
|
|
172
|
+
});
|
|
173
|
+
const latency = Date.now() - startTime;
|
|
174
|
+
if (response.ok) {
|
|
175
|
+
return { success: true, latency };
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
const errorText = await response.text().catch(() => 'Unknown error');
|
|
179
|
+
const hint = response.status === 401
|
|
180
|
+
? '(请确认是 MiniMax 的 API Key)'
|
|
181
|
+
: response.status === 404
|
|
182
|
+
? '(端点不存在 — 请检查 baseUrl)'
|
|
183
|
+
: '';
|
|
184
|
+
return {
|
|
185
|
+
success: false,
|
|
186
|
+
error: `HTTP ${response.status}: ${errorText.substring(0, 500)}${hint ? ' ' + hint : ''}`,
|
|
187
|
+
latency
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
return { success: false, error: error.message || 'Connection failed', latency: Date.now() - startTime };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
getAllProviderInfo() {
|
|
196
|
+
return AUDIO_PROVIDER_INFO;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
export const audioConfigStore = new AudioConfigStore();
|
package/dist/llm/config-store.js
CHANGED
|
@@ -56,7 +56,7 @@ export const DEFAULT_PROVIDER_CONFIGS = {
|
|
|
56
56
|
enabled: false,
|
|
57
57
|
apiKey: '',
|
|
58
58
|
baseUrl: 'https://api.minimaxi.com/v1',
|
|
59
|
-
model: 'MiniMax-
|
|
59
|
+
model: 'MiniMax-M3',
|
|
60
60
|
temperature: 0.7,
|
|
61
61
|
maxTokens: 4096,
|
|
62
62
|
requiresApiKey: true
|
|
@@ -108,16 +108,21 @@ export const DEFAULT_PROVIDER_CONFIGS = {
|
|
|
108
108
|
}
|
|
109
109
|
};
|
|
110
110
|
export const PROVIDER_INFO = {
|
|
111
|
-
openai: { name: 'OpenAI', description: 'GPT-4, GPT-3.5 等模型', requiresApiKey: true },
|
|
112
|
-
anthropic: { name: 'Anthropic', description: 'Claude 3.5 系列模型', requiresApiKey: true },
|
|
111
|
+
openai: { name: 'OpenAI', description: 'GPT-4, GPT-3.5 等模型', requiresApiKey: true, models: ['gpt-4', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gpt-3.5-turbo'] },
|
|
112
|
+
anthropic: { name: 'Anthropic', description: 'Claude 3.5 系列模型', requiresApiKey: true, models: ['claude-3-5-sonnet-20241022', 'claude-3-5-haiku-20241022', 'claude-3-opus-20240229'] },
|
|
113
113
|
openrouter: { name: 'OpenRouter', description: '聚合多个 AI 供应商', requiresApiKey: true },
|
|
114
|
-
gemini: { name: 'Google Gemini', description: 'Gemini 系列模型', requiresApiKey: true },
|
|
114
|
+
gemini: { name: 'Google Gemini', description: 'Gemini 系列模型', requiresApiKey: true, models: ['gemini-2.0-flash', 'gemini-1.5-pro', 'gemini-1.5-flash'] },
|
|
115
115
|
ollama: { name: 'Ollama', description: '本地 LLM 运行框架', requiresApiKey: false },
|
|
116
|
-
minimax: {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
116
|
+
minimax: {
|
|
117
|
+
name: 'MiniMax',
|
|
118
|
+
description: '国产大模型服务',
|
|
119
|
+
requiresApiKey: true,
|
|
120
|
+
models: ['MiniMax-M3', 'MiniMax-M2.7', 'MiniMax-M2', 'MiniMax-M2.1-highspeed', 'MiniMax-M2.7-highspeed']
|
|
121
|
+
},
|
|
122
|
+
deepseek: { name: 'DeepSeek', description: '深度求索大模型', requiresApiKey: true, models: ['deepseek-chat', 'deepseek-reasoner'] },
|
|
123
|
+
kimi: { name: 'Kimi (月之暗面)', description: 'Moonshot 长上下文模型', requiresApiKey: true, models: ['moonshot-v1-8k', 'moonshot-v1-32k', 'moonshot-v1-128k'] },
|
|
124
|
+
glm: { name: 'GLM (智谱)', description: '智谱 ChatGLM 系列模型', requiresApiKey: true, models: ['glm-4-flash', 'glm-4', 'glm-4-plus', 'glm-4-air', 'glm-4-airx'] },
|
|
125
|
+
qwen: { name: 'Qwen (通义千问)', description: '阿里云通义千问系列', requiresApiKey: true, models: ['qwen-plus', 'qwen-max', 'qwen-turbo', 'qwen-long'] },
|
|
121
126
|
local: { name: '本地模型', description: '本地部署的模型服务', requiresApiKey: false }
|
|
122
127
|
};
|
|
123
128
|
function getDefaultConfig() {
|
|
@@ -284,7 +289,12 @@ class LLMConfigStore {
|
|
|
284
289
|
}
|
|
285
290
|
else {
|
|
286
291
|
const errorText = await response.text().catch(() => 'Unknown error');
|
|
287
|
-
|
|
292
|
+
const hint = response.status === 401
|
|
293
|
+
? '(API Key 无效或不匹配该供应商 — 请检查是否复制完整、有无多余空格)'
|
|
294
|
+
: response.status === 404
|
|
295
|
+
? '(端点不存在 — 请检查 baseUrl)'
|
|
296
|
+
: '';
|
|
297
|
+
return { success: false, error: `HTTP ${response.status}: ${errorText.substring(0, 500)}${hint ? ' ' + hint : ''}`, latency };
|
|
288
298
|
}
|
|
289
299
|
}
|
|
290
300
|
catch (error) {
|
package/dist/llm/pi-ai.js
CHANGED
|
@@ -133,7 +133,7 @@ export class PiAIModel {
|
|
|
133
133
|
ollama: this.config.model || 'llama3.2',
|
|
134
134
|
openrouter: this.config.model || 'anthropic/claude-3.5-sonnet',
|
|
135
135
|
gemini: this.config.model || 'gemini-2.0-flash',
|
|
136
|
-
minimax: this.config.model || process.env.MINIMAX_MODEL || 'MiniMax-
|
|
136
|
+
minimax: this.config.model || process.env.MINIMAX_MODEL || 'MiniMax-M3',
|
|
137
137
|
deepseek: this.config.model || process.env.DEEPSEEK_MODEL || 'deepseek-chat',
|
|
138
138
|
kimi: this.config.model || process.env.KIMI_MODEL || process.env.MOONSHOT_MODEL || 'moonshot-v1-8k',
|
|
139
139
|
glm: this.config.model || process.env.GLM_MODEL || process.env.ZHIPU_MODEL || 'glm-4-flash',
|
|
@@ -401,7 +401,7 @@ function detectModel(provider) {
|
|
|
401
401
|
ollama: 'llama3.2',
|
|
402
402
|
openrouter: 'anthropic/claude-3.5-sonnet',
|
|
403
403
|
gemini: 'gemini-2.0-flash',
|
|
404
|
-
minimax: 'MiniMax-
|
|
404
|
+
minimax: 'MiniMax-M3',
|
|
405
405
|
deepseek: 'deepseek-chat',
|
|
406
406
|
kimi: 'moonshot-v1-8k',
|
|
407
407
|
glm: 'glm-4-flash',
|
|
@@ -19,6 +19,16 @@ export const DEFAULT_VIDEO_PROVIDER_CONFIGS = {
|
|
|
19
19
|
duration: 5,
|
|
20
20
|
ratio: '16:9',
|
|
21
21
|
requiresApiKey: true
|
|
22
|
+
},
|
|
23
|
+
'minimax-video': {
|
|
24
|
+
enabled: false,
|
|
25
|
+
apiKey: '',
|
|
26
|
+
baseUrl: 'https://api.minimaxi.com/v1',
|
|
27
|
+
model: 'MiniMax-video-01',
|
|
28
|
+
resolution: '720p',
|
|
29
|
+
duration: 6,
|
|
30
|
+
ratio: '16:9',
|
|
31
|
+
requiresApiKey: true
|
|
22
32
|
}
|
|
23
33
|
};
|
|
24
34
|
export const VIDEO_PROVIDER_INFO = {
|
|
@@ -27,6 +37,12 @@ export const VIDEO_PROVIDER_INFO = {
|
|
|
27
37
|
description: '字节跳动文生视频 / 图生视频模型',
|
|
28
38
|
requiresApiKey: true,
|
|
29
39
|
docs: 'https://www.volcengine.com/docs/82379'
|
|
40
|
+
},
|
|
41
|
+
'minimax-video': {
|
|
42
|
+
name: 'MiniMax Video',
|
|
43
|
+
description: 'MiniMax 文生视频 (Video-01)',
|
|
44
|
+
requiresApiKey: true,
|
|
45
|
+
docs: 'https://platform.minimaxi.com/document/Video%20Generation'
|
|
30
46
|
}
|
|
31
47
|
};
|
|
32
48
|
function getDefaultConfig() {
|
|
@@ -38,6 +54,14 @@ function getDefaultConfig() {
|
|
|
38
54
|
apiKey: process.env.SEEDANCE_API_KEY || process.env.ARK_API_KEY || ''
|
|
39
55
|
};
|
|
40
56
|
}
|
|
57
|
+
const sharedKey = process.env.MINIMAX_API_KEY || '';
|
|
58
|
+
if (sharedKey) {
|
|
59
|
+
envConfigs['minimax-video'] = {
|
|
60
|
+
...DEFAULT_VIDEO_PROVIDER_CONFIGS['minimax-video'],
|
|
61
|
+
enabled: true,
|
|
62
|
+
apiKey: sharedKey
|
|
63
|
+
};
|
|
64
|
+
}
|
|
41
65
|
const activeProvider = 'seedance';
|
|
42
66
|
const providers = { ...DEFAULT_VIDEO_PROVIDER_CONFIGS };
|
|
43
67
|
for (const [provider, config] of Object.entries(envConfigs)) {
|
|
@@ -153,9 +177,15 @@ class VideoConfigStore {
|
|
|
153
177
|
}
|
|
154
178
|
else {
|
|
155
179
|
const errorText = await response.text().catch(() => 'Unknown error');
|
|
180
|
+
// 401 通常是 key 错误,给出针对性提示
|
|
181
|
+
const hint = response.status === 401
|
|
182
|
+
? '(请确认是火山方舟 ARK 的 API Key,不是 MiniMax / 其他平台)'
|
|
183
|
+
: response.status === 404
|
|
184
|
+
? '(端点不存在 — 火山方舟可能没有 /models,请检查 baseUrl)'
|
|
185
|
+
: '';
|
|
156
186
|
return {
|
|
157
187
|
success: false,
|
|
158
|
-
error: `HTTP ${response.status}: ${errorText.substring(0,
|
|
188
|
+
error: `HTTP ${response.status}: ${errorText.substring(0, 500)}${hint ? ' ' + hint : ''}`,
|
|
159
189
|
latency
|
|
160
190
|
};
|
|
161
191
|
}
|
|
@@ -58,9 +58,9 @@ export class P2PDirect extends EventEmitter {
|
|
|
58
58
|
console.log(`[P2PDirect:${this.name}] 新连接: ${remotePubKeyHex.substring(0, 12)}... (inbound=${info.inbound || false}, type=${typeof conn}, hasWrite=${typeof conn?.write})`);
|
|
59
59
|
// 双向记录 (inbound + outbound 都能拿到)
|
|
60
60
|
this.conns.set(remotePubKeyHex, conn);
|
|
61
|
-
// v3: 触发 'connection' 事件, 上层 (web server) 可以主动给新连接发消息
|
|
62
|
-
this.emit('connection', { remotePublicKey: remotePubKeyHex, conn });
|
|
63
61
|
// 收到数据时 → 触发 'data' 事件
|
|
62
|
+
// 注意: data 监听器必须在 emit('connection') 之前注册,
|
|
63
|
+
// 否则 server 的 connection handler 发送消息后, 对端回复可能在 data 监听器就绪前到达
|
|
64
64
|
conn.on('data', (chunk) => {
|
|
65
65
|
const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
66
66
|
console.log(`[P2PDirect:${this.name}] 收到数据 from ${remotePubKeyHex.substring(0, 12)}... (${buf.length} bytes)`);
|
|
@@ -75,6 +75,9 @@ export class P2PDirect extends EventEmitter {
|
|
|
75
75
|
conn.on('close', () => {
|
|
76
76
|
this.conns.delete(remotePubKeyHex);
|
|
77
77
|
});
|
|
78
|
+
// v3: 触发 'connection' 事件, 上层 (web server) 可以主动给新连接发消息
|
|
79
|
+
// 注意: 放在 data/error/close 监听器之后, 确保 server 的 connection handler 不会先于 data 就绪
|
|
80
|
+
this.emit('connection', { remotePublicKey: remotePubKeyHex, conn });
|
|
78
81
|
});
|
|
79
82
|
await this.swarm.listen(); // server 模式
|
|
80
83
|
this.started = true;
|
|
@@ -127,6 +130,60 @@ export class P2PDirect extends EventEmitter {
|
|
|
127
130
|
return false;
|
|
128
131
|
}
|
|
129
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* 2026-06-10: 真"主动发, 等握手完成"版本 — 修复好友申请 fire-and-forget bug.
|
|
135
|
+
*
|
|
136
|
+
* 之前的问题: server.ts:2914 `await swarm.joinPeer(...)` 只触发握手, conn 还没 push 进 this.conns,
|
|
137
|
+
* 立即调 sendTo 找不到 conn → 静默返回 false → 消息扔进虚空.
|
|
138
|
+
*
|
|
139
|
+
* 现在: sendToWithWait 监听 'connection' 事件, 等到 targetPublicKey 真正出现在 this.conns,
|
|
140
|
+
* 才 write; 超时返回 NO_CONN; 写失败返回 WRITE_FAIL; 成功返回 SENT.
|
|
141
|
+
*
|
|
142
|
+
* 上层调用: const r = await p2p.sendToWithWait(pk, rpc, 5000);
|
|
143
|
+
* if (r !== 'SENT') return 502 给前端.
|
|
144
|
+
*/
|
|
145
|
+
async sendToWithWait(publicKeyHex, data, timeoutMs = 5000) {
|
|
146
|
+
// 2026-06-11: 先主动触发 joinPeer, 否则 DHT 上对面可能没 push conn
|
|
147
|
+
if (this.swarm) {
|
|
148
|
+
try {
|
|
149
|
+
await this.swarm.joinPeer(Buffer.from(publicKeyHex, 'hex'));
|
|
150
|
+
}
|
|
151
|
+
catch { }
|
|
152
|
+
}
|
|
153
|
+
// 1) 已有 conn → 立即试
|
|
154
|
+
let conn = this.conns.get(publicKeyHex);
|
|
155
|
+
if (!conn || conn.destroyed) {
|
|
156
|
+
// 2) 等 'connection' 事件 (this.emit('connection', { remotePublicKey, conn }))
|
|
157
|
+
const waitResult = await new Promise((resolve) => {
|
|
158
|
+
const timer = setTimeout(() => {
|
|
159
|
+
this.off('connection', onConn);
|
|
160
|
+
resolve('TIMEOUT');
|
|
161
|
+
}, timeoutMs);
|
|
162
|
+
const onConn = (evt) => {
|
|
163
|
+
if (evt.remotePublicKey === publicKeyHex) {
|
|
164
|
+
clearTimeout(timer);
|
|
165
|
+
this.off('connection', onConn);
|
|
166
|
+
resolve('READY');
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
this.on('connection', onConn);
|
|
170
|
+
});
|
|
171
|
+
if (waitResult === 'TIMEOUT')
|
|
172
|
+
return 'NO_CONN';
|
|
173
|
+
conn = this.conns.get(publicKeyHex);
|
|
174
|
+
if (!conn || conn.destroyed)
|
|
175
|
+
return 'NO_CONN'; // 双保险
|
|
176
|
+
}
|
|
177
|
+
const buf = typeof data === 'string' ? Buffer.from(data) : data;
|
|
178
|
+
try {
|
|
179
|
+
conn.write(buf);
|
|
180
|
+
return 'SENT';
|
|
181
|
+
}
|
|
182
|
+
catch (err) {
|
|
183
|
+
console.error(`[P2PDirect:${this.name}] sendToWithWait 写失败 (${publicKeyHex.substring(0, 12)}...):`, err.message);
|
|
184
|
+
return 'WRITE_FAIL';
|
|
185
|
+
}
|
|
186
|
+
}
|
|
130
187
|
getPublicKey() {
|
|
131
188
|
if (!this.swarm)
|
|
132
189
|
return '';
|
|
@@ -28,7 +28,7 @@ import * as path from 'path';
|
|
|
28
28
|
export { initializeMcpAdapter, discoverMcpServers, registerServer, registerTool, listTools, hasTool, getTool, executeTool, getToolCallLog, clearToolCallLog, startServer, stopServer, discoverTools, createTavilyTool, createAmapTool, getAdapterStatus, on as onMcpEvent, off as offMcpEvent, } from '../pi-ecosystem-mcp/index.js';
|
|
29
29
|
export { createGoal, createGoalQueue, getCurrentGoal, startCurrentGoal, completeCurrentGoal, failCurrentGoal, cutoffCurrentGoal, pauseCurrentGoal, checkBudget, getGoalStats, getQueueSummary, loadGoals, clearGoals, compactQueue, loadTemplates, createFromTemplate, nudgeCurrentGoal, } from '../pi-ecosystem-goals/index.js';
|
|
30
30
|
export { createSubagent, startSubagent, delegateTask, getSubagent, listSubagents, listRunningSubagents, terminateSubagent, getStats as getSubagentStats, parallelDelegate, splitTask, } from '../pi-ecosystem-subagents/index.js';
|
|
31
|
-
export { registerAnt, antScouting, antWorking, antReviewing, antComplete, antFail, antAbort, antTick, createTask, dispatchTask, recordResult, getAnt, listAnts, listAntsByRole, listAntsBySignal, getActiveAnts, getTask, listTasks, getSignalHistory, getColonyStatus, getColonyDump, persistColony, loadColony, onColonyEvent, offColonyEvent, } from '../pi-ecosystem-colony/index.js';
|
|
31
|
+
export { registerAnt, antScouting, antWorking, antReviewing, antComplete, antFail, antAbort, antTick, createTask, dispatchTask, recordResult, getAnt, listAnts, listAntsByRole, listAntsBySignal, getActiveAnts, getTask, listTasks, getSignalHistory, getColonyStatus, getColonyDump, persistColony, loadColony, onColonyEvent, offColonyEvent, } from '../pi-ecosystem-colony/index.js'; // 2026-06-11: 蚁群模块已被用户删除, 这行已经无效 (改用 stub)
|
|
32
32
|
// Judgment exports
|
|
33
33
|
export { createJudgment, updateJudgmentConfidence, getAllJudgments, getJudgmentsByType, getJudgmentsForContext, getCombinedJudgments, calculateConfidence, buildValueFunction, getValueFunction, getJudgmentStats, loadFragmentJudgments, clearCache, } from '../pi-ecosystem-judgment/index.js';
|
|
34
34
|
// Distillation exports
|
|
@@ -41,7 +41,7 @@ const CONFIG_FILE = path.join(PI_ECOSYSTEM_DIR, 'config.json');
|
|
|
41
41
|
* Default Pi ecosystem configuration (mirrors oh-pi)
|
|
42
42
|
*/
|
|
43
43
|
const DEFAULT_CONFIG = {
|
|
44
|
-
model: process.env.MINIMAX_MODEL || 'MiniMax-
|
|
44
|
+
model: process.env.MINIMAX_MODEL || 'MiniMax-M3',
|
|
45
45
|
extensions: [
|
|
46
46
|
'safe-guard',
|
|
47
47
|
'git-guard',
|
|
@@ -198,17 +198,20 @@ export class PiEcosystem {
|
|
|
198
198
|
}
|
|
199
199
|
async registerAnt(name, role) {
|
|
200
200
|
const colony = await import('../pi-ecosystem-colony/index.js');
|
|
201
|
-
|
|
202
|
-
|
|
201
|
+
// 2026-06-11: colony 已退化为 stub, registerAnt 不接参数, 返回 void
|
|
202
|
+
colony.registerAnt();
|
|
203
|
+
return name;
|
|
203
204
|
}
|
|
204
205
|
async createColonyTask(description) {
|
|
205
206
|
const colony = await import('../pi-ecosystem-colony/index.js');
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
// 2026-06-11: createTask 不接参数, 返回空对象
|
|
208
|
+
const task = colony.createTask();
|
|
209
|
+
return task?.id ?? description;
|
|
208
210
|
}
|
|
209
211
|
async dispatchToColony(taskId, antIds) {
|
|
210
212
|
const colony = await import('../pi-ecosystem-colony/index.js');
|
|
211
|
-
|
|
213
|
+
// 2026-06-11: dispatchTask 已退化为 stub
|
|
214
|
+
colony.dispatchTask();
|
|
212
215
|
}
|
|
213
216
|
async persist() {
|
|
214
217
|
const colony = await import('../pi-ecosystem-colony/index.js');
|
|
@@ -20,8 +20,11 @@
|
|
|
20
20
|
import { EventEmitter } from 'events';
|
|
21
21
|
import { getValueFunction, calculateConfidence, } from './index.js';
|
|
22
22
|
import { distillInput, isJudgmentSignal, detectTrigger, processFeedback, } from './distillation.js';
|
|
23
|
-
|
|
23
|
+
// 2026-06-11: 蚁群模块已被用户删除, 移除 import 防止启动失败, 加本地 stub
|
|
24
|
+
// import { listAnts, type Ant } from '../pi-ecosystem-colony/index.js';
|
|
24
25
|
import { listSubagents } from '../pi-ecosystem-subagents/index.js';
|
|
26
|
+
// Stub: 蚁群删除后, 永远返回空列表
|
|
27
|
+
function listAnts() { return []; }
|
|
25
28
|
class DecisionEventEmitter extends EventEmitter {
|
|
26
29
|
}
|
|
27
30
|
const decisionEvents = new DecisionEventEmitter();
|
|
@@ -198,7 +201,7 @@ async function consultColonyAnt(ant, request) {
|
|
|
198
201
|
console.log(`[Decision] Consulting ant ${ant.name} for ${request.id}`);
|
|
199
202
|
return {
|
|
200
203
|
target: 'colony_ant',
|
|
201
|
-
agentId: ant.
|
|
204
|
+
agentId: ant.name,
|
|
202
205
|
response: `[Simulated] Ant ${ant.name} suggests: ${request.description.substring(0, 30)}...`,
|
|
203
206
|
confidence: 0.6,
|
|
204
207
|
};
|
package/dist/social/heartbeat.js
CHANGED
|
@@ -1,8 +1,25 @@
|
|
|
1
1
|
import * as fs from 'fs/promises';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import * as crypto from 'crypto';
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// 2026-06-11: 蚁群模块 (PheromoneEngine / AdaptiveHeartbeat) 已被用户删除, 仅保留最小 stub
|
|
5
|
+
// 让 heartbeat 内的 antColony 代码块继续类型合法, 但运行时所有方法都是 no-op
|
|
6
|
+
class PheromoneEngine {
|
|
7
|
+
async initialize() { }
|
|
8
|
+
shutdown() { }
|
|
9
|
+
getStats() { return { totalTrails: 0, avgStrength: 0, capabilityCount: 0, density: 0 }; }
|
|
10
|
+
async deposit(_type, _from, _to, _strength, _meta) { }
|
|
11
|
+
}
|
|
12
|
+
class AdaptiveHeartbeat {
|
|
13
|
+
constructor(_opts) { }
|
|
14
|
+
shutdown() { }
|
|
15
|
+
decide() { return { interval: 0, priorityLevel: 'normal' }; }
|
|
16
|
+
setPheromoneDensity(_d) { }
|
|
17
|
+
recordActivity(_t) { }
|
|
18
|
+
}
|
|
19
|
+
var PheromoneType;
|
|
20
|
+
(function (PheromoneType) {
|
|
21
|
+
PheromoneType["DISCOVERY"] = "discovery";
|
|
22
|
+
})(PheromoneType || (PheromoneType = {}));
|
|
6
23
|
import { ChannelManager } from './channels/ChannelManager.js';
|
|
7
24
|
import { ChannelType } from './channels/types.js';
|
|
8
25
|
import { DiapChannelBridge } from './channels/DiapChannelBridge.js';
|
package/dist/web/api-config.html
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
6
|
<title>API 配置 - Bolloon</title>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
<!-- 2026-06-11: 移除 Google Fonts 外部 CSS — 在大陆/跨公网时经常 30s+ timeout 阻塞首屏
|
|
8
|
+
style.css 里只用字体名字符串 (JetBrains Mono / monospace), 浏览器看字面量自动落回系统字体
|
|
9
|
+
2026-06-11 提速: 不再等 Google CDN 即可渲染 -->
|
|
10
10
|
<link rel="stylesheet" href="/style.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -92,7 +92,8 @@
|
|
|
92
92
|
|
|
93
93
|
<div class="form-group">
|
|
94
94
|
<label>模型</label>
|
|
95
|
-
<input type="text" id="modelInput" placeholder="如 gpt-4">
|
|
95
|
+
<input type="text" id="modelInput" placeholder="如 gpt-4" list="modelSuggestList">
|
|
96
|
+
<datalist id="modelSuggestList"></datalist>
|
|
96
97
|
<p class="form-hint" id="modelHint"></p>
|
|
97
98
|
</div>
|
|
98
99
|
|
|
@@ -358,6 +359,17 @@
|
|
|
358
359
|
document.getElementById('baseUrlInput').value = p.baseUrl || '';
|
|
359
360
|
document.getElementById('modelInput').value = p.model || '';
|
|
360
361
|
|
|
362
|
+
// 填充模型下拉建议(仅 LLM 类型有 providerInfo.models)
|
|
363
|
+
const datalist = document.getElementById('modelSuggestList');
|
|
364
|
+
datalist.innerHTML = '';
|
|
365
|
+
if (type === 'llm' && i.models && Array.isArray(i.models)) {
|
|
366
|
+
for (const m of i.models) {
|
|
367
|
+
const opt = document.createElement('option');
|
|
368
|
+
opt.value = m;
|
|
369
|
+
datalist.appendChild(opt);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
361
373
|
// 视频/音频/LLM 专用字段显隐
|
|
362
374
|
const isVideo = type === 'video';
|
|
363
375
|
const isAudio = type === 'audio';
|