Stabilize ccweb codex app runtime
This commit is contained in:
@@ -162,6 +162,49 @@ function completeTurn(thread, turnId, text, status = 'completed') {
|
||||
});
|
||||
}
|
||||
|
||||
if (/huge output/i.test(text)) {
|
||||
const hugeOutput = `huge-output-start\n${'0123456789abcdef'.repeat(30000)}\nhuge-output-end\n`;
|
||||
send({
|
||||
method: 'item/started',
|
||||
params: {
|
||||
threadId: thread.id,
|
||||
turnId,
|
||||
startedAtMs: Date.now(),
|
||||
item: {
|
||||
id: 'huge-tool',
|
||||
type: 'commandExecution',
|
||||
command: '/bin/bash -lc huge-output',
|
||||
status: 'inProgress',
|
||||
},
|
||||
},
|
||||
});
|
||||
send({
|
||||
method: 'item/commandExecution/outputDelta',
|
||||
params: {
|
||||
threadId: thread.id,
|
||||
turnId,
|
||||
itemId: 'huge-tool',
|
||||
delta: hugeOutput,
|
||||
},
|
||||
});
|
||||
send({
|
||||
method: 'item/completed',
|
||||
params: {
|
||||
threadId: thread.id,
|
||||
turnId,
|
||||
completedAtMs: Date.now(),
|
||||
item: {
|
||||
id: 'huge-tool',
|
||||
type: 'commandExecution',
|
||||
command: '/bin/bash -lc huge-output',
|
||||
aggregatedOutput: hugeOutput,
|
||||
exitCode: 0,
|
||||
status: 'completed',
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (/subagent|collab/i.test(text)) {
|
||||
send({
|
||||
method: 'item/started',
|
||||
|
||||
@@ -859,6 +859,19 @@ async function main() {
|
||||
assert(storedCodexApp.messages.some((message) => message.role === 'assistant' && /codexapp tool prompt/.test(String(message.content || ''))), 'Codex App assistant response should be persisted');
|
||||
assert((storedCodexApp.totalUsage?.inputTokens || 0) > 0, 'Codex App token usage should be persisted');
|
||||
|
||||
ws.send(JSON.stringify({ type: 'message', text: 'codexapp huge output prompt', sessionId: codexAppSession.sessionId, mode: 'yolo', agent: 'codexapp' }));
|
||||
const codexAppHugeTool = await nextMessage(messages, ws, (msg) => msg.type === 'tool_end' && msg.sessionId === codexAppSession.sessionId && msg.toolUseId === 'huge-tool');
|
||||
assert((codexAppHugeTool.result || '').length <= 33000, 'Codex App huge tool result should be capped before sending to the browser');
|
||||
assert(/内容过长|huge-output-start/.test(codexAppHugeTool.result || ''), 'Codex App huge tool result should keep a clear truncated preview');
|
||||
await nextMessage(messages, ws, (msg) => msg.type === 'done' && msg.sessionId === codexAppSession.sessionId);
|
||||
storedCodexApp = JSON.parse(fs.readFileSync(path.join(sessionsDir, `${codexAppSession.sessionId}.json`), 'utf8'));
|
||||
const persistedHugeTool = storedCodexApp.messages
|
||||
.flatMap((message) => Array.isArray(message.toolCalls) ? message.toolCalls : [])
|
||||
.find((tool) => tool.id === 'huge-tool');
|
||||
assert(persistedHugeTool, 'Codex App huge tool call should be persisted as a preview');
|
||||
assert(String(persistedHugeTool.result || '').length <= 33000, 'Persisted Codex App huge tool result should be capped');
|
||||
assert(fs.statSync(path.join(sessionsDir, `${codexAppSession.sessionId}.json`)).size < 1024 * 1024, 'Codex App huge output should not inflate session JSON beyond 1MB');
|
||||
|
||||
const reloadMcpResult = await postAuthedJson(port, token, `/api/sessions/${codexAppSession.sessionId}/reload-mcp`);
|
||||
assert(reloadMcpResult.sessionId === codexAppSession.sessionId, 'Codex App MCP reload should return the target session id');
|
||||
assert(reloadMcpResult.result?.reloaded === true, 'Codex App MCP reload should call app-server config/mcpServer/reload');
|
||||
@@ -1098,6 +1111,56 @@ async function main() {
|
||||
} finally {
|
||||
await restartedRecoveryServer.stop();
|
||||
}
|
||||
|
||||
const oversizedRecoverySessionId = 'oversized-recovery-session';
|
||||
const oversizedRecoveryStateDir = path.join(sessionsDir, `${oversizedRecoverySessionId}-run`);
|
||||
const oversizedRecoveryStatePath = path.join(oversizedRecoveryStateDir, 'codexapp-state.json');
|
||||
fs.writeFileSync(path.join(sessionsDir, `${oversizedRecoverySessionId}.json`), JSON.stringify({
|
||||
id: oversizedRecoverySessionId,
|
||||
title: 'Oversized Recovery',
|
||||
created: new Date().toISOString(),
|
||||
updated: new Date().toISOString(),
|
||||
pinnedAt: null,
|
||||
agent: 'codexapp',
|
||||
claudeSessionId: null,
|
||||
codexThreadId: null,
|
||||
codexAppThreadId: 'oversized-thread',
|
||||
model: 'gpt-5.5(xhigh)',
|
||||
permissionMode: 'yolo',
|
||||
totalCost: 0,
|
||||
totalUsage: { inputTokens: 0, cachedInputTokens: 0, outputTokens: 0 },
|
||||
messages: [],
|
||||
cwd: homeDir,
|
||||
}, null, 2));
|
||||
mkdirp(oversizedRecoveryStateDir);
|
||||
fs.writeFileSync(oversizedRecoveryStatePath, JSON.stringify({
|
||||
version: 1,
|
||||
agent: 'codexapp',
|
||||
sessionId: oversizedRecoverySessionId,
|
||||
threadId: 'oversized-thread',
|
||||
turnId: 'oversized-turn',
|
||||
turnStatus: 'running',
|
||||
fullText: 'x'.repeat(5 * 1024 * 1024),
|
||||
toolCalls: [],
|
||||
}));
|
||||
assert(fs.statSync(oversizedRecoveryStatePath).size > 4 * 1024 * 1024, 'Oversized recovery fixture should exceed the state load guard');
|
||||
|
||||
const oversizedRecoveryServer = await startServer(recoveryEnv);
|
||||
try {
|
||||
const { ws, messages } = await connectWs(recoveryPort, password);
|
||||
await nextMessage(messages, ws, (msg) => msg.type === 'session_list' && msg.sessions.some((session) => session.id === oversizedRecoverySessionId));
|
||||
assert(!fs.existsSync(oversizedRecoveryStateDir), 'Oversized Codex App recovery state directory should be cleaned without parsing the state');
|
||||
ws.send(JSON.stringify({ type: 'load_session', sessionId: oversizedRecoverySessionId }));
|
||||
const oversizedSessionInfo = await nextMessage(messages, ws, (msg) => msg.type === 'session_info' && msg.sessionId === oversizedRecoverySessionId);
|
||||
assert((oversizedSessionInfo.messages || []).some((message) => (
|
||||
message.role === 'system' &&
|
||||
/状态文件异常/.test(String(message.content || '')) &&
|
||||
/跳过恢复/.test(String(message.content || ''))
|
||||
)), 'Oversized Codex App recovery should add a system notice');
|
||||
ws.close();
|
||||
} finally {
|
||||
await oversizedRecoveryServer.stop();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((err) => {
|
||||
|
||||
Reference in New Issue
Block a user