Skip to content

App Server: add persistent thread attachment RPCs for existing top-level sessions #24398

Closed as not planned

Description

@johnastinnett

What variant of Codex are you using?

App Server / Codex Desktop App

What feature would you like to see?

Summary

Please add a sanctioned App Server API for attaching an existing top-level Codex thread to another existing thread as a persistent child or role session.

This is the narrow App Server protocol companion to #23713. The Desktop UX request in #23713 asks for an "Attach as sub-agent to parent thread" action. This issue narrows that request to the open-source App Server surface so clients can create, list, and remove durable thread attachment relationships without editing local SQLite or JSONL state.

This also fits under the broader cross-thread orchestration direction in #14923, but it is intentionally smaller: this issue is only about the relationship primitive that lets an existing top-level thread become addressable from a parent thread while remaining a normal visible top-level session.

Related issues

Problem

Codex currently supports spawned subagents, but spawned subagents are not the same operational shape as existing top-level scoped role sessions.

A top-level thread can already have the right project folder, cwd, AGENTS.md scope, role brief, model settings, visible history, and Desktop or automation affordances. But there is no sanctioned App Server API to attach that existing thread to an orchestrator thread so the parent can message it like a subagent.

The current gap pushes advanced workflows toward unsupported local-state mutation. That works only by relying on internal implementation details. The App Server already owns thread lifecycle primitives, so this relationship should be represented through a first-class protocol method instead of local database edits.

Concrete use case

Consider a user running a durable multi-agent workspace:

  • one top-level Operator or orchestrator thread
  • one top-level Reviewer thread scoped to a reviewer folder and AGENTS.md
  • one top-level Implementor thread scoped to an implementor folder and AGENTS.md
  • one top-level Specwright thread scoped to a spec-authoring folder and AGENTS.md

The user wants the Operator thread to route work to those existing role threads without manually copying prompts between chats.

The important properties are:

  • the role thread remains visible in the normal left sidebar
  • the role thread can still be opened and prompted directly by the human
  • the role thread keeps its original cwd and instruction scope
  • the role thread keeps its accumulated history
  • the parent can address the role thread through a supported orchestration edge
  • detach removes only the relationship, not the child thread

Spawned subagents are useful for disposable delegation, but this is a different shape: persistent role desks attached to a parent orchestrator.

Why spawned subagents are not enough

Spawned subagents are useful for bounded, disposable delegation. Persistent role sessions are different:

  • They already live as top-level visible threads.
  • They can be scoped by folder-level AGENTS.md files rather than duplicated TOML prompts.
  • They preserve long-running role continuity across days or weeks.
  • They can be inspected and prompted directly by the user.
  • They can participate in Desktop automations and scheduled wakeups.
  • They should not be destroyed by disposable-child lifecycle controls.

The TOML custom-agent path also creates a drift surface. If a role is already governed by a folder-scoped AGENTS.md, duplicating that role into a TOML prompt, or telling the TOML prompt to "go read this AGENTS.md," is less reliable than using the existing scoped top-level session directly.

Maintainer decision requested

The main design question is not whether the Desktop UI should eventually expose this. That is covered by #23713.

The narrower App Server questions are:

  1. Should existing top-level threads be attachable to a parent thread through a first-class relationship API?
  2. If yes, should this be modeled as dedicated attachment RPCs rather than raw metadata mutation?
  3. Should v1 enforce one active parent attachment per child thread, with many-parent graphs left out of scope?

My proposed answer is yes to all three, with a conservative v1 focused on create, list, and delete.

Proposed API

Add purpose-built JSON-RPC methods rather than expanding thread/metadata/update into a raw writable metadata bag:

thread/attachment/create
thread/attachment/delete
thread/attachments/list

Suggested create shape:

{
  "parentThreadId": "string",
  "childThreadId": "string",
  "roleLabel": "optional string",
  "displayName": "optional string"
}

Suggested returned relationship:

{
  "attachmentId": "string",
  "parentThreadId": "string",
  "childThreadId": "string",
  "roleLabel": "optional string",
  "displayName": "optional string",
  "state": "active | detached | archived",
  "createdAt": "timestamp",
  "updatedAt": "timestamp"
}

Smallest useful MVP

The smallest useful version does not need Desktop UI work.

MVP:

  • create an attachment relationship between two existing threads
  • list attachment relationships
  • delete or detach an attachment relationship
  • allow the parent runtime to address the attached child thread through the existing child-agent messaging path, or through an equivalent supported App Server turn-routing path
  • preserve both threads as normal top-level threads
  • make detach non-destructive

Everything else can layer later.

Required semantics

  • Creating an attachment must not fork, spawn, archive, close, or rewrite either thread.
  • Creating an attachment must not change either thread's cwd, instruction scope, model settings, transcript, rollout identity, or history.
  • Creating an attachment must reject self-attachment.
  • Creating an attachment must reject cycles.
  • Creating an attachment must reject nonexistent or archived threads.
  • Creating an attachment should reject duplicate active attachments.
  • Default should be one active parent attachment per child unless maintainers explicitly want many-parent semantics.
  • Deleting an attachment detaches only the relationship. It must not archive, close, delete, or mutate either thread.
  • Listing attachments should support lookup by parent thread, child thread, or both.
  • Attachment state should be visible to clients without local state scraping.
  • Cross-thread activity should remain auditable from the parent and child side.

