Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
896d6e9
fix(provider): propagate OpenAI Responses stream failures
Oct 24, 2026
a322ec1
test(runtime): capture Windows tar extraction diagnostics
wangtsiao Nov 5, 2026
483299b
fix(runtime): decode UTF-8 tar names on Windows
wangtsiao Nov 5, 2026
a4e0ded
revert(runtime): remove ineffective Windows tar charset option
wangtsiao Nov 5, 2026
a047833
fix(install): fail closed on stale runtime cache locks
wangtsiao Nov 5, 2026
db95581
test(runtime): log concurrent installer request timing
wangtsiao Nov 5, 2026
a300eac
test(runtime): fix concurrent request diagnostic placement
wangtsiao Nov 5, 2026
0829d72
feat: move shell execution to the host and harden session journals
wangtsiao Nov 6, 2026
62bfc5a
fix(runtime): package Windows online packs as ZIP
wangtsiao Nov 6, 2026
ccd756e
fix(runtime): pass Windows ZIP path manifest as a file
wangtsiao Nov 6, 2026
b636ed4
fix(core): gate unix-only shell exec test import
wangtsiao Nov 6, 2026
7ac706a
fix(runtime): validate flat Windows ZIP path manifest
wangtsiao Nov 6, 2026
5349309
test(runtime): read Windows ZIP entries from UTF-8 file
wangtsiao Nov 6, 2026
c541a03
test(runtime): add Windows ZIP codepoint diagnostics
wangtsiao Nov 6, 2026
cb0e34a
fix(runtime): use UTC8 singleton for Windows ZIP names
wangtsiao Nov 6, 2026
adef73d
test(runtime): place Windows ZIP payloads in installed runtime tree
wangtsiao Nov 6, 2026
60c7a69
test(runtime): retain UTF8 ZIP flag regression
wangtsiao Nov 6, 2026
b76c162
fix(install): use atomic noclobber runtime lock
wangtsiao Nov 6, 2026
2457d6d
fix(ci): repair runtime packs and sandbox runner compatibility
wangtsiao Nov 6, 2026
2ce02fe
Merge pull request #255 from 7df-lab/feat/trusted-kernel-host-shell
wangtsiao Nov 6, 2026
3704443
Merge PR #254 Responses fixes onto current main
wangtsiao Nov 6, 2026
2ab13e6
ci: remove optional live LLM TUI job
wangtsiao Nov 6, 2026
fdd5236
Merge pull request #256: integrate #254 Responses errors
wangtsiao Nov 6, 2026
301e969
Merge main after #254 integration for final CI verification
wangtsiao Nov 6, 2026
e3583e1
Merge pull request #257: remove optional live LLM TUI job
wangtsiao Nov 6, 2026
f85d331
feat(goal): wait for managed background work before continuing (#258)
wangtsiao Nov 6, 2026
b02804c
refactor: remove obsolete TUI and runtime scaffolding (#260)
wangtsiao Nov 6, 2026
a84821e
refactor: use live models.dev and pinned user connections (#259)
wangtsiao Nov 6, 2026
6505bc0
refactor: route generic Python MCP only through the Rust host (#261)
wangtsiao Nov 6, 2026
e523366
refactor: remove unused Desktop fallback and compatibility paths (#262)
wangtsiao Nov 6, 2026
3a7f56e
refactor: unify skill loading and host-owned harness state (#263)
wangtsiao Nov 6, 2026
a449b4b
fix: align prompt contracts and deliver agent and automation context …
wangtsiao Nov 7, 2026
4228c9d
feat(rlm): discover and select independent child models (#265)
wangtsiao Nov 8, 2026
9930bff
feat(factory): add explicit cell-driven DAG workflows
wangtsiao Nov 8, 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
  •  
  •  
  •  
107 changes: 10 additions & 97 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,11 @@ jobs:
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
- name: Test kernel credential environment and host protocol
if: runner.os == 'macOS'
run: |
cargo test --locked -p devo-kernel session::credential_env_tests
cargo test --locked -p devo-kernel protocol::tests

windows-kernel:
needs: changes
Expand Down Expand Up @@ -151,24 +154,6 @@ jobs:
- 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'
Expand Down Expand Up @@ -209,89 +194,12 @@ jobs:
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"}}}' \
'{"model": "deepseek/deepseek-v4-flash", "provider": {"deepseek": {"credential": "deepseek", "enabled": true, "base_url": "http://127.0.0.1:8801", "wire_api": "openai_chat_completions"}}}' \
> "$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'
Expand All @@ -312,6 +220,11 @@ jobs:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install kernel snapshot dependency
run: python -m pip install dill==0.4.1
- run: cargo test --workspace --jobs 1

fmt:
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.DS_Store
.claude
__pycache__/
.venv/
node_modules
/apps/desktop/out/
/apps/desktop/resources/runtime/
Expand Down
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,11 @@ DeepSeek、Qwen、Kimi、GLM、MiniMax が含まれます。
`/model` でモデルを切り替え、`devo onboard` でプロバイダーを再設定できます。

手動設定、認証情報、MCP、オフラインインストール、リリースミラーは
[ユーザーガイド(英語)](./docs/usage.md)を参照してください。
[ユーザーガイド(英語)](./docs/user-guide.md)を参照してください。

## ドキュメントと貢献

- [ユーザーガイド(英語)](./docs/usage.md) — インストール、プロバイダー、設定、オフライン利用。
- [ユーザーガイド(英語)](./docs/user-guide.md) — インストール、プロバイダー、設定、オフライン利用。
- [TUI ガイド(英語)](./apps/tui/README.md) — ターミナル操作とコマンド。
- [貢献ガイド(英語)](./CONTRIBUTING.md) — 問題報告、ドキュメント改善、小規模な修正を歓迎します。

Expand Down
32 changes: 24 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,22 @@ The agent can filter large outputs in Python, reuse earlier work, and delegate
independent subtasks to child agents with their own sessions and Python state.
MCP, skills, permissions, and resumable sessions round out the workflow.

Children can independently select a configured model and reasoning level:

```python
models = await rlm.find_models(limit=100)
chosen = next((m for m in models if "high" in m.thinking_levels), None)
if chosen is not None:
reviewer = await rlm.spawn(
"Review the current diff.", name="reviewer",
model=chosen.selector, thinking="high",
)
```

Omitted options inherit the immediate parent. Invalid selections fail without
fallback. Discovery reflects configuration, not live health or credits. See the
[child model API](docs/rlm-child-models.md).

## Quick start

**Desktop:** Download the package for your OS and architecture from
Expand Down Expand Up @@ -59,12 +75,12 @@ Terminal bundles include Node.js and Python. On Windows, install
[Git for Windows](https://gitforwindows.org/) for shell commands and approve
the one-time sandbox setup prompt.

> **Temporarily disabled.** OS sandboxing and the permission picker are
> disabled repo-wide: every process runs with full user permissions and new
> sessions default to full-access. Revert by flipping
> `SANDBOX_ENFORCEMENT_TEMPORARILY_DISABLED` (Rust, `crates/sandbox/src/lib.rs`),
> `PERMISSIONS_COMMAND_ENABLED` (TUI), `PERMISSION_PICKER_ENABLED` (desktop),
> and the default-preset literals back to their asking defaults.
> Shell and terminal execution follow the configured sandbox profile; there is
> no global sandbox-disable switch. Python kernels are trusted user-privileged
> processes, not OS-isolated environments. See
> [the execution boundary](docs/trusted-kernel-host-shell.md).
> Permission-picker UI flags and new-session permission defaults are separate
> settings; removing the global switch does not change them.

## Configuration

Expand All @@ -74,11 +90,11 @@ DeepSeek, Qwen, Kimi, GLM, and MiniMax. Use `/model` to switch models or
`devo onboard` to revisit provider setup.

For manual configuration, credentials, MCP, offline installation, and release
mirrors, see the [user guide](./docs/usage.md).
mirrors, see the [user guide](./docs/user-guide.md).

## Docs and contributing

- [User guide](./docs/usage.md) — installation, providers, configuration, and offline use.
- [User guide](./docs/user-guide.md) — installation, providers, configuration, and offline use.
- [TUI guide](./apps/tui/README.md) — terminal controls and commands.
- [Contributing](./CONTRIBUTING.md) — issues, documentation, and focused fixes welcome.

Expand Down
4 changes: 2 additions & 2 deletions README.ru.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ devo
Переключайте модели через `/model`, повторно настраивайте провайдера через `devo onboard`.

Ручная конфигурация, учётные данные, MCP, офлайн-установка и зеркала релизов
описаны в [руководстве пользователя (на английском)](./docs/usage.md).
описаны в [руководстве пользователя (на английском)](./docs/user-guide.md).

## Документация и участие

- [Руководство пользователя (на английском)](./docs/usage.md) — установка, провайдеры, конфигурация и офлайн-работа.
- [Руководство пользователя (на английском)](./docs/user-guide.md) — установка, провайдеры, конфигурация и офлайн-работа.
- [Руководство TUI (на английском)](./apps/tui/README.md) — управление терминалом и команды.
- [Участие в разработке (на английском)](./CONTRIBUTING.md) — приветствуются сообщения об ошибках, улучшения документации и небольшие исправления.

Expand Down
4 changes: 2 additions & 2 deletions README.zh-Hans.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ devo
**Anthropic Messages** API。内置目录包含 DeepSeek、Qwen、Kimi、GLM 和 MiniMax。
用 `/model` 切换模型,用 `devo onboard` 重新配置提供商。

手动配置、凭据、MCP、离线安装和发布镜像见[使用指南(英文)](./docs/usage.md)。
手动配置、凭据、MCP、离线安装和发布镜像见[使用指南(英文)](./docs/user-guide.md)。

## 文档与贡献

- [使用指南(英文)](./docs/usage.md) — 安装、提供商、配置与离线使用。
- [使用指南(英文)](./docs/user-guide.md) — 安装、提供商、配置与离线使用。
- [TUI 指南(英文)](./apps/tui/README.md) — 终端操作与命令。
- [贡献指南(英文)](./CONTRIBUTING.md) — 欢迎问题反馈、文档改进和小范围修复。

Expand Down
4 changes: 2 additions & 2 deletions README.zh-Hant.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ devo
**Anthropic Messages** API。內建目錄包含 DeepSeek、Qwen、Kimi、GLM 和 MiniMax。
用 `/model` 切換模型,用 `devo onboard` 重新設定供應商。

手動設定、憑證、MCP、離線安裝和發行鏡像請見[使用指南(英文)](./docs/usage.md)。
手動設定、憑證、MCP、離線安裝和發行鏡像請見[使用指南(英文)](./docs/user-guide.md)。

## 文件與貢獻

- [使用指南(英文)](./docs/usage.md) — 安裝、供應商、設定與離線使用。
- [使用指南(英文)](./docs/user-guide.md) — 安裝、供應商、設定與離線使用。
- [TUI 指南(英文)](./apps/tui/README.md) — 終端操作與命令。
- [貢獻指南(英文)](./CONTRIBUTING.md) — 歡迎問題回報、文件改進和小範圍修正。

Expand Down
12 changes: 11 additions & 1 deletion apps/desktop/docs/windows-desktop-qa.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ replaced by scripted browser end-to-end tests.
| Provider failure | HTTP 402 was visible as “Insufficient provider balance,” with expandable details. |
| Funded recovery | A new real Electron request produced assistant output, Python output, and a fixture read after balance was replenished. No new 402 occurred. |
| Funded automatic queue | Enter queued a follow-up during a 60-second Python cell; it drained automatically and produced its own Python result and assistant reply. |
| Mouse submission during work | Clicked Queue message during a fenced 150-second Python cell. Edited and resubmitted the first queued entry, then queued and steered a second. The steer produced 402; the edited queue drained automatically and produced 301. Stop remains separately available. |
| Mouse submission during work | Clicked Queue message during a fenced 150-second Python cell (historical: the QA build then sandboxed the Python kernel). Edited and resubmitted the first queued entry, then queued and steered a second. The steer produced 402; the edited queue drained automatically and produced 301. Stop remains separately available. |
| Completed activity label | Removed the tool suffix: completed activity displays “Worked for 1s,” with Python details inside the disclosure. |
| Separate inner cells | Clicked Worked to reveal collapsed Thought and Python rows. Opened each independently and collapsed the whole group with Worked. Python source and output stay paired. |
| Steering visibility after completion | A fresh steer during a 90-second Python wait remained a user bubble after completion, runtime restart, and renderer reload. The assistant applied it and produced 503. The reported disappearance was not reproduced in this checkout; live/replay regression tests preserve it. |
Expand All @@ -84,6 +84,16 @@ a fresh real Electron Python turn reported the token `DevoSandboxOffline`,
used the fenced kernel, and emitted no new unfenced warning. Historical
warnings remain attached to their original turns.

Historical note: the Python-turn sentences above (the `DevoSandboxOffline`
token, the fenced kernel, and the no-new-unfenced-warning result) describe the
pre-cleanup build, where the Python kernel itself ran in the Windows sandbox.
Current builds run the Python kernel as a trusted, user-privileged process with
no OS sandbox (`docs/trusted-kernel-host-shell.md`); only host shell execution
follows the configured sandbox profile. The earlier write/network probe is
historical evidence, not validation of the current host-shell path; that path
requires a fresh Windows runtime check. A current Python turn should not be
expected to report that token or a fenced-kernel state.

Funded Electron requests now succeed. The earlier 402 checks remain coverage
of provider failure handling, rather than a current provider-balance blocker.

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { describe, expect, test } from "bun:test"
import { buildAutomationPromptParts } from "../../../../src/main/automation/prompt"
import {
DESKTOP_INITIALIZE_PARAMS,
createDevoClient,
Expand Down Expand Up @@ -1138,6 +1139,26 @@ describe("Native desktop SDK interactions", () => {
})
})

test("delivers automation run guidance and task through the Native queue", async () => {
const transport = new FakeNativeTransport()
const client = createDevoClient({ directory: "/repo", transport })
await client.session.create()
const parts = buildAutomationPromptParts("Review today's changes", "Daily review", "memory.md")

await client.session.promptAsync({ sessionId: "session-1", parts })

const requests = transport.requests.filter((request) => request.method === "session/queue/push")
expect(requests).toEqual([{
method: "session/queue/push",
params: {
sessionId: "session-1",
input: [{ type: "text", text: `${parts[0].text}\nReview today's changes` }],
idempotencyKey: expect.any(String),
},
directory: "/repo",
}])
})

test("keeps the session busy after turn/start until turn/completed", async () => {
const transport = new FakeNativeTransport()
const client = createDevoClient({ directory: "/repo", transport })
Expand Down
4 changes: 1 addition & 3 deletions apps/desktop/packages/devo-ai-sdk/src/v2/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1486,9 +1486,7 @@ class NativeClient {
},
discover: async (params: ProviderDiscoverParams): Promise<ProviderDiscoverResult> => {
const result = (await this.requestCanonical("provider/discover", params)) as ProviderDiscoverResult
// Discover mutates the connection model directory; composer selectors
// read model/preferences which must not keep a pre-discover snapshot.
this.invalidateConfigOptionCaches()
// Discovery is read-only. Callers may explicitly save selected results.
return result
},
auth: async () => ({ data: [] }),
Expand Down
Loading