修复完整会话历史与长气泡交互并更新 CentOS 7 发布包
提交当前工作区全部源码、回归脚本、任务记录及重新构建的 Bun baseline 发布包。发布单文件 MCP 初始化及前端资源一致性验证通过。
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user