chore: rebuild CentOS7 release package
This commit is contained in:
Binary file not shown.
@@ -872,7 +872,9 @@ function handleRequest(message) {
|
||||
method: 'thread/goal/updated',
|
||||
params: { threadId: thread.id, goal: thread.goal },
|
||||
});
|
||||
setTimeout(() => {
|
||||
send({ id, result: { goal: thread.goal } });
|
||||
}, 250);
|
||||
return;
|
||||
}
|
||||
if (method === 'thread/goal/clear') {
|
||||
|
||||
@@ -1500,8 +1500,22 @@ async function main() {
|
||||
assert(!storedCodexAppAfterRetryMismatch.messages.some((message) => message.role === 'assistant' && /codexapp retry thread mismatch prompt/.test(String(message.content || ''))), 'Codex App retry mismatch should not persist a successful assistant response on the wrong thread');
|
||||
|
||||
ws.send(JSON.stringify({ type: 'message', text: '/goal improve benchmark coverage', sessionId: codexAppSession.sessionId, mode: 'yolo', agent: 'codexapp' }));
|
||||
const codexAppGoalSyncing = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /正在同步 Goal/.test(msg.message || ''), 5000);
|
||||
assert(/正在同步 Goal/.test(codexAppGoalSyncing.message || ''), 'Codex App /goal should immediately show a syncing notice');
|
||||
const codexAppGoalRunningList = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'session_list' &&
|
||||
Array.isArray(msg.sessions) &&
|
||||
msg.sessions.some((session) => session.id === codexAppSession.sessionId && session.isRunning)
|
||||
), 5000);
|
||||
assert(codexAppGoalRunningList.sessions.some((session) => session.id === codexAppSession.sessionId && session.isRunning), 'Codex App /goal RPC should mark the session running while waiting for app-server');
|
||||
const codexAppGoalSet = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /Goal active/.test(msg.message || '') && /improve benchmark coverage/.test(msg.message || ''));
|
||||
assert(/Goal active/.test(codexAppGoalSet.message || ''), 'Codex App /goal should set an active goal');
|
||||
const codexAppGoalIdleList = await nextMessage(messages, ws, (msg) => (
|
||||
msg.type === 'session_list' &&
|
||||
Array.isArray(msg.sessions) &&
|
||||
msg.sessions.some((session) => session.id === codexAppSession.sessionId && !session.isRunning)
|
||||
), 5000);
|
||||
assert(codexAppGoalIdleList.sessions.some((session) => session.id === codexAppSession.sessionId && !session.isRunning), 'Codex App /goal RPC should clear running state after app-server responds');
|
||||
ws.send(JSON.stringify({ type: 'message', text: '/goal', sessionId: codexAppSession.sessionId, mode: 'yolo', agent: 'codexapp' }));
|
||||
const codexAppGoalShow = await nextMessage(messages, ws, (msg) => msg.type === 'system_message' && msg.sessionId === codexAppSession.sessionId && /Goal active/.test(msg.message || '') && /improve benchmark coverage/.test(msg.message || ''));
|
||||
assert(/improve benchmark coverage/.test(codexAppGoalShow.message || ''), 'Codex App /goal should show the current goal');
|
||||
|
||||
98
server.js
98
server.js
@@ -662,6 +662,9 @@ const activeProcesses = new Map();
|
||||
|
||||
// Active Codex app-server turns: sessionId -> { ws, threadId, turnId, fullText, toolCalls }
|
||||
const activeCodexAppTurns = new Map();
|
||||
|
||||
// Active Codex app-server goal RPCs: sessionId -> { id, ws, action, cancelled }
|
||||
const activeCodexAppGoalCommands = new Map();
|
||||
// ccweb MCP child agents tracked from Codex App native collaboration mode:
|
||||
// childThreadId -> { parentSessionId, parentThreadId, spawnToolId, ...state }
|
||||
const ccwebMcpChildThreads = new Map();
|
||||
@@ -2906,7 +2909,7 @@ function isCodexLikeSession(session) {
|
||||
}
|
||||
|
||||
function isSessionRunning(sessionId) {
|
||||
return activeProcesses.has(sessionId) || activeCodexAppTurns.has(sessionId);
|
||||
return activeProcesses.has(sessionId) || activeCodexAppTurns.has(sessionId) || activeCodexAppGoalCommands.has(sessionId);
|
||||
}
|
||||
|
||||
function getRuntimeSessionId(session) {
|
||||
@@ -6725,6 +6728,34 @@ function isCodexGoalUnsupportedError(err) {
|
||||
|| /goals feature is disabled|unsupported remote app-server request|method not found|unknown mock method/i.test(detail);
|
||||
}
|
||||
|
||||
function isCurrentCodexAppGoalCommand(sessionId, entry) {
|
||||
return !!entry && activeCodexAppGoalCommands.get(sessionId)?.id === entry.id && !entry.cancelled;
|
||||
}
|
||||
|
||||
function finishCodexAppGoalCommand(sessionId, entry) {
|
||||
if (!entry || activeCodexAppGoalCommands.get(sessionId)?.id !== entry.id) return false;
|
||||
activeCodexAppGoalCommands.delete(sessionId);
|
||||
broadcastSessionList();
|
||||
return true;
|
||||
}
|
||||
|
||||
function cancelCodexAppGoalCommand(sessionId, ws = null) {
|
||||
const entry = activeCodexAppGoalCommands.get(sessionId);
|
||||
if (!entry) return false;
|
||||
entry.cancelled = true;
|
||||
activeCodexAppGoalCommands.delete(sessionId);
|
||||
const targetWs = ws || entry.ws || null;
|
||||
if (targetWs) {
|
||||
wsSend(targetWs, {
|
||||
type: 'system_message',
|
||||
sessionId,
|
||||
message: '已取消 Goal 同步状态。底层 Codex app-server 请求可能仍会自然返回,结果将被忽略。',
|
||||
});
|
||||
}
|
||||
broadcastSessionList();
|
||||
return true;
|
||||
}
|
||||
|
||||
async function handleCodexAppGoalSlashCommand(ws, text, session) {
|
||||
const command = parseCodexGoalCommand(text);
|
||||
if (!command) return;
|
||||
@@ -6741,29 +6772,49 @@ async function handleCodexAppGoalSlashCommand(ws, text, session) {
|
||||
wsSend(ws, { type: 'system_message', sessionId: session.id, message: command.error });
|
||||
return;
|
||||
}
|
||||
if (activeCodexAppGoalCommands.has(session.id)) {
|
||||
wsSend(ws, { type: 'system_message', sessionId: session.id, message: 'Codex App Goal 正在同步,请稍候。' });
|
||||
return;
|
||||
}
|
||||
|
||||
const activeGoalCommand = {
|
||||
id: crypto.randomUUID(),
|
||||
ws,
|
||||
action: command.action,
|
||||
cancelled: false,
|
||||
startedAt: new Date().toISOString(),
|
||||
};
|
||||
activeCodexAppGoalCommands.set(session.id, activeGoalCommand);
|
||||
wsSend(ws, { type: 'system_message', sessionId: session.id, message: '正在同步 Goal...' });
|
||||
broadcastSessionList();
|
||||
|
||||
try {
|
||||
const { client, threadId } = await ensureCodexAppGoalThread(session);
|
||||
if (!isCurrentCodexAppGoalCommand(session.id, activeGoalCommand)) return;
|
||||
if (command.action === 'show') {
|
||||
const response = await client.request('thread/goal/get', { threadId }, 30000);
|
||||
if (!isCurrentCodexAppGoalCommand(session.id, activeGoalCommand)) return;
|
||||
const goal = normalizeCodexThreadGoal(response?.goal, threadId);
|
||||
wsSend(ws, {
|
||||
const targetWs = activeGoalCommand.ws || ws;
|
||||
wsSend(targetWs, {
|
||||
type: 'system_message',
|
||||
sessionId: session.id,
|
||||
message: goal ? formatCodexGoalUsage(goal) : '用法: /goal <目标描述>',
|
||||
});
|
||||
sendSessionList(ws);
|
||||
sendSessionList(targetWs);
|
||||
return;
|
||||
}
|
||||
|
||||
if (command.action === 'clear') {
|
||||
const response = await client.request('thread/goal/clear', { threadId }, 30000);
|
||||
wsSend(ws, {
|
||||
if (!isCurrentCodexAppGoalCommand(session.id, activeGoalCommand)) return;
|
||||
const targetWs = activeGoalCommand.ws || ws;
|
||||
wsSend(targetWs, {
|
||||
type: 'system_message',
|
||||
sessionId: session.id,
|
||||
message: response?.cleared ? 'Goal cleared' : 'No goal to clear',
|
||||
});
|
||||
sendSessionList(ws);
|
||||
sendSessionList(targetWs);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -6772,18 +6823,24 @@ async function handleCodexAppGoalSlashCommand(ws, text, session) {
|
||||
...(command.action === 'set' ? { objective: command.objective } : {}),
|
||||
status: command.action === 'pause' ? 'paused' : 'active',
|
||||
}, 30000);
|
||||
if (!isCurrentCodexAppGoalCommand(session.id, activeGoalCommand)) return;
|
||||
const goal = normalizeCodexThreadGoal(response?.goal, threadId);
|
||||
wsSend(ws, {
|
||||
const targetWs = activeGoalCommand.ws || ws;
|
||||
wsSend(targetWs, {
|
||||
type: 'system_message',
|
||||
sessionId: session.id,
|
||||
message: goal ? formatCodexGoalUsage(goal) : 'Goal updated',
|
||||
});
|
||||
sendSessionList(ws);
|
||||
sendSessionList(targetWs);
|
||||
} catch (err) {
|
||||
const message = isCodexGoalUnsupportedError(err)
|
||||
? '当前 Codex app-server 不支持 /goal,请升级 Codex 或启用 goals feature。'
|
||||
: `Goal failed: ${err?.message || err}`;
|
||||
wsSend(ws, { type: 'system_message', sessionId: session.id, message });
|
||||
if (isCurrentCodexAppGoalCommand(session.id, activeGoalCommand)) {
|
||||
wsSend(activeGoalCommand.ws || ws, { type: 'system_message', sessionId: session.id, message });
|
||||
}
|
||||
} finally {
|
||||
finishCodexAppGoalCommand(session.id, activeGoalCommand);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6799,6 +6856,10 @@ function handleSlashCommand(ws, text, sessionId, fallbackAgent) {
|
||||
wsSend(ws, { type: 'system_message', message: 'Codex App 运行中暂不支持 slash 指令,请等待完成或点击停止。' });
|
||||
return;
|
||||
}
|
||||
if (session && isCodexAppSession(session) && activeCodexAppGoalCommands.has(sessionId)) {
|
||||
wsSend(ws, { type: 'system_message', sessionId, message: 'Codex App Goal 正在同步,请稍候。' });
|
||||
return;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case '/clear': {
|
||||
@@ -7338,6 +7399,11 @@ function handleLoadSession(ws, msg) {
|
||||
text: truncateTextValue(entry.fullText || '', SESSION_MESSAGE_CONTENT_MAX_CHARS),
|
||||
toolCalls: sanitizeToolCallsForPersist(entry.toolCalls || []),
|
||||
});
|
||||
} else if (activeCodexAppGoalCommands.has(sessionId)) {
|
||||
const entry = activeCodexAppGoalCommands.get(sessionId);
|
||||
entry.ws = ws;
|
||||
entry.wsDisconnectTime = null;
|
||||
wsSend(ws, { type: 'system_message', sessionId, message: '正在同步 Goal...' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7406,6 +7472,11 @@ function handleDeleteSession(ws, sessionId) {
|
||||
pendingSlashCommands.delete(sessionId);
|
||||
pendingCompactRetries.delete(sessionId);
|
||||
cancelCodexCapacityRetry(sessionId);
|
||||
if (activeCodexAppGoalCommands.has(sessionId)) {
|
||||
const entry = activeCodexAppGoalCommands.get(sessionId);
|
||||
entry.cancelled = true;
|
||||
activeCodexAppGoalCommands.delete(sessionId);
|
||||
}
|
||||
deleteCrossConversationRepliesForSession(sessionId);
|
||||
for (const [threadId, child] of ccwebMcpChildThreads.entries()) {
|
||||
if (child.parentSessionId === sessionId) ccwebMcpChildThreads.delete(threadId);
|
||||
@@ -7525,6 +7596,7 @@ function handleAbort(ws) {
|
||||
const sessionId = wsSessionMap.get(ws);
|
||||
if (!sessionId) return;
|
||||
if (handleCodexAppAbortSession(sessionId, ws)) return;
|
||||
if (cancelCodexAppGoalCommand(sessionId, ws)) return;
|
||||
const entry = activeProcesses.get(sessionId);
|
||||
if (!entry) {
|
||||
if (cancelCodexCapacityRetry(sessionId)) {
|
||||
@@ -7642,6 +7714,10 @@ function handleMessage(ws, msg, options = {}) {
|
||||
return handleCodexAppSteerMessage(ws, msg, options);
|
||||
}
|
||||
|
||||
if (sessionId && activeCodexAppGoalCommands.has(sessionId)) {
|
||||
return fail('session_running', 'Codex App Goal 正在同步,请稍候。');
|
||||
}
|
||||
|
||||
if (sessionId && activeProcesses.has(sessionId)) {
|
||||
return fail('session_running', '正在处理中,请先点击停止按钮。');
|
||||
}
|
||||
@@ -7968,6 +8044,12 @@ function detachWsFromActiveRuntimes(ws, options = {}) {
|
||||
if (disconnectTime) entry.wsDisconnectTime = disconnectTime;
|
||||
}
|
||||
}
|
||||
for (const [, entry] of activeCodexAppGoalCommands) {
|
||||
if (entry.ws === ws) {
|
||||
entry.ws = null;
|
||||
if (disconnectTime) entry.wsDisconnectTime = disconnectTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function findCodexAppEntryByRuntime(params = {}) {
|
||||
|
||||
Reference in New Issue
Block a user