fix: treat signal-killed agents as success when output was delivered

Two bugs fixed:

1. agent-runner: process close handler only checked `code !== 0`,
   but signal kills (SIGTERM/SIGKILL from post-close cleanup) set
   code=null which was misclassified as error even after successful
   output delivery. Now checks `code === null && signal` and resolves
   as success when hadStreamingOutput is true.

2. message-agent-executor: wrappedOnOutput persisted newSessionId
   before checking for poisoned session, allowing a stale session to
   be re-saved after clearSession. Reordered to check poison first
   and guard persist with !resetSessionRequested.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Eyejoker
2026-03-26 21:25:30 +09:00
parent c4744177e9
commit d1c693fb17
2 changed files with 54 additions and 4 deletions

View File

@@ -269,7 +269,7 @@ export async function runAgentProcess(
}
});
proc.on('close', (code) => {
proc.on('close', (code, signal) => {
clearTimeout(timeout);
const duration = Date.now() - startTime;
@@ -286,6 +286,7 @@ export async function runAgentProcess(
`Process: ${processName}`,
`Duration: ${duration}ms`,
`Exit Code: ${code}`,
`Signal: ${signal}`,
`Had Streaming Output: ${hadStreamingOutput}`,
].join('\n'),
);
@@ -299,6 +300,7 @@ export async function runAgentProcess(
processName,
duration,
code,
signal,
},
'Agent timed out after output (idle cleanup)',
);
@@ -316,6 +318,49 @@ export async function runAgentProcess(
return;
}
// Signal kill (SIGTERM/SIGKILL) from post-close cleanup or service
// restart. When the agent already delivered streaming output this is
// normal lifecycle — not an error.
if (code === null && signal) {
if (hadStreamingOutput) {
logger.info(
{
group: group.name,
chatJid: input.chatJid,
runId: input.runId,
processName,
duration,
signal,
},
'Agent terminated by signal after output delivery (normal cleanup)',
);
outputChain.then(() => {
resolve({ status: 'success', result: null, newSessionId });
});
return;
}
// No output delivered before signal kill — genuine error
logger.error(
{
group: group.name,
chatJid: input.chatJid,
runId: input.runId,
processName,
duration,
signal,
},
'Agent killed by signal before producing output',
);
outputChain.then(() => {
resolve({
status: 'error',
result: null,
error: `Agent killed by ${signal} before producing output`,
});
});
return;
}
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
const logFile = path.join(
logsDir,
@@ -335,6 +380,7 @@ export async function runAgentProcess(
`AgentType: ${group.agentType || 'claude-code'}`,
`Duration: ${duration}ms`,
`Exit Code: ${code}`,
`Signal: ${signal}`,
``,
];