Ship openagents coder: a terminal coding agent in the CLI #132

Closed AtlantisPleb opened this 3d ago 2 comments

Outcome

npm i -g @openagentsinc/cli then openagents coder gives you a terminal coding agent. It signs in with the account you already have, spends a budgeted inference grant the server mints, edits the checkout you launched it from, and asks before it changes anything. No second binary, no provider key, no second backend.

Current behavior

Every piece exists except one route and one command.

The probe repository ships the agent: a turn state machine, permission as typed data, six confined tools with output caps and path refusal, an ACP v1 server, OpenAI-compatible and Gemini wire lowerings, and a WebAssembly build whose artifact is checked in and whose ABI is synchronous, so a Node host owns every asynchronous concern. Its native host already reads PROBE_INFERENCE_GRANT and PROBE_INFERENCE_URL from its environment. It is already pointed here.

This server ships the authority: POST /api/inference/proxy (lib/openagents_web/router.ex:342) is an OpenAI-compatible chat-completions surface a coding agent calls with an OpenAgents.Inference.Grant as its bearer, metered against the owner, with the provider credential never leaving the server. Three call sites mint grants (lib/openagents/work/scv.ex:133, lib/openagents/work/coding.ex:64, lib/openagents/work/delegation_server.ex:168) and every one is server-initiated.

What is missing is that no route mints a grant for a client holding a user token. probe's own specification names this: the paired-computer controller has "no channel by which a per-delegation inference grant can reach probe." The CLI has that channel — the token openagents auth login stored — and needs a route to exchange it.

The CLI ships neither. It registers api, auth, and repo (packages/openagents-cli/src/cli.ts:909 in the openagents monorepo), has no streaming (ApiTransport reads the whole body then parses, src/api-transport.ts:103), and has no interactive code at all: terminal-session.ts is 17 lines that report whether stdin and stdout are TTYs.

Work

Full design in docs/2026-08-23-openagents-coder-cli-spec.md. The load-bearing decision is that the CLI is an ACP client, not a second coding agent: it mints the grant, spawns the runtime with the grant in the child environment, speaks ACP v1 over stdio, and renders. Being a separate process is also what fixes the approval problem probe spent three commits failing to solve in process (efcb799, 29459f1, cfdd422), because session/request_permission is a request over the wire rather than a prompt competing for stdin.

This repository.

  • POST, GET, and DELETE /api/v3/coder/sessions on :chat_account_api, scope chat:account (router.ex:57). Mint through OpenAgents.Inference.mint/1 with conversation_id from Conversations.ensure_conversation/1 and machine_id: nil, which lib/openagents/work/coding.ex:64 already proves the schema accepts.
  • Ceilings independent of the delegation ceilings, a cap on concurrent active grants per account, and revocation on DELETE.
  • Later: chunk the inference proxy's SSE response instead of building the whole body and sending it once (inference_proxy_controller.ex:155-:170); checkpoint receipts and the commit trailer that links a commit to one, both currently proposed in docs/taxonomy.md.

The openagents monorepo (packages/openagents-cli/).

  • openagents coder [prompt] registered at src/cli.ts:909, reading the token through the existing resolveApiSession (src/session.ts:47) so profiles, the credential store, and OPENAGENTS_TOKEN all work unchanged.
  • A CoderSession service that mints and revokes; an AcpClient that spawns the runtime and speaks newline-delimited JSON-RPC over its standard streams.
  • An OpenTUI interface in split-footer mode: streaming Markdown, CodeRenderable with line numbers for tool output, DiffRenderable for edits, a status line carrying the grant's call and token budget, double-escape interruption, and a decision pane for approvals. @opentui/core is an optional dependency and the command degrades to line-oriented output when it is unavailable, when stdout is not a TTY, or under --plain or --json.
  • A client-side policy table that moves edit to approval-required. The runtime's default allows it, which is right for a delegated agent under a controller's tier policy and wrong for your own checkout.

What is deliberately not built. No new tool-execution endpoint: local tools run locally and delegated work goes through the delegation routes that exist (router.ex:420-:423). No POST /api/v3/coder/turns: a coder turn is not a row in turns or account_chat_runs, because DATA-002 gives an account one conversation and TURN-001 gives a conversation one active turn. The durable evidence is the grant ledger and the forge push receipt.

