Retry Agent bridge answers across framework races

Keep internal bridge requests using framework question state, distinguish bridge responses from user answers in Genie logs, and retry Agent-side bridge answers across short framework timing races instead of failing immediately.

Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
Iliyan Malchev
2026-03-19 01:22:27 -07:00
parent 9de3f3ba6a
commit cf1f537879
2 changed files with 37 additions and 2 deletions

View File

@@ -4,6 +4,7 @@ import android.app.agent.AgentManager
import android.app.agent.AgentService
import android.app.agent.AgentSessionEvent
import android.app.agent.AgentSessionInfo
import android.os.Process
import android.util.Log
import org.json.JSONObject
import java.util.concurrent.ConcurrentHashMap
@@ -12,6 +13,8 @@ import kotlin.concurrent.thread
class CodexAgentService : AgentService() {
companion object {
private const val TAG = "CodexAgentService"
private const val BRIDGE_ANSWER_RETRY_COUNT = 10
private const val BRIDGE_ANSWER_RETRY_DELAY_MS = 50L
private const val BRIDGE_REQUEST_PREFIX = "__codex_bridge__ "
private const val BRIDGE_RESPONSE_PREFIX = "__codex_bridge_result__ "
private const val METHOD_GET_AUTH_STATUS = "get_auth_status"
@@ -110,7 +113,7 @@ class CodexAgentService : AgentService() {
}
runCatching {
manager.answerQuestion(sessionId, "$BRIDGE_RESPONSE_PREFIX$response")
answerBridgeQuestion(manager, sessionId, "$BRIDGE_RESPONSE_PREFIX$response")
}.onFailure { err ->
handledBridgeRequests.remove(requestKey)
Log.w(TAG, "Failed to answer bridge question for $sessionId", err)
@@ -118,6 +121,21 @@ class CodexAgentService : AgentService() {
}
}
private fun answerBridgeQuestion(manager: AgentManager, sessionId: String, response: String) {
repeat(BRIDGE_ANSWER_RETRY_COUNT) { attempt ->
runCatching {
manager.answerQuestion(sessionId, response)
}.onSuccess {
return
}.onFailure { err ->
if (attempt == BRIDGE_ANSWER_RETRY_COUNT - 1 || !isBridgeQuestionPending(manager, sessionId, err)) {
throw err
}
Thread.sleep(BRIDGE_ANSWER_RETRY_DELAY_MS)
}
}
}
private fun hasAnswerForRequest(events: List<AgentSessionEvent>, requestId: String): Boolean {
return events.any { event ->
if (event.type != AgentSessionEvent.TYPE_ANSWER || event.message == null) {
@@ -132,4 +150,20 @@ class CodexAgentService : AgentService() {
}.getOrNull() == requestId
}
}
private fun isSessionWaitingForUser(manager: AgentManager, sessionId: String): Boolean {
return manager.getSessions(Process.myUid() / 100000).any { session ->
session.sessionId == sessionId &&
session.state == AgentSessionInfo.STATE_WAITING_FOR_USER
}
}
private fun isBridgeQuestionPending(
manager: AgentManager,
sessionId: String,
err: Throwable,
): Boolean {
return err.message?.contains("not waiting for user input", ignoreCase = true) == true ||
!isSessionWaitingForUser(manager, sessionId)
}
}