radian 是一个 Rust 二进制。启动不用先拉起运行时,上下文只花在任务需要的那几行上;无头命令把每一步按 JSON 逐行输出,退出码说明它是怎么结束的。
$ brew install hisea/tap/radian开源,MIT 或 Apache-2.0 协议:github.com/hisea/radian。
❯ 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
原生二进制,不用先拉起一个运行时。前台命令不会自己转到后台:只有 background: true 才会起一个 job,跑完的通知会送到模型那里,模型不用干等。
读文件按行号范围读。预算守卫盯着上下文窗口,快满之前先压缩。footer 里随时看得到 token 和费用。
radian 不调度外部 agent,它是为了被调度而做的。一句话从 radian exec 进去,每一步按 JSON 逐行出来,退出码分得清完成、失败和超出预算。在 herdr 里它自己报告 working、idle、blocked;radian attach 可以只读地看任何一个会话。并行只发生在一条回复之内:几个 subagent 调用同时跑。
三者底下:每一步都写进可恢复、可 fork 的会话日志,沙箱是一个开关(一个参数,或按项目保存,只能收窄),审批只能由你来给。
一句话进,journal 出:每一步一行 JSON,最后一行是结局。同一个会话文件,脚本跑到一半,可以在全屏界面里接着跑。
在 herdr 里,radian 自己报告 working、idle、blocked,不报命令、路径和内容。
指令文件、skills、MCP server 和主题可以直接带过来。
AGENTS.md · CLAUDE.md从工作区一路读到仓库根,支持 @path 导入。skills~/.agents/skills/<name>/SKILL.md,和 pi、Codex、opencode 共用的目录。MCPmcpServers 格式的配置,支持 ${VAR} 展开,stdio server。hooksClaude / Codex 的 JSON 子集:PreToolUse、PostToolUse、Stop。themespi 的主题文件,用 radian theme import 一次性导入。memory全局和项目两层。agent 改不了你自己写的记录。macOS 和 Linux 都能用 Homebrew,也可以下载 release 安装包,或者用 Cargo 从源码装。
$ brew install hisea/tap/radian$ cargo install --git https://github.com/hisea/radian radian-tuigithub.com/hisea/radian/releases 签名的 macOS 包和静态 Linux 包
radian,因为 homebrew-core 里已经有一个叫 rad 的包;安装包和 Cargo 还会带上 rad 这个别名。workspace 等级需要先装 bubblewrap。radian login,再运行 radian。完整步骤见快速开始。坐在键盘前用全屏界面;脚本、CI 和别的编排器用无头命令。
全屏终端界面。
radian
# 沙箱
radian --profile workspace
一句话进,每一步按 JSON 逐行出。
radian exec --resume demo \
--output jsonl \
-p "now read docs/"
/login 把凭据存进 ~/.radian/credentials/auth.json。一弧度,就是弧长正好等于半径时的那个角。