chore: rebuild release package
This commit is contained in:
147
server.js
147
server.js
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user