Skip to content

symfonic.agent.native_pause_events

native_pause_events

The built-in one-question ask_user pause, carried on the typed wire.

Shared by recovered continuations and the selected root's guarded typed stream, so a pause minted with the platform's default registration is framed the same way on both: as the generic InterruptEvent with its real one-field payload, never as an AskUserQuestionEvent it cannot satisfy.

native_pause_events async

native_pause_events(source: AsyncIterator[AgentEvent]) -> AsyncIterator[AgentEvent]

Keep the native one-question ask_user schema honest on the typed wire.

The shared projector's AskUserQuestionEvent requires a different, multi-question payload. The platform's built-in one-question registration therefore uses the generic typed interrupt, retaining its real payload.

Source code in src/symfonic/agent/native_pause_events.py
async def native_pause_events(source: AsyncIterator[AgentEvent]) -> AsyncIterator[AgentEvent]:
    """Keep the native one-question ask_user schema honest on the typed wire.

    The shared projector's ``AskUserQuestionEvent`` requires a different,
    multi-question payload. The platform's built-in one-question registration
    therefore uses the generic typed interrupt, retaining its real payload.
    """
    from hashlib import sha256

    from symfonic.capabilities.human.factory import AskUserPayload

    async with aclosing(source):
        async for event in source:
            pending = event.interrupt
            if (
                event.kind == "ask_user"
                and pending is not None
                and isinstance(pending.payload, AskUserPayload)
            ):
                # The one-question registration has a tool call id, but no
                # generic interrupt id. Bind a stable public correlation id
                # to the already-opaque token without minting new authority.
                identifier = f"i-{sha256(pending.token.encode()).hexdigest()[:12]}"
                native = pending.model_copy(
                    update={"kind": "interrupt", "interrupt_id": identifier}
                )
                yield event.model_copy(update={"kind": "interrupt", "interrupt": native})
            else:
                yield event