Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Goal:
Replace the current `codex` provider's Chat Completions wire path with the official Codex Responses HTTP SSE path for ChatGPT-authenticated Codex traffic.

Scope:
- Add a Codex-specific Responses provider that posts to `/responses`.
- Convert existing agent messages/tools into Responses API request payloads.
- Parse Responses SSE events into `TextEvent`, `ThinkingEvent`, `ToolCallEvent`, `UsageEvent`, and `DoneEvent`.
- Wire `provider="codex"` to the new provider and update focused tests.

Out of scope:
- Responses WebSocket transport and warmup/reuse.
- Browser-cookie reuse beyond the existing ChatGPT bearer token flow.
- Changes to non-Codex OpenAI-compatible providers.

Context:
- ADRs:
- `docs/adr/0050-codex-oauth-provider.md`
- `docs/adr/0057-codex-provider-responses-wire-protocol.md`
- Relevant files:
- `src/coding_agent/providers/codex_responses.py`
- `src/coding_agent/plugins/llm_provider.py`
- `tests/providers/test_codex_responses.py`
- `tests/coding_agent/plugins/test_llm_provider.py`

Target tests:
- `uv run pytest tests/providers/test_codex_responses.py tests/coding_agent/plugins/test_llm_provider.py -k "codex" -v`
- `uv run ruff check src/coding_agent/providers/codex_responses.py src/coding_agent/plugins/llm_provider.py tests/providers/test_codex_responses.py tests/coding_agent/plugins/test_llm_provider.py`

Loop policy:
- Engineer implements the smallest correct change and runs the target tests.
- Reviewer reviews only the resulting diff and affected tests.
- Reviewer reports only P1/P2 findings.
- Engineer fixes only accepted P1/P2 findings and reruns the same target tests.
- Verifier reruns the exact target tests and reports pass/fail only.

Stop conditions:
- At most one review/fix/retest cycle.
- Escalate architectural redirection or scope expansion to the human.
- Ignore non-blocking optimization suggestions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
Goal:
Separate the local interactive CLI runtime from the HTTP/control-plane session
manager while preserving current REPL behavior.

Scope:
- Identify the smallest behavior-preserving boundary between
`coding_agent.cli.repl.InteractiveSession` and server session management.
- Introduce or prepare a local CLI runtime/session abstraction for pipeline,
adapter, tape id, approval memory, model switching, and checkpoint restore.
- Remove direct local REPL dependence on remote/control-plane-only concepts from
`server.SessionManager` where practical in the first slice.

Out of scope:
- Renaming HTTP endpoints or remote CLI commands.
- Moving executor, Bee, Docker, or workspace provider modules.
- Changing checkpoint restore semantics or tape storage formats.

Context:
- ADRs:
- `docs/adr/0048-application-structure-refactor-boundaries.md`
- `docs/adr/0054-executor-runtime-terminology-and-resume-first-direction.md`
- `docs/adr/0055-session-resume-and-interrupted-run-semantics.md`
- `docs/adr/0056-local-cli-control-plane-and-workspace-product-boundaries.md`
- Relevant files:
- `src/coding_agent/cli/repl.py`
- `src/coding_agent/cli/commands.py`
- `src/coding_agent/app.py`
- `src/coding_agent/adapter.py`
- `src/coding_agent/server/session_manager.py`
- `tests/cli/test_repl.py`
- `tests/cli/test_commands.py`

Target tests:
- `uv run pytest tests/cli/test_repl.py tests/cli/test_commands.py -k "managed_session or model or checkpoint or process_message" -v`
- `uv run pytest tests/ui/test_session_manager_public_api.py tests/ui/test_session_manager_runtime.py -k "file_session_store or checkpoint_restore or runtime" -v`

Loop policy:
- Engineer implements the smallest correct change and runs the target tests.
- Reviewer reviews only the resulting diff and affected tests.
- Reviewer reports only P1/P2 findings.
- Engineer fixes only accepted P1/P2 findings and reruns the same target tests.
- Verifier reruns the exact target tests and reports pass/fail only.

