diff --git a/scripts/src/agentrun/secrets.ts b/scripts/src/agentrun/secrets.ts index 524bb453..d8de8451 100644 --- a/scripts/src/agentrun/secrets.ts +++ b/scripts/src/agentrun/secrets.ts @@ -1193,7 +1193,11 @@ try { ") command_facts ON TRUE", "LEFT JOIN LATERAL (", " SELECT COUNT(*)::int AS event_count, COALESCE(jsonb_agg(jsonb_build_object(", - " 'seq', e.seq, 'type', e.type, 'phase', e.payload->>'phase', 'commandId', e.payload->>'commandId', 'createdAt', e.created_at", + " 'seq', e.seq, 'type', e.type, 'phase', e.payload->>'phase', 'commandId', e.payload->>'commandId',", + " 'userMessageIdPresent', jsonb_typeof(e.payload->'userMessageId') = 'string' AND length(e.payload->>'userMessageId') > 0,", + " 'textPresent', jsonb_typeof(e.payload->'text') = 'string' AND length(e.payload->>'text') > 0,", + " 'sourcePresent', jsonb_typeof(e.payload->'source') = 'string' AND length(e.payload->>'source') > 0,", + " 'createdAt', e.created_at", " ) ORDER BY e.seq), '[]'::jsonb) AS events", " FROM (SELECT seq, type, payload, created_at FROM agentrun_events WHERE run_id = j.run_id ORDER BY seq LIMIT 501) e", ") event_facts ON TRUE", @@ -1502,8 +1506,21 @@ function hasNoBusinessProgress(fact) { if (runCreated === null || runCreated !== runUpdated || commandCreated === null || commandCreated !== commandUpdated) return false; if (now - runCreated < stalePendingMaxAgeMs || now - commandCreated < stalePendingMaxAgeMs) return false; if (fact.commandCount !== 1) return false; - const phases = fact.events.map((event) => event?.phase || null); - if (fact.events.some((event) => event?.type !== "backend_status" || typeof event?.phase !== "string" || !allowedNoProgressPhases.has(event.phase))) return false; + const userMessages = fact.events.filter((event) => event?.type === "user_message"); + if (userMessages.length > 1) return false; + const phases = fact.events.flatMap((event) => { + if (event?.type === "user_message") { + const valid = event.commandId === fact.commandId + && event.userMessageIdPresent === true + && event.textPresent === true + && event.sourcePresent === true; + return valid ? [] : [null]; + } + return event?.type === "backend_status" && typeof event?.phase === "string" && allowedNoProgressPhases.has(event.phase) + ? [event.phase] + : [null]; + }); + if (phases.includes(null)) return false; if (!phases.includes("run-created") || !phases.includes("command-created") || !phases.includes("runner-job-created")) return false; return fact.events.every((event) => event?.commandId === null || event?.commandId === undefined || event.commandId === fact.commandId); }