feat(clarification): rehydrate pending questions on reconnect + restart
A stage parked on operator clarification lost its modal whenever the client disconnected or the server restarted — the questions were stranded in the event log with no way to re-surface them (unlike approvals, which already rehydrate). Two composing mechanisms, mirroring the approval path: - On reconnect: SessionEventBridge.replaySnapshot() now re-pushes the last unanswered ClarificationRequestedEvent for a PAUSED/CLARIFICATION_PENDING session as a ClarificationRequired message. Gated on the orchestrator still holding a live deferred (SessionOrchestrator.liveClarificationRequestIds) so a client never sees a modal whose answer could not be delivered. - On restart: ServerModule.resumeAbandonedSessions() eagerly relaunches clarification-parked sessions (approval-parked stay lazy). rehydrate+resume re-runs the parked stage, which re-emits a fresh, live ClarificationRequestedEvent (round budget MAX=3, 1 prior round recorded, so it re-parks rather than silently proceeding), re-arming the deferred that the reconnect path gates on. pauseReason == "CLARIFICATION_PENDING" is the discriminator because the reducer sets pendingApproval=true for every pause.
This commit is contained in:
+9
@@ -312,6 +312,15 @@ abstract class SessionOrchestrator(
|
||||
ConcurrentHashMap<ClarificationRequestId, CompletableDeferred<List<ClarificationAnswer>>> =
|
||||
ConcurrentHashMap()
|
||||
|
||||
/**
|
||||
* Request-ids of clarifications this process is still parked on (the [CompletableDeferred] is
|
||||
* live and awaitable). A reconnecting client uses this to re-push only clarifications that can
|
||||
* actually be answered — a request whose deferred died with a server restart is excluded, so the
|
||||
* client never shows a modal that would resume nothing.
|
||||
*/
|
||||
fun liveClarificationRequestIds(): Set<String> =
|
||||
pendingClarifications.keys.mapTo(mutableSetOf()) { it.value }
|
||||
|
||||
/** Cache mapping "sessionId:artifactId" to JSON-serialized artifact payload content.
|
||||
* Populated when ProcessResult artifacts are stored on tool failure.
|
||||
* Used by DefaultSessionOrchestrator.step() to populate EvaluationContext.artifactContent. */
|
||||
|
||||
Reference in New Issue
Block a user