Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
4627107
feat: provider/model catalog Connections across Desktop, server, and …
wangtsiao Sep 4, 2026
a3c4e36
fix: cancel tools and clean up child processes
wangtsiao Sep 5, 2026
b98063b
fix: make code search installation opt-in (#198)
wangtsiao Sep 5, 2026
6f603bf
feat: durable turn recovery, workspace Changes review, and git-aligne…
wangtsiao Sep 7, 2026
acd078a
fix: pause goals on provider failure even when recovery is available
wangtsiao Sep 7, 2026
f38e0f8
style: rustfmt after goal pause recovery fix
wangtsiao Sep 7, 2026
6a4d456
Merge pull request #199 from wangtsiao/feat/changes-review-expand
wangtsiao Sep 7, 2026
3705caa
chore: release v0.1.39
wangtsiao Sep 7, 2026
d7081f6
Merge pull request #200 from wangtsiao/chore/release-v0.1.39
wangtsiao Sep 8, 2026
ce8f900
fix: stop turn writers before shutdown recovery rollout appends (#201)
wangtsiao Sep 8, 2026
045d17a
fix(desktop): compaction timeline, tool verbs; remove compaction_toke…
wangtsiao Sep 8, 2026
d3c75ac
Large refactor: Pi Agent-inspired architecture and Recursive Agent su…
wangtsiao Oct 5, 2026
ac6f092
fix: harden Desktop and refresh project docs (#207)
wangtsiao Oct 5, 2026
dc7084b
docs: introduce Recursive Agent and IPython workspace (#208)
wangtsiao Oct 6, 2026
be79688
fix: restore Windows cargo build (#209)
wangtsiao Oct 6, 2026
153f3a6
Add opt-in anonymous Parallel Search MCP provider (#211)
georgeatparallel Oct 8, 2026
6137765
fix(windows): restore desktop turns and harden desktop recovery (#212)
wangtsiao Oct 9, 2026
da52435
chore: release v0.2.0 (#213)
wangtsiao Oct 10, 2026
2949321
Fix Unix credential delivery across release targets (#214)
wangtsiao Oct 10, 2026
7c8b56e
Bundle private TUI and Python runtimes and remove code search (#215)
wangtsiao Oct 10, 2026
3243298
Fix onboarding catalog, TUI release prefix, and root cleanup (#216)
wangtsiao Oct 10, 2026
199ec3d
Refresh TUI models in the background and reduce runtime bundle size (…
wangtsiao Oct 10, 2026
09186da
Remove unknown from public Linux release asset names (#218)
wangtsiao Oct 10, 2026
ad85955
fix(tui): preserve in-flight steering and detect Windows Git Bash (#219)
wangtsiao Oct 11, 2026
f0b8b31
fix(release): ship Windows sandbox helpers with every client (#220)
wangtsiao Oct 11, 2026
b8264de
fix(windows): provision Python sandbox on first tool use (#221)
wangtsiao Oct 11, 2026
f32b330
fix(tui): use a terminal-safe shortcut to follow output (#222)
wangtsiao Oct 11, 2026
7890924
docs: shorten and align multilingual READMEs (#223)
wangtsiao Oct 11, 2026
443236b
fix(tui): render rlm.bash calls as shell summaries (#224)
wangtsiao Oct 11, 2026
b54167a
perf(ci): skip unrelated Rust builds on client-only PRs (#225)
wangtsiao Oct 11, 2026
80807f0
test(tui): require observable outcomes in tmux smoke tests (#226)
wangtsiao Oct 11, 2026
83dc675
fix(windows): normalize trailing slash in install PATH (#227)
wangtsiao Oct 12, 2026
10cb997
fix(runtime): isolate bundled interpreters from host settings (#228)
wangtsiao Oct 12, 2026
9bebb93
fix(refine): preserve catalog model capabilities (#229)
wangtsiao Oct 12, 2026
2e630fa
fix(tui): show the running release version
wangtsiao Oct 12, 2026
4841415
Merge pull request #230 from wangtsiao/codex/tui-release-version
wangtsiao Oct 12, 2026
1ce2945
fix(harness): document recovered child session names
wangtsiao Oct 12, 2026
dbb7551
Merge pull request #231 from wangtsiao/codex/recovered-child-guidance
wangtsiao Oct 12, 2026
931ae5b
fix(tui): finish source placeholders with Python cells
wangtsiao Oct 12, 2026
152482e
Merge pull request #232 from wangtsiao/codex/completed-cell-source-state
wangtsiao Oct 12, 2026
2dee5e8
fix(compact): retain agent requests across queued follow-ups
wangtsiao Oct 12, 2026
9c169f6
Merge pull request #233 from wangtsiao/codex/preserve-deferred-compac…
wangtsiao Oct 12, 2026
f70ec72
fix(compact): reject canceled or superseded admission
wangtsiao Oct 12, 2026
9064e55
Merge pull request #234 from wangtsiao/codex/reject-stale-compact-adm…
wangtsiao Oct 12, 2026
b514d7a
build: add an opt-in faster development profile
wangtsiao Oct 12, 2026
5afcedd
Merge pull request #235 from wangtsiao/codex/measure-fast-dev-profile
wangtsiao Oct 12, 2026
2cd47d5
fix(tui): preserve the server context window
wangtsiao Oct 12, 2026
e60d31a
Merge pull request #236 from wangtsiao/codex/preserve-server-context-…
wangtsiao Oct 12, 2026
6d57474
fix(runtime): register queued turns for cancellation
wangtsiao Oct 12, 2026
dfae978
fix(runtime): seed queued reservation snapshot before activation
wangtsiao Oct 12, 2026
c365e5a
fix(goal): refresh context after in-flight goal changes
wangtsiao Oct 12, 2026
3244fab
Merge pull request #237 from wangtsiao/codex/register-queued-turn-can…
wangtsiao Oct 12, 2026
52dc162
Merge pull request #238 from wangtsiao/codex/refresh-inflight-goal-co…
wangtsiao Oct 12, 2026
51e4bcc
fix(server): retain goal resume while an interrupted turn finishes
wangtsiao Oct 13, 2026
a9a741f
Merge pull request #239 from 7df-lab/codex/goal-resume-after-python-i…
wangtsiao Oct 13, 2026
4c6dc5d
fix(server): bound incoming RPC future storage
wangtsiao Oct 13, 2026
f20dcc4
Merge pull request #240 from wangtsiao/codex/box-native-request-dispatch
wangtsiao Oct 13, 2026
8550c18
fix(tests): build stdio fixtures in the selected profile (#242)
wangtsiao Oct 13, 2026
8dece99
fix(server): preserve nested agent paths on restore
wangtsiao Oct 13, 2026
9b3312b
Merge pull request #243 from wangtsiao/codex/persist-child-agent-iden…
wangtsiao Oct 13, 2026
43b962a
chore(release): v0.2.12
wangtsiao Oct 13, 2026
a45f5cc
Merge pull request #244 from wangtsiao/codex/release-v0.2.12
wangtsiao Oct 13, 2026
78bda51
fix(rlm): support approved shell escalation and writable Devo home (#…
wangtsiao Oct 15, 2026
7a53c9f
fix(cli): make headless RLM prompts run correctly on Linux (#246)
wangtsiao Oct 15, 2026
10374fb
chore: release v0.2.14
wangtsiao Oct 15, 2026
42ce095
Merge pull request #247 from wangtsiao/release/v0.2.14
wangtsiao Oct 15, 2026
292ebee
chore: release v0.2.15 (#248)
wangtsiao Oct 15, 2026
7c324f5
fix(tui): guard openModelPicker crash after /login model selection
wangtsiao Oct 19, 2026
8fab0f4
fix(kernel): resolve bare python via PATH before spawning kernel
wangtsiao Oct 19, 2026
0312dab
ci(tui): add mock-provider smoke and three-wire live LLM jobs
wangtsiao Oct 19, 2026
30f3346
feat(sandbox): temporarily disable OS-sandbox enforcement everywhere
wangtsiao Oct 19, 2026
a5d2af6
feat(permissions): default new sessions to full-access
wangtsiao Oct 19, 2026
85e5533
feat(tui): hide /permissions while sandboxing is disabled
wangtsiao Oct 19, 2026
dd0fc1c
feat(desktop): hide the permission picker, default launches to full-a…
wangtsiao Oct 19, 2026
d8ae300
docs: note the temporary sandbox disablement and its revert switches
wangtsiao Oct 19, 2026
accca9e
test: adjust contract tests for the full-access default
wangtsiao Oct 19, 2026
76198fc
style: cargo fmt
wangtsiao Oct 19, 2026
c9fb12f
fix(ci): read the optional OPENAI key from job env in the step if
wangtsiao Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
40 changes: 40 additions & 0 deletions .github/workflows/benchmark-cli.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Benchmark CLI build

on:
pull_request:
paths:
- crates/cli/**
- crates/sandbox/**
- .github/workflows/benchmark-cli.yml
workflow_dispatch:

permissions:
contents: read

jobs:
linux-cli:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-musl
- uses: mlugg/setup-zig@v2
- uses: taiki-e/install-action@v2
with:
tool: cargo-zigbuild@0.23.0
- uses: Swatinem/rust-cache@v2
with:
key: benchmark-linux-cli
- name: Verify detached Linux kernel fence
run: cargo test -p devo-sandbox --test headless_tty -- --nocapture
- name: Build native Linux CLI for container smoke runs
run: cargo zigbuild --target x86_64-unknown-linux-musl -p devo-cli
- name: Strip debug information from the benchmark artifact
run: strip target/x86_64-unknown-linux-musl/debug/devo
- uses: actions/upload-artifact@v4
with:
name: devo-benchmark-linux-${{ github.event.pull_request.head.sha || github.sha }}
path: target/x86_64-unknown-linux-musl/debug/devo
if-no-files-found: error
retention-days: 7
311 changes: 301 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,296 @@ on:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:

env:
CARGO_TERM_COLOR: always
RUSTFLAGS: -Dwarnings

jobs:
changes:
name: Select affected CI components
runs-on: ubuntu-latest
outputs:
rust: ${{ steps.scope.outputs.rust }}
desktop: ${{ steps.scope.outputs.desktop }}
tui: ${{ steps.scope.outputs.tui }}
kernel_python: ${{ steps.scope.outputs.kernel_python }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: python -m unittest discover -s scripts -p test_ci_scope.py -v
- id: scope
env:
EVENT_NAME: ${{ github.event_name }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
if [ "$EVENT_NAME" = "pull_request" ]; then
python scripts/ci_scope.py --base "$BASE_SHA" --head "$HEAD_SHA" --output "$GITHUB_OUTPUT"
else
python scripts/ci_scope.py --all --output "$GITHUB_OUTPUT"
fi

unix-kernel:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Unix kernel (${{ matrix.target }})
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
target: x86_64-unknown-linux-musl
- os: ubuntu-latest
target: aarch64-unknown-linux-musl
- os: macos-latest
target: x86_64-apple-darwin
- os: macos-latest
target: aarch64-apple-darwin
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- uses: Swatinem/rust-cache@v2
- uses: mlugg/setup-zig@v2
if: runner.os == 'Linux'
- uses: taiki-e/install-action@v2
if: runner.os == 'Linux'
with:
tool: cargo-zigbuild@0.23.0
- run: cargo zigbuild --locked -p devo-kernel --lib --target ${{ matrix.target }}
if: runner.os == 'Linux'
- run: cargo check --locked -p devo-kernel --all-targets --target ${{ matrix.target }}
if: runner.os == 'macOS'
- run: cargo test --locked -p devo-kernel credential_unix::tests
if: runner.os == 'macOS'

windows-kernel:
needs: changes
if: needs.changes.outputs.kernel_python == 'true'
name: Windows kernel (Python ${{ matrix.python }})
runs-on: windows-latest
strategy:
matrix:
python: ["3.11", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python }}
- run: python -m pip install dill==0.4.1
- run: python -m unittest discover -s crates/kernel/rlm-runtime/tests -v
env:
PYTHONPATH: crates/kernel/rlm-runtime/src

windows-check:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Windows Rust check and Clippy
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo check --workspace --all-targets
- run: cargo clippy --workspace --all-targets -- -D warnings

- run: cargo test -p devo-windows-sandbox --bin devo-command-runner

- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.4.0"
- run: bun test apps/desktop/scripts/dev-runtime.test.ts

desktop:
needs: changes
if: needs.changes.outputs.desktop == 'true'
name: Desktop tests and types
runs-on: ubuntu-latest
defaults:
run:
working-directory: apps/desktop
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: oven-sh/setup-bun@v2
with:
bun-version: "1.4.0"
- run: bun install --frozen-lockfile
- run: bun run test
- run: bunx tsc --noEmit

tui:
needs: changes
if: needs.changes.outputs.tui == 'true'
name: TUI tests (${{ matrix.os }})
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest]
defaults:
run:
working-directory: apps/tui
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: apps/tui/package-lock.json
- run: npm ci --ignore-scripts --no-audit --no-fund
- run: npm test
- run: node --import tsx --test lib/coding-agent/src/modes/interactive/interactive-mode-model-candidates.test.ts lib/coding-agent/src/modes/interactive/interactive-mode-session-ancillary.test.ts

tmux-secrets:
name: Detect live TUI provider secrets
runs-on: ubuntu-latest
outputs:
live: ${{ steps.detect.outputs.live }}
steps:
# Job-level `if` cannot read the secrets context; fork PRs see empty
# secrets, so this doubles as the fork gate for the live job.
- id: detect
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
run: |
if [ -n "$DEEPSEEK_API_KEY" ]; then
echo "live=true" >> "$GITHUB_OUTPUT"
else
echo "live=false" >> "$GITHUB_OUTPUT"
fi

tmux:
needs: changes
if: needs.changes.outputs.rust == 'true' || needs.changes.outputs.tui == 'true'
name: TUI tmux smoke (mock provider)
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: apps/tui/package-lock.json
- name: Ensure tmux
run: command -v tmux || (sudo apt-get update && sudo apt-get install -y tmux)
- name: Install TUI dependencies
working-directory: apps/tui
run: npm ci --ignore-scripts --no-audit --no-fund
- name: Build CLI
run: cargo build -p devo-cli --bin devo --locked
- name: Boot and outcome scenarios (no provider credentials)
working-directory: apps/tui
run: |
for scenario in T1 goal heartbeat; do
echo "::group::tmux smoke: $scenario"
node scripts/run-tmux-smoke.js "$scenario"
echo "::endgroup::"
done
- name: Completed-turn scenario against the local mock provider
working-directory: apps/tui
env:
DEVO_TUI_TEST_TIMEOUT_MS: "120000"
run: |
home="$(mktemp -d)"
node scripts/smoke-mock-provider.mjs 8801 &
mock_pid=$!
trap 'kill "$mock_pid" 2>/dev/null || true' EXIT
printf '%s\n' \
'{"model": "deepseek/deepseek-v4-flash", "provider": {"deepseek": {"credential": "deepseek", "enabled": true, "base_url": "http://127.0.0.1:8801"}}}' \
> "$home/providers.json"
printf '%s\n' '{"deepseek": {"type": "api_key", "key": "ci-mock-key"}}' > "$home/auth.json"
chmod 600 "$home/auth.json"
DEVO_HOME="$home" node scripts/run-tmux-smoke.js T2

tmux-live:
needs: [changes, tmux-secrets]
if: >-
needs.tmux-secrets.outputs.live == 'true' &&
(needs.changes.outputs.rust == 'true' || needs.changes.outputs.tui == 'true')
name: TUI tmux live LLM (${{ matrix.os }})
runs-on: ${{ matrix.os }}
timeout-minutes: 40
strategy:
fail-fast: false
matrix:
# macOS covers the seatbelt fence and python3 fallback paths that
# Linux cannot reach with the same code.
os: [ubuntu-latest, macos-latest]
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v4
with:
node-version: "24"
cache: npm
cache-dependency-path: apps/tui/package-lock.json
- name: Ensure tmux
run: command -v tmux || (if [ "$(uname)" = Darwin ]; then brew install tmux; else sudo apt-get update && sudo apt-get install -y tmux; fi)
- name: Install TUI dependencies
working-directory: apps/tui
run: npm ci --ignore-scripts --no-audit --no-fund
- name: Build CLI
run: cargo build -p devo-cli --bin devo --locked
- name: Seed live Devo homes (one per wire API)
run: |
# chat-completions: builtin DeepSeek endpoint
chat="$(mktemp -d)/home"; mkdir -p "$chat"
printf '%s\n' '{"model": "deepseek/deepseek-v4-flash", "provider": {"deepseek": {"credential": "deepseek", "enabled": true, "base_url": "https://api.deepseek.com", "wire_api": "openai_chat_completions"}}}' > "$chat/providers.json"
# anthropic-messages: DeepSeek's Anthropic-compatible endpoint, same key
anth="$(mktemp -d)/home"; mkdir -p "$anth"
printf '%s\n' '{"model": "deepseek/deepseek-v4-flash", "provider": {"deepseek": {"credential": "deepseek", "enabled": true, "base_url": "https://api.deepseek.com/anthropic", "wire_api": "anthropic_messages"}}}' > "$anth/providers.json"
# responses: builtin OpenAI (openai_responses wire)
resp="$(mktemp -d)/home"; mkdir -p "$resp"
printf '%s\n' '{"model": "openai/gpt-5-mini", "provider": {"openai": {"credential": "openai", "enabled": true, "base_url": "https://api.openai.com/v1", "wire_api": "openai_responses"}}}' > "$resp/providers.json"
for home in "$chat" "$anth"; do
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ deepseek: { type: "api_key", key: process.env.DEEPSEEK_API_KEY } }, null, 2));' "$home/auth.json"
chmod 600 "$home/auth.json"
done
node -e 'const fs = require("fs"); fs.writeFileSync(process.argv[1], JSON.stringify({ openai: { type: "api_key", key: process.env.OPENAI_API_KEY } }, null, 2));' "$resp/auth.json"
chmod 600 "$resp/auth.json"
echo "DEVO_LIVE_CHAT_HOME=$chat" >> "$GITHUB_ENV"
echo "DEVO_LIVE_ANTHROPIC_HOME=$anth" >> "$GITHUB_ENV"
echo "DEVO_LIVE_RESPONSES_HOME=$resp" >> "$GITHUB_ENV"
- name: Chat Completions wire (real DeepSeek)
working-directory: apps/tui
env:
DEVO_TUI_TEST_TIMEOUT_MS: "180000"
run: DEVO_HOME="$DEVO_LIVE_CHAT_HOME" node scripts/run-tmux-smoke.js T2
- name: Anthropic Messages wire (real DeepSeek)
working-directory: apps/tui
env:
DEVO_TUI_TEST_TIMEOUT_MS: "180000"
run: DEVO_HOME="$DEVO_LIVE_ANTHROPIC_HOME" node scripts/run-tmux-smoke.js T2
- name: Responses wire (real OpenAI)
# Optional second secret; skipped when absent. The secrets context is
# not available in step-level `if`, so read the job env mirror.
if: env.OPENAI_API_KEY != ''
working-directory: apps/tui
env:
DEVO_TUI_TEST_TIMEOUT_MS: "180000"
run: DEVO_HOME="$DEVO_LIVE_RESPONSES_HOME" node scripts/run-tmux-smoke.js T2
- name: Interrupted-turn scenario (real DeepSeek, Chat Completions)
working-directory: apps/tui
env:
DEVO_TUI_TEST_TIMEOUT_MS: "180000"
run: DEVO_HOME="$DEVO_LIVE_CHAT_HOME" node scripts/run-tmux-smoke.js T3

check:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Check
runs-on: ubuntu-latest
steps:
Expand All @@ -21,6 +304,8 @@ jobs:
- run: cargo check --workspace --all-targets

test:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Test
runs-on: ubuntu-latest
steps:
Expand All @@ -30,6 +315,8 @@ jobs:
- run: cargo test --workspace --jobs 1

fmt:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Rustfmt
runs-on: ubuntu-latest
steps:
Expand All @@ -39,18 +326,22 @@ jobs:
components: rustfmt
- run: cargo fmt --all -- --check

# clippy:
# name: Clippy
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v4
# - uses: dtolnay/rust-toolchain@stable
# with:
# components: clippy
# - uses: Swatinem/rust-cache@v2
# - run: cargo clippy --workspace --all-targets -- -D warnings
clippy:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy --workspace --all-targets -- -D warnings

doc:
needs: changes
if: needs.changes.outputs.rust == 'true'
name: Doc
runs-on: ubuntu-latest
steps:
Expand Down
Loading
Loading