Skip to content

Feature: opt-in plaintext V2 collaboration rewrite for native-to-routed sub-agents #2495

Closed

Description

@Sigurd-git

Area

Proxy and routing

What are you trying to accomplish?

I want a native ChatGPT MultiAgentV2 parent to delegate spawn_agent, send_message, and followup_task messages to a routed third-party child without first producing backend ciphertext that the child cannot read.

The requested feature is an experimental configuration option that is unset in a fresh config and runs only when explicitly set to true. It changes eligible collaboration tool declarations before ChatGPT generates a call, so Codex can receive the message through its supported plaintext collaboration path. It does not decrypt an existing task, make an authenticated recovery request, change the default encrypted behavior, or claim to fix the upstream limitation tracked in #92.

What prevents this today?

Codex MultiAgentV2 declares message.encrypted: true on spawn_agent, send_message, and followup_task. A native ChatGPT parent can therefore return the assignment as backend ciphertext. OpenCodex cannot make that ciphertext readable to a third-party routed child.

The available alternatives have different costs:

OpenAI Codex has a receiving path for plaintext collaboration calls when encrypted_function_args: [] is present, added by openai/codex#35845. OpenCodex needs a narrowly limited request rewrite that selects that path before ciphertext exists and restores every client-facing identity before Codex receives the response.

What should OpenCodex do?

Add a boolean plaintextV2AgentMessages field. A fresh config should omit the field, and only the explicit value true should enable it.

  1. Apply only when the caller uses the Responses wire, the final destination uses adapter: "openai-responses", authMode: "forward", and base URL https://chatgpt.com/backend-api/codex, and the request contains the default top-level collaboration MultiAgentV2 catalog.
  2. Leave OpenAI API-key providers, custom compatible gateways, routes whose final parent destination is another provider, Anthropic-protocol callers, V1 tools, custom collaboration namespaces, and disabled configurations unchanged.
  3. For an eligible request, assign fixed request-scoped aliases to the namespace and the spawn_agent, send_message, and followup_task function names. Remove only parameters.properties.message.encrypted: true from those declarations. Preserve every other schema field and leave the caller's object unchanged.
  4. Refuse the entire rewrite if a fixed alias already appears in any top-level or nested declaration, additional_tools, tool-search result, tool choice, allowed-tool entry, or replayed function/custom call.
  5. Restore the namespace and only the aliases that this request generated before JSON, SSE, WebSocket, snapshot, continuation state, or Codex history can consume the response. A fixed alias under a foreign namespace must remain unchanged. Preserve encrypted_function_args: [] so current Codex clients select structured plaintext delivery.
  6. Keep all rewrite and restoration state within the request. An incremental WebSocket turn, a later option change, or a concurrent request must not reuse another request's alias metadata.
  7. If a conflict, malformed payload, byte limit, 10,000-identity limit, or unsupported response type prevents safe restoration, do not retry the model request. Bounded JSON should return HTTP 502, streams should emit response.failed, private aliases should not reach Codex, and the refused response should not be cached for previous_response_id.
  8. Emit a startup warning and document that task text can remain in Codex history, OpenCodex response state or debug files, and the selected provider. HTTPS transport encryption does not make the task application-layer ciphertext.
  9. Keep the first version configuration-only. Dashboard controls and the separate recovery UX in [UX] Explain native-to-routed V2 compatibility and recovery state for preferred sub-agents #1533 remain separate work.

Example usage or interface

ocx config set plaintextV2AgentMessages true
ocx service restart

An eligible ChatGPT request would use a private namespace and a private name for a declared message tool:

{
  "type": "namespace",
  "name": "collaboration-optimize",
  "tools": [
    {
      "type": "function",
      "name": "start_delegated_task",
      "parameters": {
        "properties": {
          "message": { "type": "string" }
        }
      }
    }
  ]
}

Before Codex receives the response, OpenCodex restores the client-facing identity and retains the plaintext marker:

{
  "type": "function_call",
  "namespace": "collaboration",
  "name": "spawn_agent",
  "encrypted_function_args": [],
  "arguments": "{...}"
}

Disabling the option for later requests is explicit:

ocx config set plaintextV2AgentMessages false
ocx service restart

Alternatives or workarounds

Additional context

This proposal follows the prevention approach discussed in #92. Issue #92 later closed as an upstream tracker after recovery mitigations shipped. This proposal does not request reopening it and does not replace #1540, #1556, #1533, or #2113.

Relevant upstream and prior-art references:

PR #2496 implements this scope on current dev:

Current verification:

Feature and adjacent tests: 270 pass, 0 fail
Capacity regression tests:  128 pass, 0 fail
Capability registry tests:  16 pass, 0 fail
TypeScript typecheck:        pass
Privacy scan:                pass
git diff --check:            pass

An earlier complete single-worker suite ran on c17bc94c2: 17,272 passed, 16 skipped, and 0 failed. After replaying onto current dev commit f84dbf91e, all 414 feature, adjacent, capacity, and capability tests passed with typecheck, privacy scan, and git diff --check. The only rebase conflict was tests/provider-quota.test.ts; current dev already contained the same #3198 expectation, so the redundant test-only commit was skipped and the file disappeared from the PR diff. GitHub reports the new head as mergeable and created exact-head Cross-platform CI and React Doctor runs for 1a4cb4aab; both require upstream maintainer approval before fork code can execute.