Acceptance criteria

  • openagents coder "what does this repository do" in a fresh checkout answers from files it read, with no sign-in beyond openagents auth login.
  • openagents api "coder/sessions/<id>" reports the metered calls and tokens for that session, and the grant is revoked when the process exits.
  • The token never reaches the runtime process; only the grant does, in the child environment, and it appears in no transcript, log line, or tool environment.
  • Every edit, shell, git push, tracker write, and delegation prompts before it runs, and a refusal reaches the model as a tool result rather than failing the turn.
  • No permission option is offered whose id, name, or kind matches /bypass/i, and there is no flag that means "stop asking".
  • Double-escape within five seconds cancels a running turn, keeps the partial assistant text, and lets an already-running tool record its outcome.
  • With no TTY, with --plain, or with the renderer unavailable, the command runs line-oriented and its exit code is unchanged.
  • A mid-session token expiry suspends the session with a typed reason instead of terminating it; SIGINT still exits 130.
  • No coder turn creates a row in turns or account_chat_runs.

Verification

Controller and context tests for the session routes covering mint, ceilings, per-account concurrency, revocation, and a scope refusal. Client tests in packages/openagents-cli/test/ covering the ACP handshake, the permission round trip in both directions, cancellation, the degradation matrix, and grant redaction. Renderer fixture tests over a session snapshot. A live smoke that runs a read-only session against this repository and shows the metered grant afterward.

Dependencies

  • docs/2026-08-23-openagents-coder-cli-spec.md carries the full design, the route-by-route API split, and the open questions.
  • Sibling of #129 (named issue and project commands in the CLI): both add commands to @openagentsinc/cli, and the tracker tools in Stage 4 should reuse whatever #129 settles about repository inference and --json envelopes.
  • #127 covers scoped push identity for a delegation working an issue on a connected Computer. The coder does not work around it: where an assignment credential is absent, the session reads, edits, and reports, and says it cannot land the work.
  • #73 standardizes ACP as the executor contract across Work and SCV. This issue is the interactive client for the same protocol.
  • Blocked on confirming that the runtime package is installable and that its JavaScript host reaches tool parity with its native host. Section 11 of the doc names the commands that settle it.
  1. AtlantisPleb opened this issue 3d ago
  2. A AtlantisPleb Author 2d ago

    This epic is now tracked by the Coder v1 project, oriented by docs/2026-08-24-coder-first-cloud-complements.md (product plan, compute mix, sequence) and docs/2026-08-24-registry-network-strategy.md (why the coder is the wedge). First release scope: thread transcript writer and resume (openagents#23, openagents#24, unblocked by openagents#19), the three-lane compute mix — house provider catalog (#199), metered inference offering (#200), foreign-harness delegation with thread capture (openagents#25) — model and reasoning honesty (#160, #164), the web thread viewer (#201), fleet rendering (openagents#28), foreign session resume (#198), and the leaderboard union arm (#204). The plugin lane runs in parallel as Plugin registry.

  3. A AtlantisPleb Author 2d ago

    Shipped, and both acceptance criteria are proven end to end rather than argued.

    openagents coder in a fresh checkout answers from files it read. Run just now in a brand-new git repository containing only a README and a sorter.py, with no sign-in beyond the stored token: it read the file and answered 'sorter.py sorts widgets first by mass, then by colour', and on a broader question described the module and correctly noted the repository has no tests, packaging, or dependencies.

    The session's metered calls and tokens are readable. That was the one gap left, and it is closed on main in b65a63a: OpenAgents.Threads.spend/1 sums calls and usage across every grant a thread has held, and the thread view carries it. From the run above:

    openagents coder in probe-demo on main
    {"calls": 2, "grants": 1, "usage": {"input_tokens": 7422, "output_tokens": 46,
     "cache_read_input_tokens": 7329, "estimated_cost_microusd": 9737, "total_tokens": 7468}}
    

    That also happens to be #220's cached-token split visible in real traffic: 7,329 of 7,422 input tokens were cache reads, which is exactly the prefix-reuse shape that made the old cost figures wrong.

    Two deliberate departures from the spec written here, both later decisions rather than omissions:

    • The grant-minting route is the thread lane (POST /api/v1/threads), not /api/v3/coder/sessions. It solves the same problem the issue named — no route minted a grant for a client holding a user token — and gives a durable transcript besides.
    • 'The grant is revoked when the process exits' was reversed by #209: a thread is durable so that --resume can work at all, and what bounds an account is its credit rather than a cap. Revocation is explicit (DELETE), and re-minting on resume is the fence.

    The coder is real: tonight's entire delegation fleet ran on it.

  4. closed this as completed 2d ago
Sign in with GitHub to comment on this issue.