fix: harden agent and WeChat run completion
This commit is contained in:
+33
-17
@@ -488,7 +488,7 @@ export function createAgentRunGateway({
|
||||
return true;
|
||||
}
|
||||
|
||||
async function markRun(runId, status, fields = {}) {
|
||||
async function markRun(runId, status, fields = {}, { expectedStatus = null } = {}) {
|
||||
const updates = ['status = ?', 'updated_at = ?'];
|
||||
const values = [status, nowMs()];
|
||||
for (const [key, value] of Object.entries(fields)) {
|
||||
@@ -496,12 +496,18 @@ export function createAgentRunGateway({
|
||||
values.push(value);
|
||||
}
|
||||
values.push(runId);
|
||||
await pool.query(
|
||||
`UPDATE h5_agent_runs SET ${updates.join(', ')} WHERE id = ?`,
|
||||
const where = expectedStatus
|
||||
? 'WHERE id = ? AND status = ?'
|
||||
: 'WHERE id = ?';
|
||||
if (expectedStatus) values.push(expectedStatus);
|
||||
const [result] = await pool.query(
|
||||
`UPDATE h5_agent_runs SET ${updates.join(', ')} ${where}`,
|
||||
values,
|
||||
);
|
||||
if (Number(result?.affectedRows ?? 0) === 0) return false;
|
||||
await appendEvent(runId, status, fields);
|
||||
await appendRunSnapshot(runId);
|
||||
return true;
|
||||
}
|
||||
|
||||
function startRunHeartbeat(runId, { attempt }) {
|
||||
@@ -940,11 +946,12 @@ export function createAgentRunGateway({
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
await markRun(runId, 'succeeded', {
|
||||
const marked = await markRun(runId, 'succeeded', {
|
||||
agent_session_id: sessionId,
|
||||
completed_at: nowMs(),
|
||||
error_message: null,
|
||||
});
|
||||
}, { expectedStatus: 'running' });
|
||||
if (!marked) return false;
|
||||
if (typeof observePersonalMemoryOnSuccess === 'function') {
|
||||
await observePersonalMemoryOnSuccess({
|
||||
userId: row.user_id,
|
||||
@@ -999,7 +1006,7 @@ export function createAgentRunGateway({
|
||||
await markRun(runId, retryable ? 'retryable' : 'failed', {
|
||||
error_message: message,
|
||||
completed_at: retryable ? null : nowMs(),
|
||||
});
|
||||
}, { expectedStatus: 'running' });
|
||||
if (retryable && autoDispatch) {
|
||||
setTimeout(() => dispatchRun(runId), retryDelaysMs[nextAttempt - 1]);
|
||||
}
|
||||
@@ -1129,6 +1136,7 @@ export function createAgentRunGateway({
|
||||
);
|
||||
const startedCutoff = nowMs() - normalizedStaleMs;
|
||||
const sessionFinishedCutoff = nowMs() - normalizedSessionFinishedGraceMs;
|
||||
const heartbeatCutoff = nowMs() - normalizedStaleMs;
|
||||
const [rows] = await pool.query(
|
||||
`SELECT
|
||||
r.id,
|
||||
@@ -1155,6 +1163,7 @@ export function createAgentRunGateway({
|
||||
) sf ON sf.run_id = r.id
|
||||
WHERE r.status = 'running'
|
||||
AND r.started_at IS NOT NULL
|
||||
AND (h.latest_heartbeat_at IS NULL OR h.latest_heartbeat_at <= ?)
|
||||
AND (
|
||||
r.started_at <= ?
|
||||
OR (
|
||||
@@ -1164,7 +1173,7 @@ export function createAgentRunGateway({
|
||||
)
|
||||
ORDER BY COALESCE(sf.session_finished_at, r.started_at) ASC
|
||||
LIMIT ?`,
|
||||
[startedCutoff, sessionFinishedCutoff, normalizedLimit],
|
||||
[heartbeatCutoff, startedCutoff, sessionFinishedCutoff, normalizedLimit],
|
||||
);
|
||||
const recovered = [];
|
||||
for (const row of rows) {
|
||||
@@ -1198,11 +1207,12 @@ export function createAgentRunGateway({
|
||||
requireRecoverableError: false,
|
||||
});
|
||||
if (deliverableRecovered) {
|
||||
await markRun(row.id, 'succeeded', {
|
||||
const marked = await markRun(row.id, 'succeeded', {
|
||||
agent_session_id: row.agent_session_id ?? null,
|
||||
completed_at: nowMs(),
|
||||
error_message: null,
|
||||
});
|
||||
}, { expectedStatus: 'running' });
|
||||
if (!marked) continue;
|
||||
item.status = 'succeeded';
|
||||
item.recoveredAs = 'deliverables';
|
||||
recovered.push(item);
|
||||
@@ -1223,15 +1233,21 @@ export function createAgentRunGateway({
|
||||
AND started_at IS NOT NULL
|
||||
AND (
|
||||
started_at <= ?
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM h5_agent_run_events sf
|
||||
WHERE sf.run_id = h5_agent_runs.id
|
||||
AND sf.event_type = 'session_finished'
|
||||
AND sf.created_at <= ?
|
||||
OR EXISTS (
|
||||
SELECT 1
|
||||
FROM h5_agent_run_events sf
|
||||
WHERE sf.run_id = h5_agent_runs.id
|
||||
AND sf.event_type = 'session_finished'
|
||||
AND sf.created_at <= ?
|
||||
)
|
||||
)`,
|
||||
[message, completedAt, completedAt, row.id, startedCutoff, sessionFinishedCutoff],
|
||||
)
|
||||
AND (
|
||||
SELECT COALESCE(MAX(hb.created_at), h5_agent_runs.started_at)
|
||||
FROM h5_agent_run_events hb
|
||||
WHERE hb.run_id = h5_agent_runs.id
|
||||
AND hb.event_type = 'worker_heartbeat'
|
||||
) <= ?`,
|
||||
[message, completedAt, completedAt, row.id, startedCutoff, sessionFinishedCutoff, heartbeatCutoff],
|
||||
);
|
||||
if (Number(update?.affectedRows ?? 0) === 0) continue;
|
||||
await appendEvent(row.id, 'stale_recovered', {
|
||||
|
||||
Reference in New Issue
Block a user