chore: rebuild release package

This commit is contained in:
shiyue
2026-07-24 08:00:31 +08:00
parent e5059e97c4
commit 4ab02ae33b
15 changed files with 778 additions and 73 deletions

147
server.js
View File

@@ -725,20 +725,16 @@ const MCP_PROMPT_RESPONSE_MAX_CHARS = 20000;
// Codex 默认模型优先读取 ~/.codex/config.toml缺失时再回退到旧默认值。
const FALLBACK_CODEX_MODEL = 'gpt-5.4';
const CODEX_REASONING_LEVELS = new Set(['low', 'medium', 'high', 'xhigh']);
const CODEX_REASONING_LEVELS = new Set(['low', 'medium', 'high', 'xhigh', 'ultra']);
const CCWEB_TITLE_TOOL_INSTRUCTIONS = [
'Use the ccweb title tool sparingly. For a new chat, call "mcp__ccweb__ccweb_set_title" or "ccweb_set_title" once after the user\'s initial request is clear, and set a concise task title.',
'Do not rename the chat for routine progress, substeps, implementation details, or slightly better wording. Rename only when the user\'s primary objective changes substantially and the existing title would be misleading.',
].join('\n');
const CODEX_APP_COLLABORATION_INSTRUCTIONS = [
CCWEB_TITLE_TOOL_INSTRUCTIONS,
'Codex sub-agent spawning rules:',
'- Treat omitted fork_context the same as fork_context: true: a full-history fork inherits the parent agent type, model, and reasoning effort.',
'- If you call spawn_agent with fork_context omitted or true, do not set agent_type, model, or reasoning_effort.',
'- If you need a specific agent_type, model, or reasoning_effort, set fork_context: false and include only the necessary context in the message.',
'- Do not rely on parent turn reasoning settings for spawned agents; only set reasoning_effort on spawn_agent when the chosen child model supports it.',
'- When calling wait_agent, always pass timeout_ms explicitly. Use 300000ms for normal waits, and use a longer value when the child task is expected to run longer.',
'- If wait_agent returns without a final child-agent status, keep waiting on the same target agents in additional wait_agent rounds until they return, the user interrupts, or the result is no longer needed.',
'Codex sub-agent runtime rules:',
'- Follow the current runtime tool schema and tool descriptions; do not assume optional fields exist.',
'- Do not hard-code version-specific spawn, wait, or completion behavior in prompts.',
].join('\n');
function getLocalCodexConfigTomlPath() {
@@ -7963,14 +7959,14 @@ function closeCcwebMcpChildAgent(sessionId, childThreadId, options = {}) {
}
const child = ccwebMcpChildThreads.get(normalizedThreadId);
if (!child || child.parentSessionId !== normalizedSessionId) {
return { ok: false, code: 'child_agent_not_found', message: '未找到可关闭的 ccweb MCP 子代理。' };
return { ok: false, code: 'child_agent_not_found', message: '未找到可中断的 ccweb MCP 子代理。' };
}
const now = new Date().toISOString();
child.status = 'closed';
child.closedAt = now;
child.status = 'interrupted';
child.interruptedAt = now;
child.updatedAt = now;
child.closeReason = options.reason || 'manual';
child.interruptReason = options.reason || 'manual';
ccwebMcpChildThreads.set(normalizedThreadId, child);
if (child.turnId && codexAppClient?.isRunning()) {
@@ -8010,7 +8006,7 @@ function handleCcwebMcpChildAgentClose(ws, msg = {}) {
tone: 'info',
transient: true,
autoDismissMs: 4000,
message: `关闭子代理 ${result.child.label || result.child.threadId}`,
message: `中断子代理 ${result.child.label || result.child.threadId}`,
});
}
@@ -8562,10 +8558,11 @@ function codexAppCollabToolName(value) {
function ccwebMcpChildStatus(value, fallback = 'running') {
const normalized = String(value || '').trim().toLowerCase().replace(/[\s_-]/g, '');
if (!normalized) return fallback;
if (normalized === 'interrupted') return 'interrupted';
if (/^(closed|close|cleanup|cleaned)$/.test(normalized)) return 'closed';
if (/^(returned|completed|complete|done|success|succeeded|finished)$/.test(normalized)) return 'returned';
if (/^(failed|failure|error|errored)$/.test(normalized)) return 'failed';
if (/^(cancelled|canceled|aborted|interrupted)$/.test(normalized)) return 'closed';
if (/^(cancelled|canceled|aborted)$/.test(normalized)) return 'closed';
if (/^(pending|pendinginit|queued|waiting|running|working|active|inprogress|started)$/.test(normalized)) return 'running';
return fallback;
}
@@ -8633,6 +8630,17 @@ function ccwebMcpRecoveredChildStatus(input = {}, tool = {}) {
return 'running';
}
function normalizeCcwebMcpChildPlanProgress(value) {
if (!value || typeof value !== 'object') return null;
const total = Number.parseInt(value.total, 10);
const completed = Number.parseInt(value.completed, 10);
if (!Number.isFinite(total) || total <= 0 || !Number.isFinite(completed)) return null;
return {
completed: Math.max(0, Math.min(completed, total)),
total,
};
}
function isSubAgentActivityTool(tool = {}, input = {}) {
return tool.name === 'subAgentActivity'
|| tool.kind === 'subAgentActivity'
@@ -8650,10 +8658,16 @@ function recoverCcwebMcpChildThreadsFromPersistedToolCalls(sessionId, state = {}
for (const tool of toolCalls) {
const input = parseMaybeJsonObject(tool?.input) || (tool?.input && typeof tool.input === 'object' ? tool.input : {});
if (!isSubAgentActivityTool(tool, input)) continue;
const result = parseMaybeJsonObject(tool?.result) || (tool?.result && typeof tool.result === 'object' ? tool.result : {});
const threadId = normalizeCodexAppThreadId(
input.agentThreadId || input.agent_thread_id || input.threadId || input.thread_id
);
if (!threadId) continue;
const resultStates = result.agentsStates || result.agents_states || {};
const inputStates = input.agentsStates || input.agents_states || {};
const persistedState = resultStates[threadId] || inputStates[threadId] || {};
const recoveredState = { ...input, ...persistedState };
const recoveredPlanProgress = normalizeCcwebMcpChildPlanProgress(recoveredState.planProgress || recoveredState.plan_progress);
const existing = ccwebMcpChildThreads.get(threadId);
const child = existing || {
@@ -8662,12 +8676,17 @@ function recoverCcwebMcpChildThreadsFromPersistedToolCalls(sessionId, state = {}
parentSessionId,
parentThreadId,
spawnToolId: tool.id || '',
label: ccwebMcpRecoveredChildLabel(input, threadId),
role: String(input.role || input.agentRole || input.agent_role || '').trim(),
label: ccwebMcpRecoveredChildLabel(recoveredState, threadId),
role: String(recoveredState.role || recoveredState.agentRole || recoveredState.agent_role || '').trim(),
agentPath: String(recoveredState.agentPath || recoveredState.agent_path || '').trim(),
taskDescription: String(recoveredState.taskDescription || recoveredState.task_description || input.prompt || '').trim(),
lastAssistantMessage: '',
candidateResult: '',
finalMessage: '',
status: 'running',
planProgress: recoveredPlanProgress,
planCurrentStep: String(recoveredState.planCurrentStep || recoveredState.plan_current_step || '').trim(),
planUpdatedAt: recoveredState.planUpdatedAt || recoveredState.plan_updated_at || null,
summaryAttempts: 0,
createdAt: now,
updatedAt: now,
@@ -8675,9 +8694,18 @@ function recoverCcwebMcpChildThreadsFromPersistedToolCalls(sessionId, state = {}
child.parentSessionId = child.parentSessionId || parentSessionId;
child.parentThreadId = child.parentThreadId || parentThreadId;
child.spawnToolId = child.spawnToolId || tool.id || '';
child.label = ccwebMcpRecoveredChildLabel(input, child.label || threadId);
child.role = String(input.role || input.agentRole || input.agent_role || child.role || '').trim();
if (child.status !== 'closed') child.status = ccwebMcpRecoveredChildStatus(input, tool);
child.label = ccwebMcpRecoveredChildLabel(recoveredState, child.label || threadId);
child.role = String(recoveredState.role || recoveredState.agentRole || recoveredState.agent_role || child.role || '').trim();
child.agentPath = String(recoveredState.agentPath || recoveredState.agent_path || child.agentPath || '').trim();
child.taskDescription = String(recoveredState.taskDescription || recoveredState.task_description || input.prompt || child.taskDescription || '').trim();
if (child.status !== 'closed') child.status = ccwebMcpRecoveredChildStatus(recoveredState, tool);
if (recoveredPlanProgress) child.planProgress = recoveredPlanProgress;
if (recoveredState.planCurrentStep !== undefined || recoveredState.plan_current_step !== undefined) {
child.planCurrentStep = String(recoveredState.planCurrentStep || recoveredState.plan_current_step || '').trim();
}
if (recoveredState.planUpdatedAt || recoveredState.plan_updated_at) {
child.planUpdatedAt = recoveredState.planUpdatedAt || recoveredState.plan_updated_at;
}
child.updatedAt = child.updatedAt || now;
child.recoveredFromState = true;
ccwebMcpChildThreads.set(threadId, child);
@@ -8705,7 +8733,12 @@ function ccwebMcpChildPublicState(child = {}) {
threadId: child.threadId || '',
label: child.label || child.threadId || '子代理',
role: child.role || '',
agentPath: child.agentPath || '',
taskDescription: child.taskDescription || '',
status: child.status || 'running',
planProgress: normalizeCcwebMcpChildPlanProgress(child.planProgress),
planCurrentStep: child.planCurrentStep || '',
planUpdatedAt: child.planUpdatedAt || null,
detail: ccwebMcpChildSummary(child),
candidateResult,
finalMessage: child.finalMessage || '',
@@ -8714,6 +8747,7 @@ function ccwebMcpChildPublicState(child = {}) {
createdAt: child.createdAt || null,
updatedAt: child.updatedAt || null,
returnedAt: child.returnedAt || null,
interruptedAt: child.interruptedAt || null,
closedAt: child.closedAt || null,
};
}
@@ -8792,11 +8826,16 @@ function mergeCcwebMcpChildIntoTool(tool, child) {
title: child.title || child.label || previousState.title || '',
name: child.label || agentsStates[child.threadId]?.name || child.threadId,
role: child.role || agentsStates[child.threadId]?.role || '',
agentPath: child.agentPath || previousState.agentPath || previousState.agent_path || '',
status: child.status || 'running',
planProgress: normalizeCcwebMcpChildPlanProgress(child.planProgress),
planCurrentStep: child.planCurrentStep || '',
planUpdatedAt: child.planUpdatedAt || null,
taskDescription,
summary: ccwebMcpChildSummary(child),
candidateResult,
finalMessage: child.finalMessage || '',
interruptedAt: child.interruptedAt || null,
closedAt: child.closedAt || null,
returnedAt: child.returnedAt || null,
};
@@ -8869,31 +8908,57 @@ function sendCcwebMcpChildAgentUpdate(sessionId, child) {
}
function syncCcwebMcpChildAgentsFromCollabItem(routed, item = {}) {
if (!routed?.sessionId || item?.type !== 'collabAgentToolCall') return;
const toolName = codexAppCollabToolName(item.tool || item.name);
const receiverThreadIds = extractCcwebMcpStringArray(item.receiverThreadIds, item.receiver_thread_ids, item.targets);
const itemType = String(item?.type || '').trim();
const isSubAgentActivity = itemType === 'subAgentActivity';
if (!routed?.sessionId || (!isSubAgentActivity && itemType !== 'collabAgentToolCall')) return;
const toolName = isSubAgentActivity ? 'spawn_agent' : codexAppCollabToolName(item.tool || item.name);
const activityThreadId = isSubAgentActivity
? normalizeCodexAppThreadId(item.agentThreadId || item.agent_thread_id || item.threadId || item.thread_id)
: '';
const receiverThreadIds = activityThreadId
? [activityThreadId]
: extractCcwebMcpStringArray(item.receiverThreadIds, item.receiver_thread_ids, item.targets);
if (receiverThreadIds.length === 0) return;
const states = item.agentsStates && typeof item.agentsStates === 'object'
const activityAgentPath = String(item.agentPath || item.agent_path || '').trim();
const activityPrompt = String(item.prompt || item.taskDescription || item.task_description || '').trim();
const activityState = isSubAgentActivity ? {
label: activityAgentPath ? path.basename(activityAgentPath) : '',
role: String(item.role || item.agentRole || item.agent_role || '').trim(),
status: item.kind || item.status || 'started',
agentPath: activityAgentPath,
taskDescription: activityPrompt,
} : null;
const states = activityState
? { [activityThreadId]: activityState }
: (item.agentsStates && typeof item.agentsStates === 'object'
? item.agentsStates
: (item.agents_states && typeof item.agents_states === 'object' ? item.agents_states : {});
: (item.agents_states && typeof item.agents_states === 'object' ? item.agents_states : {}));
for (const threadId of receiverThreadIds) {
const state = states[threadId] && typeof states[threadId] === 'object' ? states[threadId] : {};
const existing = ccwebMcpChildThreads.get(threadId);
const now = new Date().toISOString();
const isSpawn = toolName === 'spawn_agent' || !existing;
const parentThreadId = routed.role === 'child'
? routed.child?.threadId || ''
: routed.entry?.threadId || item.senderThreadId || item.sender_thread_id || '';
const child = existing || {
threadId,
turnId: null,
parentSessionId: routed.sessionId,
parentThreadId: routed.entry?.threadId || item.senderThreadId || item.sender_thread_id || '',
parentThreadId,
spawnToolId: isSpawn ? item.id : '',
label: ccwebMcpChildLabel(state, threadId),
role: String(state.role || state.agent || state.agentType || state.agent_type || '').trim(),
agentPath: String(state.agentPath || state.agent_path || '').trim(),
taskDescription: String(state.taskDescription || state.task_description || item.prompt || '').trim(),
lastAssistantMessage: '',
candidateResult: '',
finalMessage: '',
status: 'running',
planProgress: null,
planCurrentStep: '',
planUpdatedAt: null,
summaryAttempts: 0,
createdAt: now,
updatedAt: now,
@@ -8901,16 +8966,24 @@ function syncCcwebMcpChildAgentsFromCollabItem(routed, item = {}) {
if (!child.spawnToolId && isSpawn) child.spawnToolId = item.id;
if (!child.spawnToolId && item.id) child.spawnToolId = item.id;
child.parentSessionId = child.parentSessionId || routed.sessionId;
child.parentThreadId = child.parentThreadId || routed.entry?.threadId || item.senderThreadId || item.sender_thread_id || '';
child.parentThreadId = child.parentThreadId || parentThreadId;
child.label = ccwebMcpChildLabel(state, child.label || threadId);
child.role = String(state.role || state.agent || state.agentType || state.agent_type || child.role || '').trim();
child.agentPath = String(state.agentPath || state.agent_path || child.agentPath || '').trim();
child.taskDescription = String(state.taskDescription || state.task_description || item.prompt || child.taskDescription || '').trim();
const planProgress = normalizeCcwebMcpChildPlanProgress(state.planProgress || state.plan_progress);
if (planProgress) child.planProgress = planProgress;
if (state.planCurrentStep !== undefined || state.plan_current_step !== undefined) {
child.planCurrentStep = String(state.planCurrentStep || state.plan_current_step || '').trim();
}
if (state.planUpdatedAt || state.plan_updated_at) child.planUpdatedAt = state.planUpdatedAt || state.plan_updated_at;
if (child.status !== 'closed') {
const candidate = extractCcwebMcpChildCandidate(state);
if (candidate) {
child.candidateResult = truncateTextValue(candidate, SESSION_MESSAGE_CONTENT_MAX_CHARS);
child.lastAssistantMessage = child.candidateResult;
}
const rawStatus = state.status || state.state || item.status;
const rawStatus = state.status || state.state || item.kind || item.status;
const fallback = child.status || 'running';
const nextStatus = ccwebMcpChildStatus(rawStatus, fallback);
child.status = nextStatus === 'returned' && !child.candidateResult && !candidate ? fallback : nextStatus;
@@ -8933,6 +9006,15 @@ function processCcwebMcpChildNotification(child, notification) {
return { changed: false, done: false };
}
const planUpdate = codexAppRuntime.planUpdateFromNotification(notification);
if (planUpdate) {
child.planProgress = normalizeCcwebMcpChildPlanProgress(planUpdate.progress);
child.planCurrentStep = planUpdate.currentStep || '';
child.planUpdatedAt = now;
child.updatedAt = now;
return { changed: true, done: false };
}
if (method === 'turn/started') {
child.status = 'running';
child.updatedAt = now;
@@ -9027,6 +9109,12 @@ function handleCodexAppNotification(notification) {
return;
}
const item = notification?.params?.item || null;
const isChildActivityItem = item?.type === 'collabAgentToolCall' || item?.type === 'subAgentActivity';
if (routed.role === 'child' && isChildActivityItem) {
syncCcwebMcpChildAgentsFromCollabItem(routed, item);
}
if (routed.role === 'child') {
const result = processCcwebMcpChildNotification(routed.child, notification);
if (result.changed) sendCcwebMcpChildAgentUpdate(routed.sessionId, routed.child);
@@ -9034,8 +9122,7 @@ function handleCodexAppNotification(notification) {
}
const result = codexAppRuntime.processCodexAppNotification(routed.entry, notification, routed.sessionId);
const item = notification?.params?.item || null;
if (item?.type === 'collabAgentToolCall') {
if (item?.type === 'collabAgentToolCall' || item?.type === 'subAgentActivity') {
syncCcwebMcpChildAgentsFromCollabItem(routed, item);
}
persistCodexAppTurnState(routed.sessionId, routed.entry, { immediate: !!result?.done });
@@ -9677,7 +9764,7 @@ function handleCodexAppServerExit(signature, info = {}) {
function codexAppModelSettings(session) {
const raw = String(session?.model || getDefaultCodexModel() || '').trim();
const match = raw.match(/^(.*)\((low|medium|high|xhigh)\)\s*$/i);
const match = raw.match(/^(.*)\((low|medium|high|xhigh|ultra)\)\s*$/i);
if (!match) return { model: raw || null, effort: null };
return {
model: String(match[1] || '').trim() || null,