Stop conditions:
- At most one review/fix/retest cycle.
- Escalate if local CLI behavior requires changing HTTP server semantics.
- Escalate if the slice would require migrating persisted session data.
- Ignore non-blocking optimization suggestions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
Goal:
Clarify Workspace as the execution boundary across local repositories, Docker
sandbox workspaces, future OS-native sandboxes, cloud workspaces, and external
executor workspace references.

Scope:
- Document and enforce product terminology around `ExecutionBinding`,
`Environment`, and `WorkspaceProvider`.
- Keep session metadata responsible for workspace binding, not for filesystem or
shell implementation details.
- Prepare executor-related code to sit behind an execution-plane boundary while
preserving existing compatibility paths.

Out of scope:
- Implementing OS-native sandbox support.
- Rewriting Docker provider behavior.
- Renaming public remote HTTP protocol fields in this slice.
- Changing action-safety approval policy.

Context:
- ADRs:
- `docs/adr/0038-workspace-provider-and-sandbox-mvp-boundaries.md`
- `docs/adr/0051-external-worker-execution-control-plane.md`
- `docs/adr/0054-executor-runtime-terminology-and-resume-first-direction.md`
- `docs/adr/0056-local-cli-control-plane-and-workspace-product-boundaries.md`
- Relevant files:
- `src/coding_agent/server/execution_binding.py`
- `src/coding_agent/environment/local.py`
- `src/coding_agent/environment/cloud.py`
- `src/coding_agent/environment/workspace_provider.py`
- `src/coding_agent/environment/docker_workspace_provider.py`
- `src/coding_agent/server/session_manager.py`
- `src/coding_agent/external_executor.py`
- `tests/ui/test_execution_binding.py`
- `tests/coding_agent/environment/`

Target tests:
- `uv run pytest tests/ui/test_execution_binding.py -v`
- `uv run pytest tests/coding_agent/environment/test_local_environment.py tests/coding_agent/environment/test_cloud_environment.py -v`
- `uv run pytest tests/coding_agent/environment/test_docker_workspace_provider.py -k "binding or unavailable or provider_instance" -v`
- `uv run pytest tests/coding_agent/test_external_executor.py -k "capability or authorized or workspace" -v`

Loop policy:
- Engineer implements the smallest correct change and runs the target tests.
- Reviewer reviews only the resulting diff and affected tests.
- Reviewer reports only P1/P2 findings.
- Engineer fixes only accepted P1/P2 findings and reruns the same target tests.
- Verifier reruns the exact target tests and reports pass/fail only.

Stop conditions:
- At most one review/fix/retest cycle.
- Escalate if the slice would require a protocol migration.
- Escalate if Docker or hosted infrastructure becomes required for deterministic tests.
- Ignore non-blocking optimization suggestions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# ADR-0056: Local CLI, Control Plane, and Workspace Product Boundaries

**Status**: Proposed
**Date**: 2026-06-01

## Context

`coding_agent` now contains three product shapes that grew from the same
runtime: a local interactive CLI, a remote HTTP control plane, and an executor
plane for sandboxed or attached execution. The current implementation shares
large session/runtime objects across those shapes. For example, the local REPL
uses the server `SessionManager`, and the server session model stores both
durable metadata and in-process runtime handles.

This makes the product meaning of `local`, `remote`, `session`, `workspace`,
and `executor` unclear. A local CLI session should behave like a Codex or Claude
Code session: one user, one local workspace, an interactive REPL, local approval
prompts, tape persistence, model switching, and checkpoint restore. The remote
server should be a control plane for sessions, runs, workspaces, approvals, and
events. Executors should be an execution plane that consumes authorized plans
and reports status and results.

The existing runtime direction remains valid: AgentKit owns the generic
Pipeline, Hook Runtime, Tape, Plugin, Tool, Provider, Directive, Approval, and
Checkpoint mechanisms. Coding Agent owns product policy and workspace semantics.

## Decision

Separate product meanings before moving large amounts of code.

The local CLI product must depend on a local runtime/session abstraction rather
than on the HTTP control-plane `SessionManager`. That abstraction may reuse
`app.create_agent`, `PipelineAdapter`, tape stores, checkpoint stores, and
approval components, but it should not expose remote session, workspace
inventory, owner lease, executor claim, or HTTP event-queue concerns to the REPL.

