rad coding agent · rust · one binary编码 agent · rust · 一个二进制

A fast, efficient coding agent that fits in your pipeline. 快而高效,放得进任何流水线的编码 agent。

radian is one Rust binary. It starts with no runtime to boot and spends context on the lines a task needs. Its headless command prints every step as a JSON line and exits with a code that says how it ended. radian 是一个 Rust 二进制。启动不用先拉起运行时,上下文只花在任务需要的那几行上;无头命令把每一步按 JSON 逐行输出,退出码说明它是怎么结束的。

$ brew install hisea/tap/radian

Open source under MIT or Apache-2.0: github.com/hisea/radian.开源,MIT 或 Apache-2.0 协议:github.com/hisea/radian。

◜ radian · ~/code/radian120 × 74
❯ finish ADR 0037 slice 3 and show me the diff                                                                          
                                                                                                                        
▾ thinking · 3.2s                                                                                                       
│ Checking the inbox lanes first.                                                                                       
│                                                                                                                       
│ The job notice must not lower the turn's source: observe_input_source only ever lowers the source, so an inject       
│ from a System job would turn a human turn into a non-human one.                                                       
│                                                                                                                       
│ - followup wakes a new turn, so the source is set fresh                                                               
│ - inject lands mid-turn and lowers it                                                                                 
│                                                                                                                       
│ So the completion should arrive as a notice with a System source, never as a user turn.                               
                                                                                                                        
• shell $ cargo test -p radian-host --locked · exit 0 · 12.4s ✓                                                         
│ running 41 tests                                                                                                      
│ test job_notify::settled_job_wakes_the_idle_model ... ok                                                              
│ test job_notify::a_waited_job_sends_no_notice ... ok                                                                  
│ test result: ok. 41 passed; 0 failed; 0 ignored                                                                       
                                                                                                                        
• read crates/radian-host/src/job_notify.rs 100-160 · 0.01s ✓                                                           
                                                                                                                        
