WikifitaGitHub live67e8de5
pesquisa · openai-research/flow-a-manual-step-by-step

Flow A Manual — Step-by-Step (executable by a fresh agent)

The validated end-to-end sequence of flow A1 (session-keeper) with exact headers, bodies and the live token acquisition — reproducible request by request via the bezetacil harness

Baixar raw

Flow A Manual — Step-by-Step (A1)

Part of openai-research. This is the validated sequence (2026-08-27; multiple successful runs: tool call, client return, multi-turn continuation). Every step is one action; every HTTP request goes through the bezetacil trigger conv (the main process electron.net — the same transport and pool as the app).

0. Environment

  • bezetacil running (http://127.0.0.1:9228), the ChatGPT desktop app logged in (the app-server holds the session).
  • Installation constants (this Mac, 2026-08-27): oai-did = a05f9cdd-ca51-4a91-9da1-9c374dfe0536 (live from the renderer — verified at step 2), ChatGPT-Account-Id = aa06cafc-774a-432f-b104-89d45ad24d81, User-Agent = Codex Desktop/26.820.60940 (Mac OS; arm64), originator = Codex Desktop, OAI-Language = en-US, timezone America/Sao_Paulo, timezone_offset_min = 180.

1. Live access token (getAuthStatus with includeToken)

Write /tmp/standalone-a-rpc.json:

{"method": "getAuthStatus", "params": {"includeToken": true, "refreshToken": false}}

POST http://127.0.0.1:9228/run/rpc → response:

{"id": "<uuid>", "result": {"authMethod": "chatgpt", "authToken": "eyJhbGciOiJSUzI1NiIsImtpZCI6…", "requiresOpenaiAuth": true}}

Use authToken. (The live app-server session token; the static ~/.codex/auth.json may be stale after an app-server refresh — using the stale file produced 403s on 2026-08-27 16:19+ until the live token was used.)

2. Live did

POST http://127.0.0.1:9228/run/did-runtime → (real response observed 2026-08-27 — the targets list varies with the open webContents; the important field is did):

{"targets": [{"id": 3, "url": "https://chatgpt.com/?source=codex-embedded-checkout#pricing"},
              {"id": 2, "url": "app://-/index.html?initialRoute=%2Favatar-overlay"}],
 "did": "a05f9cdd-ca51-4a91-9da1-9c374dfe0536", "error": null}
```.
**Never regenerate** (the did is the persisted installation identity in the renderer localStorage key `codex.chatgpt-conversations.device-id`; a fresh uuid → 403 both at challenge and conversation).

## 3. DeviceCheck token

`POST http://127.0.0.1:9228/run/devicecheck` → `{"ok": true, "result": {"supported": true, "token": "AgAA…", "len": 2980}}` (generated in the MAIN process — the app bundle context — same as the app's own flow).

## 4. Per-turn cycle

Run 4.14.24.3 for **each** turn (user message, tool return, continuation). Write each input to `/tmp/standalone-a-input.json`, then `POST http://127.0.0.1:9228/run/conv`.

### 4.1 attestation challenge

Input:
```json
{"url": "/ios/attestation_challenge", "method": "GET",
 "headers": {
   "OAI-Language": "en-US",
   "oai-did": "a05f9cdd-ca51-4a91-9da1-9c374dfe0536",
   "originator": "Codex Desktop",
   "User-Agent": "Codex Desktop/26.820.60940 (Mac OS; arm64)",
   "Authorization": "Bearer <authToken from step 1>",
   "ChatGPT-Account-Id": "aa06cafc-774a-432f-b104-89d45ad24d81",
   "x-sentinel-dc": "{\"token\": \"<devicecheck token from step 3>\"}"}}

{"ok": true, "result": {"status": 200, "text": "{\"attestation_challenge\": \"gAAAAABqkGYnoU…\", …}"}}. The challenge is one-time — never reuse it for a second request.

4.2 prepare

Input:

{"url": "/f/conversation/prepare", "method": "POST",
 "headers": {
   "OAI-Language": "en-US",
   "oai-did": "a05f9cdd-ca51-4a91-9da1-9c374dfe0536",
   "originator": "Codex Desktop",
   "User-Agent": "Codex Desktop/26.820.60940 (Mac OS; arm64)",
   "Authorization": "Bearer <authToken>",
   "ChatGPT-Account-Id": "aa06cafc-774a-432f-b104-89d45ad24d81",
   "Content-Type": "application/json",
   "x-conduit-token": "no-token"},
 "body": {
   "action": "next",
   "client_prepare_state": "sent",
   "model": "gpt-5-6-thinking",
   "thinking_effort": "extended",
   "timezone": "America/Sao_Paulo",
   "timezone_offset_min": 180}}

{"status": 200, "text": "{\"status\":\"ok\",\"conduit_token\":\"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9…\"}"}.

4.3 conversation

Input (first turn of a new conversation — omit conversation_id and parent_message_id; every following turn includes both):

{"url": "/f/conversation", "method": "POST",
 "headers": {
   "OAI-Language": "en-US",
   "oai-did": "a05f9cdd-ca51-4a91-9da1-9c374dfe0536",
   "originator": "Codex Desktop",
   "User-Agent": "Codex Desktop/26.820.60940 (Mac OS; arm64)",
   "Authorization": "Bearer <authToken>",
   "ChatGPT-Account-Id": "aa06cafc-774a-432f-b104-89d45ad24d81",
   "Content-Type": "application/json"},
 "body": {
   "action": "next",
   "local_function_signatures": [
     {"description": "Redirect the user's request from ChatGPT to Work mode when Work mode is the better execution environment.\n\nYou MUST call this tool before doing any work when the request involves:\n- Browser use or computer-use automation\n- Building apps, local coding, repository edits, command execution, or file inspection\n- Opening, updating, reviewing, or otherwise working with PRs\n- Creating, editing, converting, inspecting or delivering files or artifacts, including implicit requests for downloadable or editable deliverables such as slide decks, `.pptx`, spreadsheets, `.xlsx`, workbooks, documents, `.docx`, or PDFs,\n- Complex analysis such as financial modeling\n\nPrefer answering directly in ChatGPT for:\n- Email, message, or prose drafting\n- Brainstorming, planning, or explanation\n- Code snippets or examples that fit naturally in chat\n\nIf the user rejected the suggestion, don't call this tool again.",
      "name": "handoff",
      "params": [
        {"name": "prompt", "required": true, "type": {"description": "A brief instruction describing what Work mode should do for the user.", "type": "string"}},
        {"name": "reason", "required": true, "type": {"description": "A short user-facing reason why continuing in Work mode would help.", "type": "string"}}],
      "type": "kwargs"}],
   "messages": [
     {"author": {"metadata": {}, "name": null, "role": "user"},
      "channel": null,
      "content": {"content_type": "text", "parts": ["<the user text>"]},
      "create_time": <unix seconds as a float>,
      "end_turn": null,
      "id": "<new uuid>",
      "metadata": {},
      "recipient": "all",
      "status": "finished_successfully",
      "update_time": null,
      "weight": 1}],
   "model": "gpt-5-6-thinking",
   "supported_encodings": ["v1"],
   "thinking_effort": "extended",
   "timezone": "America/Sao_Paulo",
   "timezone_offset_min": 180,
   "client_prepare_state": "success",
   "app_attest_challenge": "<challenge from 4.1>",
   "conversation_id": "<id of the existing conversation — omitted on the first turn>",
   "parent_message_id": "<id of the last message — omitted on the first turn>"}}

200 with the SSE (delta v1: add/append/patch; message_marker events cot_token, user_visible_token, final_channel_token; streams end with message_stream_complete + conversation_detail_metadata + title_generation).

Invariants that are now facts (each verified):

  • No x-conduit-token header on /f/conversation (the app's own winning request has none; the conduit is prepare-only).
  • The first turn of a conversation omits conversation_id/parent_message_id; every following turn includes both (omitting them on a continuation → 403; the app always sends them in follow-up payloads).
  • The local_function_signatures must contain the handoff signature in normal turns (a turn with only a custom tool and no handoff → 403).
  • The whole cycle (challenge, prepare, conversation) stays on the same transport (the main/trigger path).

5. Multi-turn continuation (same conversation)

For the next user message: run 4.1 (fresh challenge), 4.2 (fresh prepare), 4.3 with conversation_id and parent_message_id = the last assistant message.id (from the previous SSE — or from the conversation tree via GET /conversation/<id>), messages = the new user record. Validated: 200 → "De nada.".

6. TypeScript types (the A1 surface)

// ── app-server RPC (via the bezetacil rpc trigger) ───────────────────────
export type AuthMode =
  | 'apiKey'                 // Codex-managed API key
  | 'chatgpt'                // managed ChatGPT OAuth (tokens persisted/refreshed by Codex)
  | 'chatgptAuthTokens'      // EXTERNAL-host-provided tokens, memory-only (this is our A1 state:
                             //   authToken obtained AS-IS from getAuthStatus {includeToken:true})
  | 'amazonBedrock'          // Managed Bedrock
// source: codex-rs/app-server-protocol/src/protocol/common.rs line 24 (enum AuthMode)

export interface GetAuthStatusParams {
  includeToken: boolean      // false = no token returned (the app's own calls use false)
  refreshToken: boolean      // false = never refresh as a side effect
}
export interface GetAuthStatusResponse {
  authMethod: AuthMode
  authToken?: string         // present only when includeToken: true (the LIVE session token)
  requiresOpenaiAuth: boolean
}

// ── triggers (bezetacil) ─────────────────────────────────────────────────
export interface DidRuntimeResult {          // POST :9228/run/did-runtime
  targets: Array<{ id: number; url: string }>
  did: string | null                         // the renderer localStorage value
  error: string | null
}
export interface DeviceCheckTriggerResult {   // POST :9228/run/devicecheck
  supported: boolean
  token: string                               // base64 App-Attest token (2980 chars)
  len: number
}

// ── attestation challenge ────────────────────────────────────────────────
export interface ChallengeHeadersToken {     // x-sentinel-dc value
  token: string
}
export interface AttestationChallengeResponse {
  attestation_challenge: string               // 'gAAAAAB…' — one-time per use
}

// ── prepare ──────────────────────────────────────────────────────────────
export interface PrepareHeaders {
  'OAI-Language': 'en-US'
  'oai-did': string
  originator: 'Codex Desktop'
  'User-Agent': `Codex Desktop/${string} (Mac OS; arm64)`
  Authorization: `Bearer ${string}`
  'ChatGPT-Account-Id': string
  'Content-Type': 'application/json'
  'x-conduit-token': 'no-token'
}
export interface PrepareResponseBody {       // decoded from the trigger text
  status: 'ok'
  conduit_token: string                       // JWT ES256 — see Jet claims below
}
/** The conduit JWT payload (decoded from real tokens). */
export interface ConduitTokenClaims {
  conduit_uuid: string
  conduit_location: string                    // '10.130.x.x:830x' (internal cluster coord)
  cluster: string                             // 'unified-83' / 'unified-84' — the routing pin
  iat: number
  exp: number                                 // iat + 60 (1-minute lifetime)
  turn_topic_id: string | null
}

// ── conversation request headers ─────────────────────────────────────────
export interface ConversationHeaders extends Omit<PrepareHeaders, 'x-conduit-token' | 'Content-Type'> {
  'Content-Type': 'application/json'
  // NO x-conduit-token on /f/conversation (verified)
}

// ── conversation request body / response ─────────────────────────────────
export type ConversationRequestBodyV1 =
  import('./flow-a-tool-protocol').ConversationRequestBody   // the shared type from the tool protocol page
export interface ConversationResponseStream {
  /** SSE text of /f/conversation (see [response-sse-real](response-sse-real.md) for the event types). */
  // events: delta_encoding 'v1'; data-only; delta {c,o,p,v}; message_marker; title_generation;
  //         message_stream_complete; conversation_detail_metadata; server_ste_metadata
}

Cross-references