Important lifecycle safety

Persistent attached top-level sessions must be distinguished from disposable spawned children.

Existing spawned-child lifecycle controls such as close_agent are dangerous if blindly applied to persistent attached top-level threads. For an attached thread, "detach" should remove the relationship only. It should not close, archive, delete, or otherwise destroy the child thread.

This is the load-bearing difference between:

  • disposable spawned subagent
  • persistent top-level session attached as a role or child session

Likely implementation surface

Based on the public App Server shape, I expect the implementation would likely touch:

  • app-server protocol method definitions
  • thread relationship persistence
  • request handling for create, list, and delete
  • lifecycle validation for archived, nonexistent, self-attached, duplicate, and cyclic relationships
  • tests for non-destructive detach and invalid relationship graphs

That is only a hypothesis from the public code shape. I am happy to align to a maintainer-preferred design before writing code.

Why App Server is the right surface

The Desktop UI can eventually become sugar over this API, but the relationship itself is not Desktop-only. Other clients, automation tooling, and SDK users need the same primitive.

The App Server already exposes thread lifecycle and turn primitives. A persistent attachment relationship is a lifecycle and relationship concept, not merely display metadata. That is why this should be a dedicated API rather than a loose extension of thread/metadata/update.

This shape also keeps #23713 smaller. The Desktop UI can later offer an "Attach as sub-agent to parent thread" button that calls the sanctioned App Server method rather than inventing its own state mutation path.

Why not raw metadata mutation

Thread attachment is a relationship, not ordinary metadata.

Overloading thread/metadata/update with fields like thread_source, source, agent_role, agent_nickname, or edge-table state would make it too easy for clients to create inconsistent graphs. It would also mix display metadata, lifecycle state, and orchestration authority into one patch surface.

A dedicated attachment API can validate graph invariants atomically:

  • no self-edges
  • no cycles
  • no duplicate active relationship
  • no accidental many-parent relationship unless explicitly supported
  • no destructive lifecycle action during detach
  • no cwd or instruction-scope rewrite

Non-goals

Acceptance criteria

  • App Server exposes thread/attachment/create.
  • App Server exposes thread/attachment/delete.
  • App Server exposes thread/attachments/list.
  • Attachment creation validates self-attachment, cycles, duplicate active attachments, missing threads, and archived threads.
  • Attachment deletion is non-destructive to both parent and child threads.
  • Existing thread lifecycle methods continue to work unchanged.
  • Attached top-level threads remain visible as top-level sessions.
  • Parent-side orchestration can address the attached child thread.
  • The child thread preserves its original cwd and instruction scope.
  • Desktop or external clients can discover attachment state without reading local SQLite or JSONL directly.
  • Tests cover create/list/delete, invalid relationships, archived-thread behavior, and non-destructive detach behavior.

Contribution note

I understand unsolicited PRs are not accepted by default. If maintainers agree this is the right App Server protocol shape, I would be happy to help prototype the focused App Server and protocol change under whatever contribution process you prefer.

Additional information

This is a companion protocol/API request to #23713.

Activity

  1. added
    appIssues related to the Codex desktop app
    app-serverIssues involving app server protocol or interfaces
    subagentIssues involving subagents or multi-agent features
    on May 25, 2026
  2. github-actions commented on May 25, 2026

    @github-actions
    Contributor

    Potential duplicates detected. Please review them and close your issue if it is a duplicate.

    Powered by Codex Action

  3. johnastinnett commented on May 25, 2026

    @johnastinnett
    Author

    Not a duplicate, one is for the UX enhancement scope, the other is for app server shape. Companions, not duplicates.

  4. johnastinnett commented on May 25, 2026

    @johnastinnett
    Author

    I put together a small reference branch that explores the App Server side of this request:

    https://github.com/johnastinnett/codex/tree/codex/thread-attachments-rpc

    It implements the narrow RPC shape described above as a spike:

    • adds v2 App Server protocol methods for attaching, detaching, listing, and reading thread attachments
    • persists attachment metadata in state instead of requiring direct SQLite edits
    • preserves top-level thread visibility while also registering the child as a routable subagent edge
    • adds close-agent refusal protection for attached persistent children
    • includes generated protocol schema / TypeScript artifacts
    • includes focused regression coverage for attach/detach predicates, pagination, archived/missing threads, path collisions, live registry rollback, and recursive descendant collision checks

    Local verification run on the branch:

    • just write-app-server-schema
    • just write-app-server-schema --experimental as a probe, followed by stable schema regeneration
    • just test -p codex-app-server-protocol
    • just test -p codex-state
    • just test -p codex-app-server thread_attachment
    • focused core agent registry / close-refusal tests
    • git diff --check
    • just fmt via Alpine/musl container because the Python SDK formatter dependency currently resolves to musllinux wheels on Linux

    The branch is meant as implementation evidence for the proposed shape, not as a claim that this is the final product/API design.

  5. etraut-openai commented on May 25, 2026

    @etraut-openai
    Contributor

    Thanks for the suggestion, but it's unlikely that we would implement a feature as described in this feature request. This breaks a bunch of conceptual and design assumptions that are intrinsic to the Codex core harness architecture.

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

    appIssues related to the Codex desktop appapp-serverIssues involving app server protocol or interfacesenhancementNew feature or requestsubagentIssues involving subagents or multi-agent features

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions