修复完整会话历史与长气泡交互并更新 CentOS 7 发布包

提交当前工作区全部源码、回归脚本、任务记录及重新构建的 Bun baseline 发布包。发布单文件 MCP 初始化及前端资源一致性验证通过。
This commit is contained in:
shiyue
2026-10-10 23:40:16 +08:00
parent 747771a048
commit 69b6db0f5b
71 changed files with 9536 additions and 503 deletions

View File

@@ -15,7 +15,6 @@ function createAgentRuntime(deps) {
internalMcpToken,
nodePath,
wsSend,
truncateObj,
sanitizeToolInput,
loadSession,
saveSession,
@@ -32,11 +31,6 @@ function createAgentRuntime(deps) {
return Math.max(min, Math.min(max, raw));
}
const RUNTIME_FULL_TEXT_MAX_CHARS = readRuntimePositiveIntEnv(
'CC_WEB_RUNTIME_FULL_TEXT_MAX_CHARS',
256 * 1024,
{ min: 4096 },
);
const CCWEB_MCP_STARTUP_TIMEOUT_SEC = readRuntimePositiveIntEnv(
'CC_WEB_CODEX_APP_MCP_STARTUP_TIMEOUT_SEC',
30,
@@ -47,17 +41,8 @@ function createAgentRuntime(deps) {
60,
{ min: 10, max: 600 },
);
const RUNTIME_TRUNCATED_HEAD = '[cc-web: 前文过长,已保留尾部以保护服务稳定性]\n';
function keepTail(value, maxLen) {
const text = String(value || '');
if (!Number.isFinite(maxLen) || maxLen <= 0 || text.length <= maxLen) return text;
const keep = Math.max(0, maxLen - RUNTIME_TRUNCATED_HEAD.length);
return `${RUNTIME_TRUNCATED_HEAD}${text.slice(-keep)}`;
}
function appendCappedText(current, addition, maxLen = RUNTIME_FULL_TEXT_MAX_CHARS) {
return keepTail(`${String(current || '')}${String(addition || '')}`, maxLen);
function appendFullText(current, addition) {
return `${String(current || '')}${String(addition || '')}`;
}
function tomlString(value) {
@@ -282,7 +267,7 @@ function createAgentRuntime(deps) {
function codexToolInput(item) {
if (!item) return null;
if (item.type === 'command_execution') return { command: item.command || '' };
return truncateObj(item, 500);
return item;
}
function codexToolMeta(item) {
@@ -331,7 +316,7 @@ function createAgentRuntime(deps) {
if (!item) return '';
if (typeof item.aggregated_output === 'string' && item.aggregated_output) return item.aggregated_output;
if (typeof item.text === 'string' && item.text) return item.text;
return JSON.stringify(truncateObj(item, 1200));
return JSON.stringify(item);
}
function formatAgentMessageDividerTime(date = new Date()) {
@@ -369,7 +354,7 @@ function createAgentRuntime(deps) {
? (/\n\s*$/.test(currentText) ? `\n${createAgentMessageDivider()}\n\n` : `\n\n${createAgentMessageDivider()}\n\n`)
: '';
const chunk = separator + nextText;
entry.fullText = appendCappedText(entry.fullText || '', chunk);
entry.fullText = appendFullText(entry.fullText || '', chunk);
return chunk;
}
@@ -405,6 +390,38 @@ function createAgentRuntime(deps) {
function processClaudeEvent(entry, event, sessionId) {
if (!event || !event.type) return;
function toolResultText(block) {
if (typeof block?.content === 'string') return block.content;
if (Array.isArray(block?.content)) {
return block.content.map((part) => {
if (typeof part === 'string') return part;
if (typeof part?.text === 'string') return part.text;
return JSON.stringify(part);
}).filter(Boolean).join('\n');
}
if (block?.content === undefined) return '';
return JSON.stringify(block.content);
}
function processToolResult(block) {
const toolId = String(block?.tool_use_id || block?.toolUseId || '').trim();
if (!toolId) return;
const resultText = toolResultText(block);
let tc = entry.toolCalls.find((tool) => tool.id === toolId);
if (!tc) {
tc = { name: block.name || 'ClaudeTool', id: toolId, input: null, done: false };
entry.toolCalls.push(tc);
}
tc.done = true;
tc.result = resultText;
sendRuntime(entry, sessionId, {
type: 'tool_end',
toolUseId: toolId,
input: tc.input,
result: resultText,
});
}
switch (event.type) {
case 'system':
if (event.session_id) {
@@ -422,26 +439,14 @@ function createAgentRuntime(deps) {
for (const block of content) {
if (block.type === 'text' && block.text) {
entry.fullText = appendCappedText(entry.fullText || '', block.text);
entry.fullText = appendFullText(entry.fullText || '', block.text);
sendRuntime(entry, sessionId, { type: 'text_delta', text: block.text });
} else if (block.type === 'tool_use') {
const toolInput = sanitizeToolInput(block.name, block.input);
const tc = { name: block.name, id: block.id, input: toolInput, done: false };
entry.toolCalls.push(tc);
sendRuntime(entry, sessionId, { type: 'tool_start', name: block.name, toolUseId: block.id, input: tc.input });
} else if (block.type === 'tool_result') {
const resultText = typeof block.content === 'string'
? block.content
: Array.isArray(block.content)
? block.content.map((c) => c.text || '').join('\n')
: JSON.stringify(block.content);
const tc = entry.toolCalls.find((t) => t.id === block.tool_use_id);
if (tc) {
tc.done = true;
tc.result = resultText.slice(0, 2000);
}
sendRuntime(entry, sessionId, { type: 'tool_end', toolUseId: block.tool_use_id, result: resultText.slice(0, 2000) });
}
} else if (block.type === 'tool_result') processToolResult(block);
}
if (event.session_id) {
@@ -454,6 +459,15 @@ function createAgentRuntime(deps) {
break;
}
case 'user': {
const content = event.message?.content;
if (!Array.isArray(content)) break;
for (const block of content) {
if (block?.type === 'tool_result') processToolResult(block);
}
break;
}
case 'result': {
const session = loadSession(sessionId);
if (session) {
@@ -495,7 +509,7 @@ function createAgentRuntime(deps) {
const item = event.item;
if (!item || !item.id || item.type === 'agent_message') break;
const tc = ensureCodexToolCall(entry, item, sessionId);
const resultText = codexToolResult(item).slice(0, 2000);
const resultText = codexToolResult(item);
tc.done = false;
tc.result = resultText;
@@ -522,12 +536,20 @@ function createAgentRuntime(deps) {
} catch {}
if (parsedContent && Array.isArray(parsedContent)) {
if (!entry.contentBlocks) entry.contentBlocks = [];
// 只有出现结构化内容时才切换到内容块形状。纯文本回合继续使用
// 旧的字符串存储格式,避免下游把数组误转成 `[object Object]`。
// 如果前面已经收到普通文本,需要先把它作为首个文本块补回,
// 这样混合回合仍能保留完整的顺序和正文。
if (!entry.contentBlocks) {
entry.contentBlocks = [];
if (entry.fullText) entry.contentBlocks.push({ type: 'text', text: entry.fullText });
}
entry.contentBlocks.push(...parsedContent);
const textOnly = parsedContent.filter(b => b.type === 'text').map(b => b.text || '').join('');
appendCodexAgentText(entry, textOnly);
sendRuntime(entry, sessionId, { type: 'content_blocks', blocks: parsedContent });
} else {
if (entry.contentBlocks) entry.contentBlocks.push({ type: 'text', text: item.text });
const textChunk = appendCodexAgentText(entry, item.text);
sendRuntime(entry, sessionId, { type: 'text_delta', text: textChunk });
}
@@ -535,13 +557,14 @@ function createAgentRuntime(deps) {
break;
}
const tc = ensureCodexToolCall(entry, item, sessionId);
const resultText = codexToolResult(item).slice(0, 2000);
const resultText = codexToolResult(item);
tc.done = true;
tc.result = resultText;
sendRuntime(entry, sessionId, {
type: 'tool_end',
toolUseId: item.id,
input: tc.input,
result: resultText,
kind: tc.kind,
meta: tc.meta,