The server/control-plane session service remains responsible for HTTP sessions,
runs, workspace lifecycle, approval interactions, event streaming, resume
metadata, and executor placement. It should evolve toward a split between
durable records and in-process runtime handles.

Workspace is the execution boundary for file, patch, shell, archive, diff,
publish, cleanup, and validation operations. Sessions should record their
workspace through `ExecutionBinding`/workspace metadata. Local repositories,
Docker sandbox workspaces, future OS-native sandboxes, cloud workspaces, and
external executor workspace references should be represented as explicit
workspace bindings or provider-backed environments instead of being inferred
from unrelated session fields.

Dogfood this refactor in two bounded tasks:

1. Local CLI runtime boundary: introduce or prepare a local CLI runtime/session
abstraction and remove the local REPL's direct dependency on the server
`SessionManager` in the smallest behavior-preserving slice.
2. Workspace/sandbox/control-plane boundary: clarify workspace binding/provider
terminology and prepare executor-related code to live behind an execution
plane boundary without changing existing remote protocols.

## Alternatives Rejected

- Keep sharing `server.SessionManager` with local CLI — rejected because it
makes the simplest local product path depend on remote/control-plane and
executor concepts.
- Move all session, workspace, remote, and executor files in one refactor —
rejected because it would create high-risk import churn before the product
terms are stable.
- Put workspace provider semantics into AgentKit — rejected because repository
paths, Docker, cloud clients, sandbox policy, and workspace lifecycle are
Coding Agent product concerns.
- Treat Docker sandbox as the only workspace abstraction — rejected because the
intended model also includes local repositories, future OS-native sandboxing,
cloud workspaces, and external executor workspace references.

## Acceptance Criteria

- [ ] Local CLI task packet exists and names target tests for the REPL/session boundary.
- [ ] Workspace/sandbox task packet exists and names target tests for workspace binding/provider semantics.
- [ ] `test_repl_does_not_import_server_session_manager_for_local_runtime`
- [ ] `test_local_cli_runtime_preserves_model_switch_and_checkpoint_restore`
- [ ] `test_workspace_binding_product_terms_round_trip`
- [ ] `uv run pytest tests/cli/test_repl.py tests/cli/test_commands.py -k "managed_session or model or checkpoint or local_runtime" -v`
- [ ] `uv run pytest tests/ui/test_execution_binding.py tests/coding_agent/environment/test_local_environment.py tests/coding_agent/environment/test_docker_workspace_provider.py -k "binding or workspace_provider or unavailable" -v`

## References

- `docs/adr/0038-workspace-provider-and-sandbox-mvp-boundaries.md`
- `docs/adr/0048-application-structure-refactor-boundaries.md`
- `docs/adr/0054-executor-runtime-terminology-and-resume-first-direction.md`
- `docs/adr/0055-session-resume-and-interrupted-run-semantics.md`
- `src/coding_agent/cli/repl.py`
- `src/coding_agent/server/session_manager.py`
- `src/coding_agent/server/execution_binding.py`
- `src/coding_agent/environment/`
- `src/coding_agent/external_executor.py`
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# ADR-0057: Codex Provider Uses Responses Wire Protocol

**Status**: Proposed
**Date**: 2026-06-02

## Context

The current `codex` provider reuses the OpenAI-compatible Chat Completions provider and sends requests to `https://chatgpt.com/backend-api/codex/chat/completions`. The official Codex CLI does not use that path for ChatGPT-authenticated Codex traffic.

OpenAI Codex CLI uses the Responses wire API against `https://chatgpt.com/backend-api/codex/responses`, with ChatGPT bearer auth plus `ChatGPT-Account-ID`, Codex identity headers, and SSE/WebSocket streaming semantics. Treating the ChatGPT Codex backend as Chat Completions causes Cloudflare/browser-gate failures and protocol-level mismatches.

## Decision

Replace the `codex` provider implementation with a Codex-specific Responses provider. The first implementation will support HTTP SSE against `/responses`; WebSocket prewarm/reuse remains a later enhancement.

The `openai` and `openai_compat` providers continue to use Chat Completions through `OpenAICompatProvider`. Only `provider="codex"` switches to the Codex Responses provider.

## Alternatives Rejected

