WikifitaGitHub live67e8de5
pesquisa · openai-research/chat-submode-protocol

Modo ChatGPT, Submodo Chat — Protocolo Real (traces da sessão)

The chat submode protocol reconstructed from 40 captured traces — typing throttle, attestation, UI efforts, multi-turn

Baixar raw

Modo ChatGPT, Submodo Chat — Protocolo Real

Part of openai-research. Source: 40 real traces captured via bezetacil v4 in the main process (net-fetch-hook) during the test session — not static RE.

Nomenclatura UI → payload

Seletor na UImodel no payloadthinking_effort no payload
Instantgpt-5-5-instantausente
Mediumgpt-5-5-thinkingstandard
Highgpt-5-5-thinkingextended
Model 5.6 Sol / 5.5 (Model menu)/models web slugsper selection

The chat submode UI efforts are own: standard (UI Medium) e extended (UI High) — different from the codex scale (low/medium/high/xhigh). The "Instant" is a separate slug (gpt-5-5-instant), without thinking_effort in the payload.

O ciclo por mensagem (ordem real capturada)

1. User TYPES → POST /f/conversation/prepare (debounced por tecla)
   body: {action: next, client_prepare_state: sent, partial_query: {…partial text…},
          model, thinking_effort, conversation_id?}
   ← {conduit_token}                    — typing throttle: pre-caches the conversation
   (partial_query carries the PARTIAL TEXT — prepare runs on every keystroke)

2. User SENDS → GET /backend-api/ios/attestation_challenge
   headers: + x-sentinel-dc: {DeviceCheck token}
   ← {attestation_challenge: gAAAAAB…}

3. POST /f/conversation
   body: {action: next, client_prepare_state: "success",
          messages: [t6r completo — a mensagem do usuário],
          app_attest_challenge: gAAAAAB…,
          supported_encodings: [v1],
          model, thinking_effort, conversation_id?, parent_message_id?,
          local_function_signatures: [handoff]}
   ← SSE: delta protocol v1 (ver abaixo; see [flow-a-manual-step-by-step](flow-a-manual-step-by-step.md) for the full literal request)

4. Multi-turn: conversation_id + parent_message_id (última assistant message)

Diferenças do que a RE estática reconstruiu (correções impostas pelos traces)

CampoRE estática diziaTrace real
user message fieldpartial_querymessages (full t6r) — partial_query is only the typing throttle
client_prepare_state'sent''success' no conversation (verified 2026-08-26/27 on real payloads); 'sent' only in the prepare
app_attest_challengeabsentmandatory on conversation (attestation, not sentinel)
supported_encodingsnão existia['v1']
thinking_effortlow/medium/highstandard/extended (UI Medium/High)
"Instant"effortslug separado (gpt-5-5-instant), sem effort
sentinel/PoWnecessárioAUSENTE no submodo chat do desktop — o mecanismo é attestation
local_function_signaturessó no submodo workPRESENTE também no submodo chat (a tool handoff é oferecida sempre no modo ChatGPT)

O prepare como throttle (os 40 traces)

The prepare fires on every keystroke (debounced): partial_query carries the growing partial text ('qual a cor do elefante''…azul ou rosa? re''…responda em poucas palavras'). The server pre-processes the conversation while the user types — that is why long web sessions freeze: continuous PoW + prepare (the insight that preceded the PoW discovery).

Prepares sem partial_query (só conversation_id) = prepare de background ao abrir a conversa.

local_function_signatures — the client-declared tool mechanism

Beyond the server-side tool registry (web.run — executed server-side, results interleaved as role: tool), the chat submode body carries local_function_signatures — functions declared by the client in the request, callable by the model, executed by the client, with the result reinjected as role: 'tool' (the observed handoff loop). The handoff tool is the live example. For a standalone harness this is the extension point to add own tools to the chat submode without touching OpenAI's server: declare the signature, execute in the harness, inject the result back.

The handoff trigger card ("Continue in work mode" + countdown vs "Stay") is renderer-local UX — no dedicated request observed in the captured traffic (the task poll/creation happens through other channels); the pending-task state drives the card.

O handoff → task → role=tool loop (trace [37])

A conversa do chat chamou handoff → task criada no Work → o resultado voltou como role: 'tool' no próximo prepare/conversation, com client_prepare_state: 'success'. O loop chat→task→resultado dentro da mesma conversa web, confirmado nos traces.

6. TypeScript types (as built by the renderer builder)

The renderer builder (asar) emits (the fields below are literal in the builder):

// ── enums ────────────────────────────────────────────────────────────────
export type ConversationOrigin = 'tpp' | 'non-tpp' | string   // 'tpp' = Work tasks (sidebar filter)
export type ConversationAction2 = 'next'                      // only 'next' in the builder

// ── the conversation body (renderer builder) ─────────────────────────────
export interface RendererConversationBody {
  action: ConversationAction2
  attachment_mime_types?: string[]                // present when attachments exist; else undefined
  client_prepare_state: 'sent' | 'success' | 'failure'
  conversation_id?: string                        // undefined for a brand-new conversation
  continue_from_shared_conversation_id?: string   // shared-conversation path; undefined otherwise
  conversation_origin?: ConversationOrigin
  locale?: string
  local_function_signatures?: import('./flow-a-tool-protocol').LocalFunctionSignature[]
  message_id?: string
  messages: import('./flow-a-tool-protocol').ConversationMessage[]
  model: string
  parent_message_id?: string
  service_tier?: string                           // 'default' observed
  supported_encodings: ['v1']
  system_hints?: unknown[]                        // omitted when empty
  thinking_effort?: string                        // 'standard' | 'extended' (chat scale); undefined for Instant
  timezone: string                                // Intl.DateTimeFormat().resolvedOptions().timeZone
  timezone_offset_min: number
  is_client_thread?: boolean                      // only when the thread has no server conversation
}

Cross-references

No x-codex-turn-state in the chat submode

Observed: the x-codex-turn-state header appears zero times in the chat submode flow (verified against the net-fetch-hook captured traffic). The sticky routing turn-state is a mechanism of the codex backend (work submode + Codex mode), not the chat submode — the web flow keeps turn state in the session/topic via the resume_conversation_token instead.