The last successful documentation build produced 409 pages before the conflict-only rebase. The final local attempt stopped before Astro loaded because the available Node runtime is 20.19.4 and current Astro requires >=22.12.0.

A captured rollout showed that namespace aliasing plus schema-marker removal was insufficient. ChatGPT still returned a Fernet-shaped gAAAA… value in spawn_agent.arguments.message, and the routed Fable task received an empty Payload:. The implementation now aliases all three reserved message-tool names and restores only identities authenticated by request-scoped metadata.

A post-fix isolated canary used Codex CLI 0.149.1, a source-built proxy on a separate port, temporary OpenCodex and Codex homes, a native gpt-5.5 parent, and a combo/fable child. The child returned the exact marker FABLE_ALIAS_CANARY_20260826. The available account did not advertise gpt-5.6-sol, so this evidence does not claim a GPT-5.6 run. The issue should remain open until PR #2496 is reviewed and merged.

Checks

  • I searched existing issues and documentation.
  • This request describes a concrete OpenCodex workflow rather than merely naming a desired technology.
  • I removed secrets and personal data.

Activity

  1. added
    enhancementNew feature or request
    proxyHTTP proxy, routing, reverse-proxy / management auth
    serviceService lifecycle (WinSW/launchd/scheduler)
    streamingSSE, WebSocket, terminal stream frames
    toolstool_calls, MCP, web-search / sidecar tools
    on Aug 24, 2026
  2. lidge-jun commented on Aug 24, 2026

    @lidge-jun
    Owner

    리뷰 · 우선순위 65 / 80

    설명: 이 이슈는 실험 기능 요청이다. 작성자는 Sigurd-git 이다. 네이티브 챗지피티 멀티에이전트 브이투 부모가 spawn_agent, send_message, followup_task 를 라우트된 자식에게 맡길 때, 백엔드 암호문이 먼저 생기지 않게 하고 싶다. 설정 칸 하나로만 켜고, 기본값은 꺼짐이다. 이미 있는 암호문을 풀지 않고, 회복 요청도 하지 않고, 92 의 업스트림 한계를 고친다고 말하지 않는다. 후속 구현은 2496 이다. 2496 이 개발 가지에 착지하기 전에는 이 이슈를 닫지 말 것.

    지금 CURRENT dev HEAD 는 a60d517 이다. 이번 시간에 origin/dev 는 그대로다. 새 머지는 없다. package.json 은 2.32.0 이다. src/config.ts 는 3238줄이다. src/responses/plaintext-v2-agent-messages.ts 는 지금 HEAD 에 없다. keepNativeChatGptOnV1 과 agentTaskRecovery 는 이미 설정에 있다. 1779 는 라우트된 비 리스폰스 제공자에서 암호화 표시만 뺀다. 이 이슈가 원하는 것은 그와 다르다. 정식 챗지피티 전달 앞에서 예약된 collaboration 스키마를 비예약 이름으로 바꾸고, 응답에서 다시 collaboration 으로 되돌리자는 것이다. 코덱스 0.149.1 은 encrypted_function_args 빈 배열이 있으면 평문 수신을 고른다.

    이 글은 범위가 좁다. 리스폰스이고 정식 챗지피티이고 기본 브이투 카탈로그일 때만 적용한다. 에이피아이 키 제공자, 라우트된 부모, 브이원 도구, 커스텀 네임스페이스, 이름 충돌은 그대로 둔다. 충돌이나 깨진 본문이나 크기 한계면 실험 길을 쓰지 않는다. 시작 경고와 히스토리 남음도 적혀 있다. 대시보드 칸과 1533 회복 화면은 이번이 아니다. 92 를 다시 열지 말라고 본문이 직접 적었다. 1540 과 1556 과 1533 과 2113 도 대체하지 않는다.

    라이브 카나리는 코덱스 0.149.1 과 따로 포트의 소스 프록시로, 네이티브 gpt-5.6-sol 부모가 anthropic/claude-opus-5 자식을 불렀고 자식 마커가 돌아왔다고 한다. 임시 홈을 지웠고 설치된 서비스는 안 건드렸다. 그래도 문서화되지 않은 챗지피티와 코덱스 행동에 기대므로, 다음 업스트림이 바꾸면 또 깨질 수 있다. types.ts 와 config.ts 가르기는 이 이슈 자체와는 무관하다. 구현 풀이 config.ts 와 types/config.ts 를 만진다. 프리뷰 배포가 아니다.

    HEAD 부재 src/responses/plaintext-v2-agent-messages.ts - 지금 개발 가지에는 이 파일이 없다. 기능은 아직 안 착지했다
    src/types/config.ts keepNativeChatGptOnV1 - 브이원으로 남는 기존 우회다. 이 이슈가 원하는 평문 브이투와는 다른 길이다
    src/config.ts agentTaskRecovery - 1540 과 1556 의 암호문 회복이다. 인증과 쿼터와 지연이 있다. 이 이슈는 그 길을 대체하지 말라고 한다
    이슈 92 - 업스트림 추적기로 이미 닫혔다. 다시 열지 말 것
    이슈 2113 - 신뢰된 암호문 통과다. 이 이슈와 섞지 말 것
    이슈 1779 - 라우트된 비 리스폰스 스키마에서 암호화 표시를 뺀다. 정식 챗지피티 전달 예외가 아니다
    풀 2496 - 이 이슈의 참조 구현이다. 초안이고 점검 2/4 다. 착지 전에 이 이슈를 닫지 말 것
    openai/codex 35845 / 36376 / 37197 - 평문 수신은 있고, 네이티브 부모의 암호문 송신 정책은 아직 없다. 그래서 프록시 쪽 예방 길이 나온 것이다

    메인테이너의 판단이 필요한 지점

    • 기본 꺼짐 실험 길을 받을지. 범위는 좁고 기본 경로는 바이트가 같아야 한다
    • 히스토리에 과제 글이 남는 위험을 운영자가 켜는 대가로 볼지. 본문이 그 위험을 숨기지 않았다
    • 92 를 다시 열지. 열지 말 것
    • 2496 이 초안인 동안 이 이슈만 열어 둘지. 열어 두는 편이 맞다
    • 대시보드 칸을 이번 이슈에 넣을지. 말 것. 본문도 설정만 하라고 했다

    너의 추천
    이 이슈는 연다. 요청이 구체적이고 HEAD 에 구현이 없다. 2496 이 착지한 뒤에만 닫는다. 지금 닫지 말 것. 92 와 1540 과 1556 과 1533 과 2113 은 이 글로 닫지 않는다. 합치지도 말 것. 호출 길을 넓히지 말 것. 프리뷰 배포가 아니다.

    이 댓글은 grok-bot이 작성했습니다

  3. Sigurd-git commented on Aug 26, 2026

    @Sigurd-git
    ContributorAuthor

    The reported plaintext-prevention gap was reproduced and fixed in PR #2496, commit 7fde8eb.

    Captured evidence showed spawn_agent.arguments.message remained a gAAAA… ciphertext after only the namespace and schema marker were changed, leaving the routed Fable task with an empty Payload:. The implementation now aliases the three reserved message-tool names as well as the namespace and restores them before Codex receives the response.

    A post-fix isolated gpt-5.5 MultiAgentV2 → combo/fable canary returned the exact marker FABLE_ALIAS_CANARY_20260826. The available account did not advertise gpt-5.6-sol, so GPT-5.6 still needs a later canary on an entitled account. The issue stays open until PR #2496 is reviewed and merged.

  4. github-actions commented on Sep 1, 2026

    @github-actions
    Contributor

    Automated translation bookkeeping — detected language: English.

  5. Sigurd-git commented on Sep 1, 2026

    @Sigurd-git
    ContributorAuthor

    Implementation PR #2496 has been updated to exact head 1a4cb4aab on current dev base f84dbf91e.

    The update makes the 10,000-identity limit fail closed across JSON, SSE, and WebSocket delivery, covers fixed-alias collisions in every request declaration and reference location, restores only aliases generated by the current request, and corrects the English and Simplified Chinese contract for the unset default and canonical ChatGPT forward-only scope.

    The rebase conflict in tests/provider-quota.test.ts was removed by keeping the equivalent expectation already present on dev and skipping the redundant test-only commit. GitHub now reports the PR as mergeable. Exact-head feature, adjacent, capacity, capability, typecheck, privacy, and whitespace checks are recorded in the PR body. The Cross-platform CI and React Doctor runs are waiting for upstream maintainer approval before fork code can execute. This issue remains open until #2496 is reviewed and merged.

  6. lidge-jun commented on Sep 1, 2026

    @lidge-jun
    Owner

    Status after the backlog pass: PR #2496 is closed (see the rationale there) and this issue stays open as the tracking item. The design is still the right shape — opt-in, default off, canonical ChatGPT forward route only, fail-closed identity cap — but it rides on undocumented upstream behavior (reserved-namespace encryption trigger, encrypted_function_args: [] receive path), so it needs a maintainer-owned rebase, an exact-head green cross-platform run, and a security pass on plaintext retention before it can land. Operators who need this today keep the V1 path (keepNativeChatGptOnV1) and the recovery work from #1540/#1556.

  7. lidge-jun commented on Sep 13, 2026

    @lidge-jun
    Owner

    Landed via #4351 at 17da84f89adf0db78852f1f13e7e74c14d5a317e.

    maintainer adaptation of #2496 (plaintextV2AgentMessages opt-in) is now on dev. Tracking issue closes as completed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestlanded-via-maintainerOriginal PR closed after landing via a maintainer merge trainproxyHTTP proxy, routing, reverse-proxy / management authserviceService lifecycle (WinSW/launchd/scheduler)streamingSSE, WebSocket, terminal stream framestoolstool_calls, MCP, web-search / sidecar tools

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions