Member Junction
    Preparing search index...

    Interface FireAndForgetConfig<TResult>

    Configuration for a fire-and-forget GraphQL mutation execution.

    Fire-and-forget avoids Azure's ~230s HTTP proxy timeout by:

    1. Sending the mutation with fireAndForget: true
    2. Server validates input, starts background execution, returns immediately
    3. Client listens for completion via WebSocket PubSub subscription
    4. Server publishes result through PubSub when execution finishes
    interface FireAndForgetConfig<TResult> {
        createErrorResult: (errorMessage: string) => TResult;
        dataProvider: GraphQLDataProvider;
        deferMessagesUntilAck?: boolean;
        extractResult: (parsed: Record<string, unknown>) => TResult;
        extractSyncResult?: (ackResult: Record<string, unknown>) => TResult;
        isCompletionEvent: (parsed: Record<string, unknown>) => boolean;
        isRelevantMessage?: (parsed: Record<string, unknown>) => boolean;
        maxStallReconciles?: number;
        mutation: string;
        mutationFieldName: string;
        onMessage?: (parsed: Record<string, unknown>) => void;
        onStall?: () => Promise<StallDecision<TResult>>;
        operationLabel?: string;
        timeoutErrorMessage?: string;
        timeoutMs?: number;
        validateAck: (ackResult: Record<string, unknown>) => boolean;
        variables: Record<string, unknown>;
    }

    Type Parameters

    • TResult

      The type of the final result

    Index

    Properties

    createErrorResult: (errorMessage: string) => TResult

    Create an error result when the server rejects the request or when a non-exception error occurs.

    dataProvider: GraphQLDataProvider

    The GraphQL data provider instance

    deferMessagesUntilAck?: boolean

    Hold PubSub messages that arrive before the acknowledgement, then route them, in arrival order, once validateAck has accepted it. For an operation whose identity comes FROM the acknowledgement (the server names the record it created), so a message that beats the ack over the WebSocket can still be attributed rather than lost. Off by default: messages are routed as they arrive, exactly as before.

    extractResult: (parsed: Record<string, unknown>) => TResult

    Extract the final result from a matching completion event. Called when isCompletionEvent returns true.

    extractSyncResult?: (ackResult: Record<string, unknown>) => TResult

    Optional SYNCHRONOUS-fallback extractor for headless / non-browser clients.

    The fire-and-forget pattern delivers its result over a WebSocket PubSub channel (GraphQLDataProvider.PushStatusUpdates). A headless client — the integration GraphQL client, a Node/MCP consumer, any provider without a subscription surface — does not implement PushStatusUpdates, so it can never hear the completion event. Historically this crashed with PushStatusUpdates is not a function.

    When this extractor is supplied AND the data provider has no PushStatusUpdates method, FireAndForgetHelper.Execute instead runs the mutation in SYNCHRONOUS mode (it forces fireAndForget: false), so the resolver awaits the run and returns the full result inline in the mutation's own return payload. This function receives that ack payload (result[mutationFieldName]) and produces the final TResult.

    When omitted, a provider lacking PushStatusUpdates degrades to a clear error result via createErrorResult rather than crashing.

    isCompletionEvent: (parsed: Record<string, unknown>) => boolean

    Check if a parsed PubSub message is the matching completion event. Called for every PubSub message received on the session. Return true only for the completion event that matches this specific request.

    isRelevantMessage?: (parsed: Record<string, unknown>) => boolean

    Optional predicate deciding whether a PubSub message belongs to THIS operation.

    The push-status subscription is per-SESSION, not per-operation, so without this every message for every concurrent run resets every pending operation's idle timer. With several agents in flight, one chatty run holds a dead run's timer open indefinitely — the "idle" window silently means "the whole session went quiet", which is not what any caller intends (MJ #4222).

    MUST FAIL OPEN: return true for anything not positively identifiable as another operation's. Wrongly ignoring a message that IS ours shortens the window and causes a spurious reconcile; wrongly accepting one that is not merely preserves the old behaviour. When omitted, every message counts as activity, exactly as before.

    maxStallReconciles?: number

    Max consecutive 'continue' reconciliations before giving up (default: 6)

    mutation: string

    The GraphQL mutation document (gql tagged template)

    mutationFieldName: string

    The mutation field name to extract the acknowledgement from the result e.g., 'RunTest', 'RunAIAgent', 'RunAIAgentFromConversationDetail'

    onMessage?: (parsed: Record<string, unknown>) => void

    Optional handler for all PubSub messages (progress, streaming, etc.). Called for every message, not just completion events. Use this to forward progress updates to UI callbacks.

    onStall?: () => Promise<StallDecision<TResult>>

    Optional reconciliation hook invoked when the idle timer expires (no message received within the idle window) or the PubSub stream drops. Should query the authoritative server-side run record and return a StallDecision. When omitted, an idle expiry rejects with a timeout message (legacy behavior).

    operationLabel?: string

    Label for logging (e.g., 'RunTest', 'RunAIAgent')

    timeoutErrorMessage?: string

    Custom timeout error message

    timeoutMs?: number

    Idle timeout in milliseconds — resets on activity (default: 12 minutes)

    validateAck: (ackResult: Record<string, unknown>) => boolean

    Check if the server accepted the fire-and-forget request. The ack result comes from result[mutationFieldName]. Return true if accepted, false if rejected.

    variables: Record<string, unknown>

    Variables for the mutation (should include fireAndForget: true)