• patch crates/radian-host/src/job_notify.rs · +5 −2 · 0.03s ✓                                                          
│ @@ -120,9 +120,12 @@ impl JobNotify {                                                                                 
│  120       fn notice(&self, job: &Settled) -> Message {                                                               
│  121 -         let source = InputSource::User;                                                                        
│  121 +         // ADR 0037: a notice is low-trust data, never a user turn.                                            
│  122 +         let source = InputSource::System;                                                                      
│  123           let mut meta = MessageMeta::default();                                                                 
│  124           meta.source = Some(source);                                                                            
│  124 -         Message::user(format!("job {} done", job.id)).with_meta(meta)                                          
│  125 +         let body = format!("<job id={} exit={} />", job.id, job.exit);                                         
│  126 +         Message::user(body).with_meta(meta)                                                                    
│  127       }                                                                                                          
                                                                                                                        
╭─ ask ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ How should the UI extension be loaded?                                                                                
│ ◉ External process plugins                                                                                            
│   ↳ a separate program, JSON-RPC over stdio                                                                           
│ ○ Built-in Rust components                                                                                            
╰──                                                                                                                     
                                                                                                                        
▸ thinking · 3.2s · 8 lines                                                                                             
                                                                                                                        
● What I did                                                                                                            
                                                                                                                        
  1. Added background: true to the shell tool and a job registry in radian-host.                                        
  2. Ran the Verify commands; cargo test --workspace --locked passed with 0 warnings.                                   
                                                                                                                        
╭─ rust ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ fn notice(&self, job: &Settled) -> Message {                                                                          
│     Message::user(body).with_meta(meta) // never a user turn                                                          
│ }                                                                                                                     
╰──                                                                                                                     
                                                                                                                        
  ┌─────────────────────────────────┬────────────────┐                                                                  
  │ Command                         │ Result         │                                                                  
  ├─────────────────────────────────┼────────────────┤                                                                  
  │ cargo clippy --workspace        │ ok, 0 warnings │                                                                  
  ├─────────────────────────────────┼────────────────┤                                                                  
  │ cargo test --workspace --locked │ 412 passed     │                                                                  
  └─────────────────────────────────┴────────────────┘                                                                  
                                                                                                                        
── context compacted (rev 2, 148k → 61k) ──                                                                             
                                                                                                                        
* background job bg-2 finished · cargo test -p radian-host · exit 0 · 38s                                               
! background job bg-3 has printed nothing for 6m                                                                        
                                                                                                                        
── ◜  Working · 12s · esc to interrupt ─────────────────────────────────────────────────────────────────────────────────
❯ explain the inbox lanes▏                                                                                              
                                                                                                                        
Usage: 5h: 80% · 7d: 19% · 30d: 40% · 5h ↺1h35m                                                                         
 glm-5.3-flash ·  radian ·  notice-source +2 ·  render-study                              ▣ ━━━━━━━━──────── 41%  ◐ high
                                                                                    ↑15M ↓2.6M R1962M $14.510 41.3%/1.0M
                                                                                                                        
theme: built-in light
❯ finish ADR 0037 slice 3 and show me the diff                                                                          
                                                                                                                        
▾ thinking · 3.2s                                                                                                       
│ Checking the inbox lanes first.                                                                                       
│                                                                                                                       
│ The job notice must not lower the turn's source: observe_input_source only ever lowers the source, so an inject       
│ from a System job would turn a human turn into a non-human one.                                                       
│                                                                                                                       
│ - followup wakes a new turn, so the source is set fresh                                                               
│ - inject lands mid-turn and lowers it                                                                                 
│                                                                                                                       
│ So the completion should arrive as a notice with a System source, never as a user turn.                               
                                                                                                                        
• shell $ cargo test -p radian-host --locked · exit 0 · 12.4s ✓                                                         
│ running 41 tests                                                                                                      
│ test job_notify::settled_job_wakes_the_idle_model ... ok                                                              
│ test job_notify::a_waited_job_sends_no_notice ... ok                                                                  
│ test result: ok. 41 passed; 0 failed; 0 ignored                                                                       
                                                                                                                        
• read crates/radian-host/src/job_notify.rs 100-160 · 0.01s ✓                                                           
                                                                                                                        
• patch crates/radian-host/src/job_notify.rs · +5 −2 · 0.03s ✓                                                          
│ @@ -120,9 +120,12 @@ impl JobNotify {                                                                                 
│  120       fn notice(&self, job: &Settled) -> Message {                                                               
│  121 -         let source = InputSource::User;                                                                        
│  121 +         // ADR 0037: a notice is low-trust data, never a user turn.                                            
│  122 +         let source = InputSource::System;                                                                      
│  123           let mut meta = MessageMeta::default();                                                                 
│  124           meta.source = Some(source);                                                                            
│  124 -         Message::user(format!("job {} done", job.id)).with_meta(meta)                                          
│  125 +         let body = format!("<job id={} exit={} />", job.id, job.exit);                                         
│  126 +         Message::user(body).with_meta(meta)                                                                    
│  127       }                                                                                                          
                                                                                                                        
╭─ ask ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ How should the UI extension be loaded?                                                                                
│ ◉ External process plugins                                                                                            
│   ↳ a separate program, JSON-RPC over stdio                                                                           
│ ○ Built-in Rust components                                                                                            
╰──                                                                                                                     
                                                                                                                        
▸ thinking · 3.2s · 8 lines                                                                                             
                                                                                                                        
● What I did                                                                                                            
                                                                                                                        
  1. Added background: true to the shell tool and a job registry in radian-host.                                        
  2. Ran the Verify commands; cargo test --workspace --locked passed with 0 warnings.                                   
                                                                                                                        
╭─ rust ────────────────────────────────────────────────────────────────────────────────────────────────────────────────
│ fn notice(&self, job: &Settled) -> Message {                                                                          
│     Message::user(body).with_meta(meta) // never a user turn                                                          
│ }                                                                                                                     
╰──                                                                                                                     
                                                                                                                        
  ┌─────────────────────────────────┬────────────────┐                                                                  
  │ Command                         │ Result         │                                                                  
  ├─────────────────────────────────┼────────────────┤                                                                  
  │ cargo clippy --workspace        │ ok, 0 warnings │                                                                  
  ├─────────────────────────────────┼────────────────┤                                                                  
  │ cargo test --workspace --locked │ 412 passed     │                                                                  
  └─────────────────────────────────┴────────────────┘                                                                  
                                                                                                                        
── context compacted (rev 2, 148k → 61k) ──                                                                             
                                                                                                                        
* background job bg-2 finished · cargo test -p radian-host · exit 0 · 38s                                               
! background job bg-3 has printed nothing for 6m                                                                        
                                                                                                                        
── ◜  Working · 12s · esc to interrupt ─────────────────────────────────────────────────────────────────────────────────
❯ explain the inbox lanes▏                                                                                              
                                                                                                                        
Usage: 5h: 80% · 7d: 19% · 30d: 40% · 5h ↺1h35m                                                                         
 glm-5.3-flash ·  radian ·  notice-source +2 ·  render-study                              ▣ ━━━━━━━━──────── 41%  ◐ high
                                                                                    ↑15M ↓2.6M R1962M $14.510 41.3%/1.0M
                                                                                                                        
theme: built-in dark
fig. 1 · radian at 120 columns, rendered by the prototype script 图 1 · 120 列的 radian,由原型脚本渲染
/01 why radian为什么是 radian
◜ speed快

Quick to start, quick to come back.启动快,回来也快。

A native binary with no runtime to boot. A foreground command never moves to the background on its own: only background: true starts a job, and a finished job's notice reaches the model, so it keeps working.原生二进制,不用先拉起一个运行时。前台命令不会自己转到后台:只有 background: true 才会起一个 job,跑完的通知会送到模型那里,模型不用干等。

no runtime to boot · one file per session无运行时启动 · 每个会话一个文件
◝ efficiency高效

Context goes to the work.上下文关注任务。

Reads take line ranges. A budget guard watches the window and compacts before it overflows. The footer shows tokens and cost as you go.读文件按行号范围读。预算守卫盯着上下文窗口,快满之前先压缩。footer 里随时看得到 token 和费用。

read by range · auto compaction
usage and cost in the footer
按范围读 · 自动压缩
footer 显示用量和费用
◞ orchestration编排

Built to be orchestrated.为被编排而生。

radian runs no outside agents; it is built to be run by them. One prompt in through radian exec, every step out as a JSON line, and an exit code that tells completed from failed from out of budget. Inside herdr it reports working, idle or blocked by itself, and radian attach watches any session read-only. Parallel work stays inside one reply: several subagent calls run side by side.radian 不调度外部 agent,它是为了被调度而做的。一句话从 radian exec 进去,每一步按 JSON 逐行出来,退出码分得清完成、失败和超出预算。在 herdr 里它自己报告 working、idle、blocked;radian attach 可以只读地看任何一个会话。并行只发生在一条回复之内:几个 subagent 调用同时跑。

exec --output jsonl · exit codes by outcome
herdr-ready · attach · control socket
◟

Under all three: every step goes to a journal you can resume and fork, the sandbox is a switch (one flag, or saved per project, and it can only narrow), and only you can approve. 三者底下:每一步都写进可恢复、可 fork 的会话日志,沙箱是一个开关(一个参数,或按项目保存,只能收窄),审批只能由你来给。

/02 headless无头

Headless, observable.无头,可观测。

One prompt in, the journal out: a JSON line per step and the outcome on the last line. It is the same session file, so a script can stop halfway and you pick it up in the full-screen app.一句话进,journal 出:每一步一行 JSON,最后一行是结局。同一个会话文件,脚本跑到一半,可以在全屏界面里接着跑。

Inside herdr, radian reports working, idle and blocked by itself, and nothing about commands, paths or content.在 herdr 里,radian 自己报告 working、idle、blocked,不报命令、路径和内容。

/03 compatibility兼容

Works with what you have.用你已有的。

Instruction files, skills, MCP servers and themes carry over.指令文件、skills、MCP server 和主题可以直接带过来。

  • AGENTS.md · CLAUDE.mdRead from the workspace up to the repo root, with @path imports.从工作区一路读到仓库根,支持 @path 导入。
  • skills~/.agents/skills/<name>/SKILL.md, the directory pi, Codex and opencode share.~/.agents/skills/<name>/SKILL.md,和 pi、Codex、opencode 共用的目录。
  • MCPmcpServers config, ${VAR} expansion, stdio servers.mcpServers 格式的配置,支持 ${VAR} 展开,stdio server。
  • hooksThe Claude / Codex JSON subset: PreToolUse, PostToolUse, Stop.Claude / Codex 的 JSON 子集:PreToolUse、PostToolUse、Stop。
  • themespi theme files, imported once with radian theme import.pi 的主题文件,用 radian theme import 一次性导入。
  • memoryGlobal and project scopes. The agent cannot edit the records you wrote.全局和项目两层。agent 改不了你自己写的记录。
/04 install安装

Install.安装。

Homebrew on macOS and Linux, a release tarball, or Cargo from source.macOS 和 Linux 都能用 Homebrew,也可以下载 release 安装包,或者用 Cargo 从源码装。

$ brew install hisea/tap/radian
$ cargo install --git https://github.com/hisea/radian radian-tui
github.com/hisea/radian/releases signed macOS and static Linux tarballs签名的 macOS 包和静态 Linux 包
  • Homebrew links only radian: homebrew-core already has a rad formula. The tarball and Cargo also give you the rad alias.Homebrew 只链接 radian,因为 homebrew-core 里已经有一个叫 rad 的包;安装包和 Cargo 还会带上 rad 这个别名。
  • On Linux the workspace profile needs bubblewrap.Linux 上 workspace 等级需要先装 bubblewrap。
  • Then radian login and radian. Full steps: quickstart.装好后运行 radian login,再运行 radian。完整步骤见快速开始。
/05 run运行

One binary, two ways in.一个二进制,两种用法。

Full screen at the keyboard. Headless for scripts, CI and other orchestrators.坐在键盘前用全屏界面;脚本、CI 和别的编排器用无头命令。

radian

The full-screen terminal app.全屏终端界面。

radian
# sandbox# 沙箱
radian --profile workspace

radian exec

One prompt in, the steps out as JSON lines.一句话进,每一步按 JSON 逐行出。

radian exec --resume demo \
  --output jsonl \
  -p "now read docs/"
Providers: Anthropic, OpenAI, ChatGPT (browser OAuth sign-in), opencode-go, and any OpenAI-compatible endpoint. /login stores the key in ~/.radian/credentials/auth.json.模型来源:Anthropic、OpenAI、ChatGPT(浏览器 OAuth 登录)、opencode-go,以及任何兼容 OpenAI 的端点。/login 把凭据存进 ~/.radian/credentials/auth.json。
same session file同一个会话文件~/.radian/sessions/demo.jsonl
θ = 1 r s = r
/∠ the name名字

One radian is the angle where the arc is exactly as long as the radius.一弧度,就是弧长正好等于半径时的那个角。

1 rad = 180°/π ≈ 57.2958°