- Keep `codex` on `OpenAICompatProvider` — rejected because the official Codex CLI no longer uses Chat Completions for Codex traffic.
- Add a new parallel `codex-responses` provider — rejected because the existing `codex` provider name already means ChatGPT-authenticated Codex and should have correct semantics.
- Implement WebSocket first — rejected to keep the first repair narrow; the official CLI also has HTTP Responses fallback behavior.

## Acceptance Criteria

- [ ] `test_codex_provider_uses_responses_provider`
- [ ] `test_stream_posts_to_codex_responses_with_chatgpt_headers`
- [ ] `test_stream_converts_function_call_output_item_done`
- [ ] `uv run pytest tests/providers/test_codex_responses.py tests/coding_agent/plugins/test_llm_provider.py -k "codex" -v`

## References

- `src/coding_agent/providers/codex_responses.py`
- `src/coding_agent/plugins/llm_provider.py`
- `tests/providers/test_codex_responses.py`
- `tests/coding_agent/plugins/test_llm_provider.py`
- `https://github.com/openai/codex/blob/main/codex-rs/model-provider-info/src/lib.rs`
- `https://github.com/openai/codex/blob/main/codex-rs/codex-api/src/endpoint/responses.rs`
- `https://github.com/openai/codex/blob/main/codex-rs/model-provider/src/bearer_auth_provider.rs`
55 changes: 55 additions & 0 deletions coding-agent/src/coding_agent/cli/local_runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Local CLI runtime/session boundary.

This module is the CLI-facing seam for local interactive sessions. The first
slice intentionally delegates to the existing server SessionManager so behavior
stays unchanged while the REPL stops depending on control-plane symbols.
"""

from __future__ import annotations

from typing import Protocol, runtime_checkable, Any

from coding_agent.server.session_manager import SessionManager


@runtime_checkable
class LocalCliSessionManager(Protocol):
"""Subset of session behavior used by the interactive local REPL."""

async def create_session(self, **kwargs: Any) -> str: ...

def get_session(self, session_id: str) -> Any: ...

async def get_session_async(self, session_id: str) -> Any: ...

async def ensure_session_runtime(self, session_id: str) -> Any: ...

async def replace_session_runtime_config(
self,
session_id: str,
*,
model_name: str,
) -> Any: ...

async def restore_checkpoint(self, session_id: str, checkpoint_id: str) -> None: ...

async def run_agent(self, session_id: str, prompt: str) -> None: ...


def create_local_cli_session_manager() -> LocalCliSessionManager:
"""Create the local REPL session manager.

The concrete implementation remains the existing managed local session
service for now. Keeping this construction here prevents REPL code from
importing server/control-plane types directly.
"""

return SessionManager(
storage_config={
"http_session_backend": "fs",
"runtime_backend": "jsonl",
}
)


__all__ = ["LocalCliSessionManager", "create_local_cli_session_manager"]
14 changes: 6 additions & 8 deletions coding-agent/src/coding_agent/cli/repl.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@
from coding_agent.approval import ApprovalPolicy
from coding_agent.cli.commands import handle_command
from coding_agent.cli.input_handler import InputHandler, expand_pasted_refs
from coding_agent.cli.local_runtime import (
LocalCliSessionManager,
create_local_cli_session_manager,
)
from coding_agent.cli.terminal_output import (
get_prompt_output,
print_pt,
Expand All @@ -25,7 +29,6 @@
from coding_agent.ui.stream_renderer import StreamingRenderer
from coding_agent.ui.rich_consumer import RichConsumer
from coding_agent.ui.status_footer import StatusFooter
from coding_agent.server.session_manager import SessionManager
from coding_agent.wire.protocol import TurnEnd, WireMessage


Expand Down Expand Up @@ -469,13 +472,8 @@ def _is_root_turn_end(message: WireMessage) -> bool:
return isinstance(message, TurnEnd) and not message.agent_id


def _local_session_manager() -> SessionManager:
return SessionManager(
storage_config={
"http_session_backend": "fs",
"runtime_backend": "jsonl",
}
)
def _local_session_manager() -> LocalCliSessionManager:
return create_local_cli_session_manager()


async def run_repl(config: Config):
Expand Down
Loading