@bolloon/bolloon-agent 0.3.4 → 0.3.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/cli/interface.js +10 -5
- package/dist/cli/loading-tui.js +0 -6
- package/dist/cli-entry.js +12 -2
- package/dist/index.js +23 -102
- package/dist/web/client.js +42 -8
- package/dist/web/server-v3-p2p.js +2 -0
- package/dist/web/server.js +4 -0
- package/dist/web/ui/message-renderer.js +20 -3
- package/package.json +1 -1
package/dist/cli/interface.js
CHANGED
|
@@ -8,11 +8,14 @@ export class CLIInterface {
|
|
|
8
8
|
agent = null;
|
|
9
9
|
localNode = null;
|
|
10
10
|
rl;
|
|
11
|
-
|
|
11
|
+
// 2026-07-20 Bug 3: quiet 模式压制 console.error
|
|
12
|
+
_quiet;
|
|
13
|
+
constructor(quiet = false) {
|
|
12
14
|
this.rl = readline.createInterface({
|
|
13
15
|
input: process.stdin,
|
|
14
16
|
output: process.stdout
|
|
15
17
|
});
|
|
18
|
+
this._quiet = quiet;
|
|
16
19
|
}
|
|
17
20
|
async start() {
|
|
18
21
|
console.log('\n🤖 AI文档智能体 P2P 网络');
|
|
@@ -105,7 +108,8 @@ export class CLIInterface {
|
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
catch (e) {
|
|
108
|
-
|
|
111
|
+
if (!this._quiet)
|
|
112
|
+
console.error('错误:', e);
|
|
109
113
|
}
|
|
110
114
|
}
|
|
111
115
|
}
|
|
@@ -190,12 +194,13 @@ export class CLIInterface {
|
|
|
190
194
|
stdio: 'inherit',
|
|
191
195
|
});
|
|
192
196
|
child.on('close', (code) => {
|
|
193
|
-
if (code !== 0)
|
|
194
|
-
console.
|
|
197
|
+
if (code !== 0 && !this._quiet)
|
|
198
|
+
console.warn(`chat 子进程退出码 ${code}`);
|
|
195
199
|
resolve();
|
|
196
200
|
});
|
|
197
201
|
child.on('error', (err) => {
|
|
198
|
-
|
|
202
|
+
if (!this._quiet)
|
|
203
|
+
console.warn('chat 子进程启动失败:', err.message);
|
|
199
204
|
resolve();
|
|
200
205
|
});
|
|
201
206
|
});
|
package/dist/cli/loading-tui.js
CHANGED
|
@@ -90,12 +90,6 @@ export class LoadingTUI {
|
|
|
90
90
|
this.write(` ${STEP_SYMBOL[step.status]} ${step.label}\n`);
|
|
91
91
|
}
|
|
92
92
|
}
|
|
93
|
-
if (this.ok) {
|
|
94
|
-
this.write(` ${GREEN}✓${RESET} ${CYAN}Bolloon${RESET} ${GRAY}ready${RESET}\n`);
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
this.write(` ${RED}✗${RESET} ${CYAN}Bolloon${RESET} ${GRAY}startup failed${RESET}\n`);
|
|
98
|
-
}
|
|
99
93
|
this.write(SHOW);
|
|
100
94
|
}
|
|
101
95
|
isFinished() {
|
package/dist/cli-entry.js
CHANGED
|
@@ -22,8 +22,18 @@ const CYAN = '\x1b[36m';
|
|
|
22
22
|
const YELLOW = '\x1b[33m';
|
|
23
23
|
const GREEN = '\x1b[32m';
|
|
24
24
|
const MAGENTA = '\x1b[35m';
|
|
25
|
-
// 版本信息 —
|
|
26
|
-
const VERSION =
|
|
25
|
+
// 版本信息 — 2026-07-20 Bug 3: 从 package.json 读取, 不再硬编码
|
|
26
|
+
const VERSION = (() => {
|
|
27
|
+
try {
|
|
28
|
+
const entryDir = path.dirname(fileURLToPath(import.meta.url));
|
|
29
|
+
const pkgPath = path.resolve(entryDir, '..', 'package.json');
|
|
30
|
+
const raw = fs.readFileSync(pkgPath, 'utf-8');
|
|
31
|
+
return JSON.parse(raw).version || '0.0.0';
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return '0.0.0';
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
27
37
|
function log(msg, color = RESET) {
|
|
28
38
|
console.log(`${color}${msg}${RESET}`);
|
|
29
39
|
}
|
package/dist/index.js
CHANGED
|
@@ -12,8 +12,17 @@ import { createSubAgentManager } from './agents/subagent-manager.js';
|
|
|
12
12
|
import { getGlobalSharedContext } from './social/global-shared-context.js';
|
|
13
13
|
import { createBollharnessIntegration } from './bollharness-integration/index.js';
|
|
14
14
|
import * as readline from 'readline';
|
|
15
|
-
import { LoadingTUI } from './cli/loading-tui.js';
|
|
16
15
|
// 启动时自动检查更新已禁用 (改用 --update-check / --update-now 显式触发)
|
|
16
|
+
import { createRequire } from 'module';
|
|
17
|
+
const _require = createRequire(import.meta.url);
|
|
18
|
+
const _BOLLOON_VERSION = (() => {
|
|
19
|
+
try {
|
|
20
|
+
return _require('../package.json').version || '0.0.0';
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return '0.0.0';
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
17
26
|
const RESET = '\x1b[0m';
|
|
18
27
|
const BOLD = '\x1b[1m';
|
|
19
28
|
const DIM = '\x1b[2m';
|
|
@@ -32,11 +41,16 @@ const CLEAR_LINE = '\x1b[2K';
|
|
|
32
41
|
const HIDE_CURSOR = '\x1b[?25l';
|
|
33
42
|
const SHOW_CURSOR = '\x1b[?25h';
|
|
34
43
|
const s = {
|
|
35
|
-
banner: () =>
|
|
44
|
+
banner: () => {
|
|
45
|
+
const verStr = `v${_BOLLOON_VERSION}`;
|
|
46
|
+
const pad = Math.max(0, 39 - 17 - verStr.length);
|
|
47
|
+
const spaces = ' '.repeat(pad);
|
|
48
|
+
console.log(`\n${CYAN}${BOLD}
|
|
36
49
|
╔═══════════════════════════════════════════╗
|
|
37
|
-
║ ${WHITE}🤖 Bolloon ${CYAN}
|
|
50
|
+
║ ${WHITE}🤖 Bolloon ${CYAN}${verStr}${spaces}║
|
|
38
51
|
║ ${WHITE}P2P AI Document Processor${CYAN} ║
|
|
39
|
-
╚═══════════════════════════════════════════╝${RESET}\n`)
|
|
52
|
+
╚═══════════════════════════════════════════╝${RESET}\n`);
|
|
53
|
+
},
|
|
40
54
|
step: (num, total, text, status) => {
|
|
41
55
|
const check = status === 'ok' ? `${GREEN}✓` :
|
|
42
56
|
status === 'loading' ? `${YELLOW}⟳` :
|
|
@@ -1795,7 +1809,6 @@ function printHelp() {
|
|
|
1795
1809
|
// Entry point
|
|
1796
1810
|
// ---------------------------------------------------------------------------
|
|
1797
1811
|
async function main() {
|
|
1798
|
-
let loading = null;
|
|
1799
1812
|
try {
|
|
1800
1813
|
const args = parseArgs();
|
|
1801
1814
|
if (args.help) {
|
|
@@ -1808,56 +1821,21 @@ async function main() {
|
|
|
1808
1821
|
// 想完全屏蔽 (CI / sandbox): BOLLOON_SKIP_UPDATE=true
|
|
1809
1822
|
const mode = args.web ? 'web' : 'cli';
|
|
1810
1823
|
const isNonInteractive = !!(args.tool || args.prompt);
|
|
1811
|
-
const isTuiMode = mode === 'cli' && !isNonInteractive && args.tui;
|
|
1812
1824
|
const originalLog = console.log;
|
|
1813
1825
|
const originalInfo = console.info;
|
|
1814
1826
|
const originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
1815
1827
|
const isSdkLog = (msg) => {
|
|
1816
1828
|
return /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(msg);
|
|
1817
1829
|
};
|
|
1818
|
-
// CLI interactive mode: suppress all startup output, show minimal spinner
|
|
1819
1830
|
const isCLIInteractive = mode === 'cli' && !isNonInteractive;
|
|
1820
|
-
|
|
1821
|
-
if (loading) {
|
|
1822
|
-
loading.setSteps([
|
|
1823
|
-
'LLM provider 检测',
|
|
1824
|
-
'DIAP 身份生成',
|
|
1825
|
-
'DID 发布到 IPFS',
|
|
1826
|
-
'P2P 网络启动',
|
|
1827
|
-
'iroh transport 启动',
|
|
1828
|
-
'Bolloon 上下文 bootstrap',
|
|
1829
|
-
'Web 服务启动',
|
|
1830
|
-
]);
|
|
1831
|
-
loading.start('启动中...');
|
|
1831
|
+
if (isCLIInteractive) {
|
|
1832
1832
|
console.log = () => { };
|
|
1833
1833
|
console.info = () => { };
|
|
1834
1834
|
process.stdout.write = () => true;
|
|
1835
1835
|
}
|
|
1836
|
-
else if (isTuiMode) {
|
|
1837
|
-
console.log = (...args) => {
|
|
1838
|
-
const msg = args.join(' ');
|
|
1839
|
-
if (isSdkLog(msg))
|
|
1840
|
-
return;
|
|
1841
|
-
originalLog.apply(console, args);
|
|
1842
|
-
};
|
|
1843
|
-
console.info = (...args) => {
|
|
1844
|
-
const msg = args.join(' ');
|
|
1845
|
-
if (isSdkLog(msg))
|
|
1846
|
-
return;
|
|
1847
|
-
originalInfo.apply(console, args);
|
|
1848
|
-
};
|
|
1849
|
-
process.stdout.write = (chunk, ...args) => {
|
|
1850
|
-
const msg = String(chunk);
|
|
1851
|
-
if (isSdkLog(msg))
|
|
1852
|
-
return true;
|
|
1853
|
-
return originalStdoutWrite(chunk, ...args);
|
|
1854
|
-
};
|
|
1855
|
-
}
|
|
1856
1836
|
if (isNonInteractive) {
|
|
1857
1837
|
console.error = () => { };
|
|
1858
1838
|
}
|
|
1859
|
-
s.banner();
|
|
1860
|
-
s.section('系统初始化');
|
|
1861
1839
|
const hasOpenAI = !!process.env.OPENAI_API_KEY;
|
|
1862
1840
|
// 2026-06-15: 修复 — 之前 anthropic 401 是因为 shell env 残留的旧 ANTHROPIC_API_KEY 抢了 provider 选择
|
|
1863
1841
|
// 用 BOLLOON_LLM_PROVIDER env 显式覆盖, 否则还是按 env hasXxx 顺序自动选
|
|
@@ -1881,32 +1859,21 @@ async function main() {
|
|
|
1881
1859
|
hasGlm ? 'GLM' :
|
|
1882
1860
|
hasQwen ? 'Qwen' : null;
|
|
1883
1861
|
if (llmProvider) {
|
|
1884
|
-
s.step(0, 4, `LLM: ${llmProvider}`, 'ok');
|
|
1885
|
-
loading?.completeStep(0, 'ok', `LLM: ${llmProvider}`);
|
|
1886
1862
|
initMinimax({ provider: llmProvider.toLowerCase() });
|
|
1887
1863
|
}
|
|
1888
1864
|
else {
|
|
1889
|
-
s.step(0, 4, 'LLM: 未配置', 'warn');
|
|
1890
|
-
loading?.completeStep(0, 'warn', 'LLM: 未配置');
|
|
1891
1865
|
if (isNonInteractive) {
|
|
1892
1866
|
s.warn('未设置任何 LLM API Key,功能受限');
|
|
1893
1867
|
}
|
|
1894
1868
|
}
|
|
1895
|
-
loading?.startStep(1, '生成 DIAP 身份...');
|
|
1896
1869
|
const { keypair, did, name } = await bootstrapIdentity();
|
|
1897
1870
|
agentIdentity = { did, name, publicKey: Buffer.from(keypair.publicKey).toString('hex') };
|
|
1898
|
-
loading?.completeStep(1, 'ok', `身份 ${name}`);
|
|
1899
|
-
loading?.startStep(2, '发布 DID 到 IPFS...');
|
|
1900
1871
|
publishDID(name, keypair).then(({ cid, ipnsName }) => {
|
|
1901
1872
|
if (cid)
|
|
1902
1873
|
agentIdentity.cid = cid;
|
|
1903
1874
|
if (ipnsName)
|
|
1904
1875
|
agentIdentity.ipnsName = ipnsName;
|
|
1905
|
-
|
|
1906
|
-
}).catch(() => {
|
|
1907
|
-
loading?.completeStep(2, 'warn', 'DID 本地模式');
|
|
1908
|
-
});
|
|
1909
|
-
loading?.startStep(3, '启动 P2P 网络...');
|
|
1876
|
+
}).catch(() => { });
|
|
1910
1877
|
const verifier = createVerificationManager();
|
|
1911
1878
|
let comm = null;
|
|
1912
1879
|
try {
|
|
@@ -1918,9 +1885,7 @@ async function main() {
|
|
|
1918
1885
|
agentIdentity.peerId = connections[0].publicKey;
|
|
1919
1886
|
agentIdentity.p2pChannel = 'bolloon-agent-harness';
|
|
1920
1887
|
}
|
|
1921
|
-
loading?.completeStep(3, 'ok', 'P2P 已连接');
|
|
1922
1888
|
}).catch(err => {
|
|
1923
|
-
loading?.completeStep(3, 'warn', 'P2P Web 模式启动失败');
|
|
1924
1889
|
s.warn(`P2P Web 模式启动失败: ${err.message}`);
|
|
1925
1890
|
});
|
|
1926
1891
|
}
|
|
@@ -1931,32 +1896,24 @@ async function main() {
|
|
|
1931
1896
|
agentIdentity.peerId = connections[0].publicKey;
|
|
1932
1897
|
agentIdentity.p2pChannel = 'bolloon-agent-harness';
|
|
1933
1898
|
}
|
|
1934
|
-
loading?.completeStep(3, 'ok', 'P2P 已连接');
|
|
1935
1899
|
}
|
|
1936
1900
|
}
|
|
1937
1901
|
catch (err) {
|
|
1938
1902
|
s.warn(`P2P 初始化失败: ${err.message}`);
|
|
1939
1903
|
s.warn('将使用无 P2P 模式运行');
|
|
1940
|
-
loading?.completeStep(3, 'error', 'P2P 初始化失败');
|
|
1941
1904
|
}
|
|
1942
|
-
loading?.startStep(4, '启动 iroh transport...');
|
|
1943
1905
|
await bootstrapIroh(keypair, name);
|
|
1944
|
-
loading?.completeStep(4, 'ok', 'iroh 已就绪');
|
|
1945
1906
|
// Bolloon Bootstrap: 启动扫描 + Context 收集 + 挂定时任务
|
|
1946
1907
|
// 失败静默 (主流程不被阻塞)
|
|
1947
|
-
loading?.startStep(5, '正在 bootstrap bolloon 上下文...');
|
|
1948
1908
|
try {
|
|
1949
1909
|
const { bootstrapBolloon } = await import('./pi-ecosystem-judgment/human-value-pipeline.js');
|
|
1950
1910
|
s.info('正在 bootstrap bolloon 上下文...');
|
|
1951
1911
|
const bs = await bootstrapBolloon({ cwd: process.cwd() });
|
|
1952
1912
|
s.info(`Bootstrap 完成 (${bs.durationMs}ms, ${bs.errors.length} 个非致命错误)`);
|
|
1953
|
-
loading?.completeStep(5, 'ok', `Bootstrap 完成 (${bs.durationMs}ms)`);
|
|
1954
1913
|
}
|
|
1955
1914
|
catch (err) {
|
|
1956
1915
|
s.warn(`Bootstrap 失败 (非致命, 主流程继续): ${err.message}`);
|
|
1957
|
-
loading?.completeStep(5, 'warn', 'Bootstrap 失败 (已跳过)');
|
|
1958
1916
|
}
|
|
1959
|
-
s.divider();
|
|
1960
1917
|
if (mode === 'web') {
|
|
1961
1918
|
const port = parseInt(process.env.PORT || '54188');
|
|
1962
1919
|
// 2026-06-16: BOLLOON_DEV_MODE=1 或 selfImprove=true 启动项 → 开发者模式, 启用自迭代 (健康监控+自改总线)
|
|
@@ -1966,12 +1923,9 @@ async function main() {
|
|
|
1966
1923
|
console.log('[startup] BOLLOON_DEV_MODE=1, 开发者模式: 自迭代已启用');
|
|
1967
1924
|
}
|
|
1968
1925
|
const { createWebServer, openBrowser } = await import('./web/server.js');
|
|
1969
|
-
loading?.startStep(6, `启动 Web 服务端口 ${port}...`);
|
|
1970
|
-
s.info(`启动 Web 服务端口 ${port}...`);
|
|
1971
1926
|
// 2026-06-24: CLI 默认 loopback bind (安全), LAN 访问需 BOLLOON_HOST=0.0.0.0
|
|
1972
1927
|
const bindHost = process.env.BOLLOON_HOST;
|
|
1973
1928
|
const { port: actualPort } = await createWebServer(port, { selfImprove, ...(bindHost ? { host: bindHost } : {}) });
|
|
1974
|
-
loading?.completeStep(6, 'ok', `Web 服务 :${actualPort}`);
|
|
1975
1929
|
const displayHost = bindHost ?? '127.0.0.1';
|
|
1976
1930
|
s.success(`浏览器已打开 → http://${displayHost}:${actualPort}`);
|
|
1977
1931
|
openBrowser(`http://${displayHost}:${actualPort}`);
|
|
@@ -1990,46 +1944,13 @@ async function main() {
|
|
|
1990
1944
|
}
|
|
1991
1945
|
}
|
|
1992
1946
|
else {
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
console.info = originalInfo;
|
|
1997
|
-
process.stdout.write = originalStdoutWrite;
|
|
1998
|
-
loading.stop(true);
|
|
1999
|
-
}
|
|
2000
|
-
else {
|
|
2001
|
-
// For non-loading TUI CLI mode: apply SDK filtering if needed
|
|
2002
|
-
if (isTuiMode) {
|
|
2003
|
-
console.log = (...args) => {
|
|
2004
|
-
const msg = args.join(' ');
|
|
2005
|
-
if (isSdkLog(msg))
|
|
2006
|
-
return;
|
|
2007
|
-
originalLog.apply(console, args);
|
|
2008
|
-
};
|
|
2009
|
-
console.info = (...args) => {
|
|
2010
|
-
const msg = args.join(' ');
|
|
2011
|
-
if (isSdkLog(msg))
|
|
2012
|
-
return;
|
|
2013
|
-
originalInfo.apply(console, args);
|
|
2014
|
-
};
|
|
2015
|
-
process.stdout.write = (chunk, ...args) => {
|
|
2016
|
-
const msg = String(chunk);
|
|
2017
|
-
if (isSdkLog(msg))
|
|
2018
|
-
return true;
|
|
2019
|
-
return originalStdoutWrite(chunk, ...args);
|
|
2020
|
-
};
|
|
2021
|
-
}
|
|
2022
|
-
else {
|
|
2023
|
-
console.log = originalLog;
|
|
2024
|
-
console.info = originalInfo;
|
|
2025
|
-
process.stdout.write = originalStdoutWrite;
|
|
2026
|
-
}
|
|
2027
|
-
}
|
|
1947
|
+
console.log = originalLog;
|
|
1948
|
+
console.info = originalInfo;
|
|
1949
|
+
process.stdout.write = originalStdoutWrite;
|
|
2028
1950
|
startCLI(comm);
|
|
2029
1951
|
}
|
|
2030
1952
|
}
|
|
2031
1953
|
catch (e) {
|
|
2032
|
-
loading?.stop(false);
|
|
2033
1954
|
throw e;
|
|
2034
1955
|
}
|
|
2035
1956
|
}
|
package/dist/web/client.js
CHANGED
|
@@ -782,6 +782,7 @@
|
|
|
782
782
|
addMessage: () => addMessage,
|
|
783
783
|
escapeHtml: () => escapeHtml,
|
|
784
784
|
finalizeTimelineAsMessage: () => finalizeTimelineAsMessage,
|
|
785
|
+
flushStepEventBuffer: () => flushStepEventBuffer,
|
|
785
786
|
getMessagesContainerForCurrent: () => getMessagesContainerForCurrent,
|
|
786
787
|
handleStepEvent: () => handleStepEvent,
|
|
787
788
|
handleStreamTokenEvent: () => handleStreamTokenEvent,
|
|
@@ -870,8 +871,8 @@
|
|
|
870
871
|
cleanContent = cleanContent.substring(0, finalGenIdx).trim();
|
|
871
872
|
}
|
|
872
873
|
}
|
|
873
|
-
const
|
|
874
|
-
const segments = segmentChatReply(cleanContent, { knownToolNames });
|
|
874
|
+
const knownToolNames2 = ctx && ctx.knownToolNames || /* @__PURE__ */ new Set();
|
|
875
|
+
const segments = segmentChatReply(cleanContent, { knownToolNames: knownToolNames2 });
|
|
875
876
|
if (segments.length === 0) {
|
|
876
877
|
return;
|
|
877
878
|
}
|
|
@@ -939,6 +940,7 @@
|
|
|
939
940
|
}
|
|
940
941
|
if (type === "ai" && msgContainer) {
|
|
941
942
|
mountStepTimeline(div, currentChannelId2);
|
|
943
|
+
flushStepEventBuffer(currentChannelId2, ctx);
|
|
942
944
|
}
|
|
943
945
|
div.appendChild(time);
|
|
944
946
|
if (msgContainer) {
|
|
@@ -1094,6 +1096,7 @@
|
|
|
1094
1096
|
streamingText = "";
|
|
1095
1097
|
mountStepTimeline(streamingMessageEl, currentChannelId2);
|
|
1096
1098
|
container.appendChild(streamingMessageEl);
|
|
1099
|
+
flushStepEventBuffer(currentChannelId2, ctx);
|
|
1097
1100
|
if (typeof ctx.setTimelineState === "function") {
|
|
1098
1101
|
ctx.setTimelineState("streaming");
|
|
1099
1102
|
}
|
|
@@ -1142,9 +1145,12 @@
|
|
|
1142
1145
|
if (!data || !data.type) return;
|
|
1143
1146
|
let target = streamingMessageEl && streamingMessageEl.isConnected ? streamingMessageEl : null;
|
|
1144
1147
|
if (!target) {
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
+
if (currentChannelId2) {
|
|
1149
|
+
const buf = stepEventBuffer.get(currentChannelId2) || [];
|
|
1150
|
+
buf.push(data);
|
|
1151
|
+
stepEventBuffer.set(currentChannelId2, buf);
|
|
1152
|
+
}
|
|
1153
|
+
return;
|
|
1148
1154
|
}
|
|
1149
1155
|
if (!target) return;
|
|
1150
1156
|
const timeline = getStepTimeline(target);
|
|
@@ -1157,6 +1163,15 @@
|
|
|
1157
1163
|
error: data.error
|
|
1158
1164
|
});
|
|
1159
1165
|
}
|
|
1166
|
+
function flushStepEventBuffer(channelId, ctx) {
|
|
1167
|
+
if (!channelId) return;
|
|
1168
|
+
const buf = stepEventBuffer.get(channelId);
|
|
1169
|
+
if (!buf || buf.length === 0) return;
|
|
1170
|
+
stepEventBuffer.delete(channelId);
|
|
1171
|
+
for (const evt of buf) {
|
|
1172
|
+
handleStepEvent(evt, ctx);
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1160
1175
|
function resetRendererState() {
|
|
1161
1176
|
streamingMessageEl = null;
|
|
1162
1177
|
streamingTextNode = null;
|
|
@@ -1168,7 +1183,7 @@
|
|
|
1168
1183
|
scrollToBottomTimer = null;
|
|
1169
1184
|
}
|
|
1170
1185
|
}
|
|
1171
|
-
var streamingMessageEl, streamingTextNode, streamingText, lastUserCommand, lastAiContent, scrollToBottomTimer, MessageRenderer;
|
|
1186
|
+
var streamingMessageEl, streamingTextNode, streamingText, lastUserCommand, lastAiContent, stepEventBuffer, scrollToBottomTimer, MessageRenderer;
|
|
1172
1187
|
var init_message_renderer = __esm({
|
|
1173
1188
|
"src/web/ui/message-renderer.ts"() {
|
|
1174
1189
|
"use strict";
|
|
@@ -1179,12 +1194,14 @@
|
|
|
1179
1194
|
streamingText = "";
|
|
1180
1195
|
lastUserCommand = "";
|
|
1181
1196
|
lastAiContent = "";
|
|
1197
|
+
stepEventBuffer = /* @__PURE__ */ new Map();
|
|
1182
1198
|
scrollToBottomTimer = null;
|
|
1183
1199
|
MessageRenderer = {
|
|
1184
1200
|
addMessage,
|
|
1185
1201
|
handleStreamTokenEvent,
|
|
1186
1202
|
finalizeTimelineAsMessage,
|
|
1187
1203
|
handleStepEvent,
|
|
1204
|
+
flushStepEventBuffer,
|
|
1188
1205
|
escapeHtml,
|
|
1189
1206
|
getMessagesContainerForCurrent,
|
|
1190
1207
|
resetRendererState
|
|
@@ -1439,20 +1456,25 @@
|
|
|
1439
1456
|
return {};
|
|
1440
1457
|
}
|
|
1441
1458
|
var MR_addMessage = (...args) => _getMR().addMessage?.(...args);
|
|
1459
|
+
var MR_handleStreamTokenEvent = (...args) => _getMR().handleStreamTokenEvent?.(...args);
|
|
1442
1460
|
var MR_finalizeTimelineAsMessage = (...args) => _getMR().finalizeTimelineAsMessage?.(...args);
|
|
1443
1461
|
var MR_handleStepEvent = (...args) => _getMR().handleStepEvent?.(...args);
|
|
1444
1462
|
var MR_escapeHtml = (s) => _getMR().escapeHtml?.(s);
|
|
1445
1463
|
var MR_hasStreamingText = () => _getMR().hasStreamingText?.() ?? false;
|
|
1446
1464
|
var MR_replaceStreamingText = (text) => _getMR().replaceStreamingText?.(text);
|
|
1447
1465
|
var MR_injectRecoveredText = (text, ctx) => _getMR().injectRecoveredText?.(text, ctx ?? getRendererCtx());
|
|
1466
|
+
var knownToolNames = /* @__PURE__ */ new Set();
|
|
1448
1467
|
function getRendererCtx() {
|
|
1449
1468
|
return {
|
|
1450
1469
|
messagesEl,
|
|
1451
1470
|
messagesContainers,
|
|
1452
1471
|
currentChannelId,
|
|
1453
1472
|
lastUsedJudgmentIds,
|
|
1473
|
+
knownToolNames,
|
|
1474
|
+
toolCallCallback: (tool, _hostEl) => {
|
|
1475
|
+
console.log("[toolCall]", tool.name, tool.args);
|
|
1476
|
+
},
|
|
1454
1477
|
openJudgmentsModalWithFilter
|
|
1455
|
-
// 引用 client.js 函数, 通过参数注入避免循环 import
|
|
1456
1478
|
};
|
|
1457
1479
|
}
|
|
1458
1480
|
var messagesEl = document.getElementById("messages");
|
|
@@ -2293,6 +2315,9 @@ ${data.error || "channel not found"}`, "error");
|
|
|
2293
2315
|
function addMessage2(content, type, save = true, container, usedJudgmentIds = [], timestamp = void 0) {
|
|
2294
2316
|
return MR_addMessage(content, type, save, container, usedJudgmentIds, getRendererCtx(), timestamp);
|
|
2295
2317
|
}
|
|
2318
|
+
function handleStreamTokenEvent2(data) {
|
|
2319
|
+
return MR_handleStreamTokenEvent(data, getRendererCtx());
|
|
2320
|
+
}
|
|
2296
2321
|
function finalizeTimelineAsMessage2() {
|
|
2297
2322
|
return MR_finalizeTimelineAsMessage(getRendererCtx());
|
|
2298
2323
|
}
|
|
@@ -2575,7 +2600,7 @@ ${data.error || "channel not found"}`, "error");
|
|
|
2575
2600
|
currentPreviewBubble = newPreview;
|
|
2576
2601
|
}
|
|
2577
2602
|
} else if (data.type === "stream") {
|
|
2578
|
-
|
|
2603
|
+
handleStreamTokenEvent2(data);
|
|
2579
2604
|
} else if (data.type === "regenerating") {
|
|
2580
2605
|
const messages = container.querySelectorAll(".message-ai");
|
|
2581
2606
|
if (messages.length > 0) {
|
|
@@ -3359,6 +3384,15 @@ ${data.error || "channel not found"}`, "error");
|
|
|
3359
3384
|
if (!themeData.agentId) {
|
|
3360
3385
|
await saveTheme(themeData.theme, currentAgentId);
|
|
3361
3386
|
}
|
|
3387
|
+
try {
|
|
3388
|
+
const res = await fetch("/api/tools");
|
|
3389
|
+
if (res.ok) {
|
|
3390
|
+
const toolIds = await res.json();
|
|
3391
|
+
knownToolNames = new Set(toolIds);
|
|
3392
|
+
}
|
|
3393
|
+
} catch (e) {
|
|
3394
|
+
console.warn("[init] \u83B7\u53D6\u5DE5\u5177\u5217\u8868\u5931\u8D25:", e);
|
|
3395
|
+
}
|
|
3362
3396
|
await loadChannels();
|
|
3363
3397
|
await checkApiConfig();
|
|
3364
3398
|
if (channels.length > 0) {
|
|
@@ -57,6 +57,8 @@ export function sanitizeChannelForPeer(ch, peerPublicKey) {
|
|
|
57
57
|
updatedAt: ch.updatedAt,
|
|
58
58
|
hasWallet: !!ch.walletAddress,
|
|
59
59
|
share_id: ch.share_id,
|
|
60
|
+
// 2026-07-20 Bug 2: 保留 ownerPublicKey, 前端用 peerId 区分多节点
|
|
61
|
+
_ownerPublicKey: ch.publicKey,
|
|
60
62
|
};
|
|
61
63
|
}
|
|
62
64
|
/** v3 新增: 判断 channel 是否分享给 peerPublicKey */
|
package/dist/web/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import * as os from 'os';
|
|
|
9
9
|
import * as crypto from 'crypto';
|
|
10
10
|
import { validateMessageInput, validateChannelInput, healthCheck, } from './input-validator.js';
|
|
11
11
|
import { segmentChatReply } from '../agents/chat-segmenter.js';
|
|
12
|
+
import { listTools } from '../llm/tool-manifest/index.js';
|
|
12
13
|
import { registerJudgmentsRoutes } from './routes-judgments.js';
|
|
13
14
|
import { registerLlmConfigRoutes } from './routes-llm-config.js';
|
|
14
15
|
import { registerTaskRoutes } from './routes-tasks.js';
|
|
@@ -1964,6 +1965,9 @@ export async function createWebServer(port = 3000, options = {}) {
|
|
|
1964
1965
|
app.get('/api/health', (_req, res) => {
|
|
1965
1966
|
res.json(healthCheck(getPackageVersion()));
|
|
1966
1967
|
});
|
|
1968
|
+
app.get('/api/tools', (_req, res) => {
|
|
1969
|
+
res.json(listTools().map(t => t.id));
|
|
1970
|
+
});
|
|
1967
1971
|
// 2026-07-01 (v0.2.6): 前后端分离核心 — 后端切 LLM 输出为结构化 segments
|
|
1968
1972
|
// - POST /api/segment-reply { reply, knownTools }
|
|
1969
1973
|
// - 返回 ChatSegment[] (think / text / env_details / tool_call / final)
|
|
@@ -10,6 +10,7 @@ let streamingTextNode = null;
|
|
|
10
10
|
let streamingText = "";
|
|
11
11
|
let lastUserCommand = "";
|
|
12
12
|
let lastAiContent = "";
|
|
13
|
+
const stepEventBuffer = /* @__PURE__ */ new Map();
|
|
13
14
|
function hasStreamingText() {
|
|
14
15
|
return streamingText.length > 0;
|
|
15
16
|
}
|
|
@@ -160,6 +161,7 @@ function addMessage(content, type, save = true, container, usedJudgmentIds = [],
|
|
|
160
161
|
}
|
|
161
162
|
if (type === "ai" && msgContainer) {
|
|
162
163
|
mountStepTimeline(div, currentChannelId);
|
|
164
|
+
flushStepEventBuffer(currentChannelId, ctx);
|
|
163
165
|
}
|
|
164
166
|
div.appendChild(time);
|
|
165
167
|
if (msgContainer) {
|
|
@@ -315,6 +317,7 @@ function handleStreamTokenEvent(data, ctx = { messagesEl: null, messagesContaine
|
|
|
315
317
|
streamingText = "";
|
|
316
318
|
mountStepTimeline(streamingMessageEl, currentChannelId);
|
|
317
319
|
container.appendChild(streamingMessageEl);
|
|
320
|
+
flushStepEventBuffer(currentChannelId, ctx);
|
|
318
321
|
if (typeof ctx.setTimelineState === "function") {
|
|
319
322
|
ctx.setTimelineState("streaming");
|
|
320
323
|
}
|
|
@@ -363,9 +366,12 @@ function handleStepEvent(data, ctx = { messagesEl: null, messagesContainers: /*
|
|
|
363
366
|
if (!data || !data.type) return;
|
|
364
367
|
let target = streamingMessageEl && streamingMessageEl.isConnected ? streamingMessageEl : null;
|
|
365
368
|
if (!target) {
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
+
if (currentChannelId) {
|
|
370
|
+
const buf = stepEventBuffer.get(currentChannelId) || [];
|
|
371
|
+
buf.push(data);
|
|
372
|
+
stepEventBuffer.set(currentChannelId, buf);
|
|
373
|
+
}
|
|
374
|
+
return;
|
|
369
375
|
}
|
|
370
376
|
if (!target) return;
|
|
371
377
|
const timeline = getStepTimeline(target);
|
|
@@ -378,6 +384,15 @@ function handleStepEvent(data, ctx = { messagesEl: null, messagesContainers: /*
|
|
|
378
384
|
error: data.error
|
|
379
385
|
});
|
|
380
386
|
}
|
|
387
|
+
function flushStepEventBuffer(channelId, ctx) {
|
|
388
|
+
if (!channelId) return;
|
|
389
|
+
const buf = stepEventBuffer.get(channelId);
|
|
390
|
+
if (!buf || buf.length === 0) return;
|
|
391
|
+
stepEventBuffer.delete(channelId);
|
|
392
|
+
for (const evt of buf) {
|
|
393
|
+
handleStepEvent(evt, ctx);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
381
396
|
function resetRendererState() {
|
|
382
397
|
streamingMessageEl = null;
|
|
383
398
|
streamingTextNode = null;
|
|
@@ -394,6 +409,7 @@ const MessageRenderer = {
|
|
|
394
409
|
handleStreamTokenEvent,
|
|
395
410
|
finalizeTimelineAsMessage,
|
|
396
411
|
handleStepEvent,
|
|
412
|
+
flushStepEventBuffer,
|
|
397
413
|
escapeHtml,
|
|
398
414
|
getMessagesContainerForCurrent,
|
|
399
415
|
resetRendererState
|
|
@@ -406,6 +422,7 @@ export {
|
|
|
406
422
|
addMessage,
|
|
407
423
|
escapeHtml,
|
|
408
424
|
finalizeTimelineAsMessage,
|
|
425
|
+
flushStepEventBuffer,
|
|
409
426
|
getMessagesContainerForCurrent,
|
|
410
427
|
handleStepEvent,
|
|
411
428
|
handleStreamTokenEvent,
|