Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .deva.example
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ EPHEMERAL=false
# Hybrid Agent Setup:
#
# Hybrid is the DEFAULT. deva walks every populated subdir under
# ~/.config/deva/ (claude, codex, gemini, grok, kimi, opencode, pi, dsh) and mounts each agent's
# ~/.config/deva/ (claude, codex, gemini, grok, kimi, opencode, pi, dsh, cursor) and mounts each agent's
# canonical entries into the container. Populated = you either
# hand-created the subdir or autolink symlinked it from legacy
# ~/.claude, ~/.codex, ~/.gemini, ~/.grok, ~/.kimi-code, ~/.pi, ~/.dsh, or the
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ jobs:
--build-arg OPENCODE_VERSION="${{ steps.pins.outputs.opencode_version }}" \
--build-arg PI_CODING_AGENT_VERSION="${{ steps.pins.outputs.pi_coding_agent_version }}" \
--build-arg DSH_VERSION="${{ steps.pins.outputs.dsh_version }}" \
--build-arg CURSOR_CLI_VERSION="${{ steps.pins.outputs.cursor_cli_version }}" \
--build-arg CCX_VERSION="${{ steps.pins.outputs.ccx_version }}" \
--build-arg COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
.
Expand All @@ -113,6 +114,7 @@ jobs:
OPENCODE_VERSION="${{ steps.pins.outputs.opencode_version }}" \
PI_CODING_AGENT_VERSION="${{ steps.pins.outputs.pi_coding_agent_version }}" \
DSH_VERSION="${{ steps.pins.outputs.dsh_version }}" \
CURSOR_CLI_VERSION="${{ steps.pins.outputs.cursor_cli_version }}" \
CCX_VERSION="${{ steps.pins.outputs.ccx_version }}" \
COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
PLAYWRIGHT_VERSION="${{ steps.pins.outputs.playwright_version }}" \
Expand Down Expand Up @@ -142,6 +144,7 @@ jobs:
deva.sh opencode -Q -- --version
deva.sh pi -Q -- --version
deva.sh dsh -Q -- --version
deva.sh cursor -Q -- --version

- name: Smoke Claude --chrome mount assembly
shell: bash
Expand Down Expand Up @@ -219,6 +222,14 @@ jobs:
DEVA_DOCKER_TAG=ci \
./scripts/test-dsh-auth.sh

- name: Smoke cursor auth wiring
shell: bash
run: |
set -euo pipefail
DEVA_DOCKER_IMAGE=deva-smoke \
DEVA_DOCKER_TAG=ci \
./scripts/test-cursor-auth.sh

- name: Smoke version targets
shell: bash
run: |
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/nightly-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
opencode_version: ${{ steps.versions.outputs.opencode_version }}
pi_coding_agent_version: ${{ steps.versions.outputs.pi_coding_agent_version }}
dsh_version: ${{ steps.versions.outputs.dsh_version }}
cursor_cli_version: ${{ steps.versions.outputs.cursor_cli_version }}
ccx_version: ${{ steps.versions.outputs.ccx_version }}
copilot_api_version: ${{ steps.versions.outputs.copilot_api_version }}
steps:
Expand Down Expand Up @@ -90,6 +91,7 @@ jobs:
- opencode: \`${{ steps.versions.outputs.opencode_version }}\`
- pi: \`${{ steps.versions.outputs.pi_coding_agent_version }}\`
- dsh: \`${{ steps.versions.outputs.dsh_version }}\`
- cursor: \`${{ steps.versions.outputs.cursor_cli_version }}\`
- ccx: \`${{ steps.versions.outputs.ccx_version }}\`
- Copilot API: \`${{ steps.versions.outputs.copilot_api_version }}\`
- Stamp: \`${{ steps.versions.outputs.stamp }}\`
Expand Down Expand Up @@ -155,6 +157,7 @@ jobs:
OPENCODE_VERSION=${{ needs.resolve-versions.outputs.opencode_version }}
PI_CODING_AGENT_VERSION=${{ needs.resolve-versions.outputs.pi_coding_agent_version }}
DSH_VERSION=${{ needs.resolve-versions.outputs.dsh_version }}
CURSOR_CLI_VERSION=${{ needs.resolve-versions.outputs.cursor_cli_version }}
CCX_VERSION=${{ needs.resolve-versions.outputs.ccx_version }}
COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }}

Expand Down Expand Up @@ -213,6 +216,7 @@ jobs:
OPENCODE_VERSION=${{ needs.resolve-versions.outputs.opencode_version }}
PI_CODING_AGENT_VERSION=${{ needs.resolve-versions.outputs.pi_coding_agent_version }}
DSH_VERSION=${{ needs.resolve-versions.outputs.dsh_version }}
CURSOR_CLI_VERSION=${{ needs.resolve-versions.outputs.cursor_cli_version }}
CCX_VERSION=${{ needs.resolve-versions.outputs.ccx_version }}
PLAYWRIGHT_VERSION=${{ needs.load-version-pins.outputs.playwright_version }}
RUST_TOOLCHAINS=${{ needs.load-version-pins.outputs.rust_toolchains }}
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
opencode_version: ${{ steps.pins.outputs.opencode_version }}
pi_coding_agent_version: ${{ steps.pins.outputs.pi_coding_agent_version }}
dsh_version: ${{ steps.pins.outputs.dsh_version }}
cursor_cli_version: ${{ steps.pins.outputs.cursor_cli_version }}
ccx_version: ${{ steps.pins.outputs.ccx_version }}
copilot_api_version: ${{ steps.pins.outputs.copilot_api_version }}
playwright_version: ${{ steps.pins.outputs.playwright_version }}
Expand Down Expand Up @@ -95,6 +96,7 @@ jobs:
- opencode: \`${{ steps.pins.outputs.opencode_version }}\`
- pi: \`${{ steps.pins.outputs.pi_coding_agent_version }}\`
- dsh: \`${{ steps.pins.outputs.dsh_version }}\`
- cursor: \`${{ steps.pins.outputs.cursor_cli_version }}\`
- ccx: \`${{ steps.pins.outputs.ccx_version }}\`
- Copilot API: \`${{ steps.pins.outputs.copilot_api_version }}\`
- Playwright: \`${{ steps.pins.outputs.playwright_version }}\`
Expand Down Expand Up @@ -163,6 +165,7 @@ jobs:
OPENCODE_VERSION=${{ needs.load-version-pins.outputs.opencode_version }}
PI_CODING_AGENT_VERSION=${{ needs.load-version-pins.outputs.pi_coding_agent_version }}
DSH_VERSION=${{ needs.load-version-pins.outputs.dsh_version }}
CURSOR_CLI_VERSION=${{ needs.load-version-pins.outputs.cursor_cli_version }}
CCX_VERSION=${{ needs.load-version-pins.outputs.ccx_version }}
COPILOT_API_VERSION=${{ needs.load-version-pins.outputs.copilot_api_version }}

Expand Down Expand Up @@ -223,6 +226,7 @@ jobs:
OPENCODE_VERSION=${{ needs.load-version-pins.outputs.opencode_version }}
PI_CODING_AGENT_VERSION=${{ needs.load-version-pins.outputs.pi_coding_agent_version }}
DSH_VERSION=${{ needs.load-version-pins.outputs.dsh_version }}
CURSOR_CLI_VERSION=${{ needs.load-version-pins.outputs.cursor_cli_version }}
CCX_VERSION=${{ needs.load-version-pins.outputs.ccx_version }}
PLAYWRIGHT_VERSION=${{ needs.load-version-pins.outputs.playwright_version }}
RUST_TOOLCHAINS=${{ needs.load-version-pins.outputs.rust_toolchains }}
Expand Down
24 changes: 24 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
scripts/test-help-surface.sh pins the routing (24 asserts)

### Added
- Cursor CLI (cursor-agent) as the 9th agent: `deva.sh cursor` (#557).
Pin story solved without the installer: cursor.com/install hardcodes
the version and has no pin hook, but the download URL is
deterministic, so the image fetches
downloads.cursor.com/lab/<CURSOR_CLI_VERSION>/linux/<arch>/… directly
and lays it out installer-style under
~/.local/share/cursor-agent/versions/; the write bit is then stripped
from that tree, which starves the CLI's silent startup self-update
(updaters fight pins). Version format is YYYY.MM.DD-hash; `make
versions-up`/`versions-pin` resolve latest by parsing the installer
script (new cursor-installer registry type). Default `oauth` keeps
state in the per-agent config home only — deliberately NO host
~/.cursor autolink or fallback mount: that dir is the Cursor IDE's
state (worktrees, per-project chats), and macOS keeps CLI auth in
the keychain, so there is nothing portable to carry in; first login
runs in-container (`cursor-agent login`, NO_OPEN_BROWSER=1 prints
the URL). Two canonical entries on Linux: `.cursor` (cli-config,
projects) + `.config/cursor` (auth.json — the file store keeps auth
there even when config lands in `.cursor`). `--auth-with api-key`
passes CURSOR_API_KEY env-only, mounts nothing, blank-overlays
auth.json. YOLO via `--force`; only the `cursor-agent` bin is linked
(the official installer also squats `agent` — too generic for a
container with nine CLIs). scripts/test-cursor-auth.sh (16 asserts).
`--trace` rejected until cctrace ships a cursor profile
- dsh (deepseek-ai/deepseek-harness, DeepSeek's official agent harness,
launched 2026-08-13) as the 8th agent: `deva.sh dsh` (#556). One
dot-dir — everything persists under `~/.dsh` (`$DSH_HOME`, pinned by
Expand Down
5 changes: 5 additions & 0 deletions DEV-LOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@
- Minimal markdown markers, no unnecessary formatting, minimal emojis.
- Reference issue numbers in the format `#<issue-number>` for easy linking.

# [2026-08-13] Dev Log: cursor as the 9th agent #557
- Why: most-requested absentee (#557). Fits the deva model exactly; the only hard part was pinning a CLI whose installer has no version hook.
- What: agents/cursor.sh (oauth keeps state in config-home only, api-key passes CURSOR_API_KEY env-only + blank-overlays .config/cursor/auth.json), install_cursor_agent in install-agent-tooling.sh (deterministic tarball URL downloads.cursor.com/lab/<ver>/linux/<arch>, installer-style layout, then chmod a-w the versions tree to starve the silent startup self-updater), new cursor-installer type in release-utils/update-version-pins (latest = parse the installer script; date = the version's own YYYY.MM.DD prefix), CURSOR_CLI_VERSION=2026.08.11-e8db854 through the full pin plumbing, deva.sh registration, scripts/test-cursor-auth.sh (16 asserts).
- Result: 9 agents. Decisions worth remembering: no host ~/.cursor autolink ever — that's the IDE's state dir, not a CLI home, and mac CLI auth lives in the keychain so the mount carries nothing portable; only the cursor-agent bin is linked (installer also squats `agent`, too generic here); Linux auth quirk pinned in a comment — auth.json goes to .config/cursor even when cli-config lands in .cursor. test-install-agent-tooling now fakes the cursor tarball via the curl stub. --trace deferred (no cctrace cursor profile).

# [2026-08-13] Dev Log: dsh as the 8th agent #556
- Why: DeepSeek shipped its official harness (deepseek-ai/deepseek-harness) on launch day; developer preview with promised breaking changes is exactly what a container posture is for.
- What: agents/dsh.sh (default `credentials` mounts ~/.dsh rw; api-key passes DEEPSEEK_API_KEY, mounts nothing — dsh resolves env BEFORE .credentials.yaml, reverse of pi, so no blank overlay and host-key scrub matters in credentials mode), deva.sh registration (auth tag, dsh_version label, mounts, autolink ~/.dsh, status, env scrub), DSH_PERMISSION_MODE=danger-full-access + DSH_HOME pinned at launch, image pin DSH_VERSION=0.1.0-rc.6 through versions.env/Makefile/scripts/CI, scripts/test-dsh-auth.sh (13 asserts). Traps dodged: DSH_HOME default is ~/.dsh not `~/n` (issue text was a docs misread); DSH_* is scrubbed from project-discovered env by dsh itself, so the permission mode must ride the invoking env — which deva's -e injection is; no updater exists, nothing to neutralize.
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ ARG KIMI_CODE_VERSION=0.28.0
ARG OPENCODE_VERSION=1.18.14
ARG PI_CODING_AGENT_VERSION=0.84.1
ARG DSH_VERSION=0.1.0-rc.6
ARG CURSOR_CLI_VERSION=2026.08.11-e8db854

# Record key tool versions as labels for quick inspection
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
Expand All @@ -226,6 +227,7 @@ LABEL org.opencontainers.image.kimi_code_version=${KIMI_CODE_VERSION}
LABEL org.opencontainers.image.opencode_version=${OPENCODE_VERSION}
LABEL org.opencontainers.image.pi_coding_agent_version=${PI_CODING_AGENT_VERSION}
LABEL org.opencontainers.image.dsh_version=${DSH_VERSION}
LABEL org.opencontainers.image.cursor_cli_version=${CURSOR_CLI_VERSION}

ARG CCX_VERSION=v0.7.0

Expand Down
2 changes: 2 additions & 0 deletions Dockerfile.rust
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ ARG KIMI_CODE_VERSION=0.28.0
ARG OPENCODE_VERSION=1.18.14
ARG PI_CODING_AGENT_VERSION=0.84.1
ARG DSH_VERSION=0.1.0-rc.6
ARG CURSOR_CLI_VERSION=2026.08.11-e8db854
ARG CCX_VERSION=v0.7.0
ARG PLAYWRIGHT_VERSION=1.60.0
ARG RUST_TOOLCHAINS="stable"
Expand All @@ -30,6 +31,7 @@ LABEL org.opencontainers.image.kimi_code_version=${KIMI_CODE_VERSION}
LABEL org.opencontainers.image.opencode_version=${OPENCODE_VERSION}
LABEL org.opencontainers.image.pi_coding_agent_version=${PI_CODING_AGENT_VERSION}
LABEL org.opencontainers.image.dsh_version=${DSH_VERSION}
LABEL org.opencontainers.image.cursor_cli_version=${CURSOR_CLI_VERSION}
LABEL org.opencontainers.image.ccx_version=${CCX_VERSION}
LABEL org.opencontainers.image.playwright_version=${PLAYWRIGHT_VERSION}

Expand Down
17 changes: 14 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ KIMI_CODE_VERSION ?= 0.28.0
OPENCODE_VERSION ?= 1.18.14
PI_CODING_AGENT_VERSION ?= 0.84.1
DSH_VERSION ?= 0.1.0-rc.6
CURSOR_CLI_VERSION ?= 2026.08.11-e8db854
CCX_VERSION ?= v0.1.4
COPILOT_API_VERSION ?= 0ea08febdd7e3e055b03dd298bf57e669500b5c1
PLAYWRIGHT_VERSION ?= 1.60.0
Expand Down Expand Up @@ -96,6 +97,7 @@ AGENT_BUILD_ARGS := \
--build-arg OPENCODE_VERSION=$(OPENCODE_VERSION) \
--build-arg PI_CODING_AGENT_VERSION=$(PI_CODING_AGENT_VERSION) \
--build-arg DSH_VERSION=$(DSH_VERSION) \
--build-arg CURSOR_CLI_VERSION=$(CURSOR_CLI_VERSION) \
--build-arg CCX_VERSION=$(CCX_VERSION)

MAIN_BUILD_ARGS := $(TOOLCHAIN_BUILD_ARGS) $(AGENT_BUILD_ARGS) \
Expand Down Expand Up @@ -126,6 +128,7 @@ VERSION_QUERY_OVERRIDES := \
$(if $(filter command line environment environment\ override override,$(origin OPENCODE_VERSION)),OPENCODE_VERSION=$(OPENCODE_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin PI_CODING_AGENT_VERSION)),PI_CODING_AGENT_VERSION=$(PI_CODING_AGENT_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin DSH_VERSION)),DSH_VERSION=$(DSH_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin CURSOR_CLI_VERSION)),CURSOR_CLI_VERSION=$(CURSOR_CLI_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin CCX_VERSION)),CCX_VERSION=$(CCX_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin COPILOT_API_VERSION)),COPILOT_API_VERSION=$(COPILOT_API_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin PLAYWRIGHT_VERSION)),PLAYWRIGHT_VERSION=$(PLAYWRIGHT_VERSION)) \
Expand Down Expand Up @@ -175,10 +178,11 @@ build-main: build-network-check
prev_opencode=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.opencode_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_pi=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.pi_coding_agent_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_dsh=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.dsh_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_cursor=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.cursor_cli_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
fmt() { v="$$1"; if [ -z "$$v" ] || [ "$$v" = "<no value>" ]; then echo "-"; else case "$$v" in v*) echo "$$v";; *) echo "v$$v";; esac; fi; }; \
curC=$$(fmt "$$prev_claude"); curX=$$(fmt "$$prev_codex"); curG=$$(fmt "$$prev_gemini"); curK=$$(fmt "$$prev_grok"); curKi=$$(fmt "$$prev_kimi"); curO=$$(fmt "$$prev_opencode"); curP=$$(fmt "$$prev_pi"); curD=$$(fmt "$$prev_dsh"); \
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); tgtK=$$(fmt "$(GROK_CLI_VERSION)"); tgtKi=$$(fmt "$(KIMI_CODE_VERSION)"); tgtO=$$(fmt "$(OPENCODE_VERSION)"); tgtP=$$(fmt "$(PI_CODING_AGENT_VERSION)"); tgtD=$$(fmt "$(DSH_VERSION)"); \
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ] && [ "$$curK" = "$$tgtK" ] && [ "$$curKi" = "$$tgtKi" ] && [ "$$curO" = "$$tgtO" ] && [ "$$curP" = "$$tgtP" ] && [ "$$curD" = "$$tgtD" ]; then \
curC=$$(fmt "$$prev_claude"); curX=$$(fmt "$$prev_codex"); curG=$$(fmt "$$prev_gemini"); curK=$$(fmt "$$prev_grok"); curKi=$$(fmt "$$prev_kimi"); curO=$$(fmt "$$prev_opencode"); curP=$$(fmt "$$prev_pi"); curD=$$(fmt "$$prev_dsh"); curCu=$$(fmt "$$prev_cursor"); \
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); tgtK=$$(fmt "$(GROK_CLI_VERSION)"); tgtKi=$$(fmt "$(KIMI_CODE_VERSION)"); tgtO=$$(fmt "$(OPENCODE_VERSION)"); tgtP=$$(fmt "$(PI_CODING_AGENT_VERSION)"); tgtD=$$(fmt "$(DSH_VERSION)"); tgtCu=$$(fmt "$(CURSOR_CLI_VERSION)"); \
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ] && [ "$$curK" = "$$tgtK" ] && [ "$$curKi" = "$$tgtKi" ] && [ "$$curO" = "$$tgtO" ] && [ "$$curP" = "$$tgtP" ] && [ "$$curD" = "$$tgtD" ] && [ "$$curCu" = "$$tgtCu" ]; then \
echo "Claude: $$tgtC (no change)"; \
echo "Codex: $$tgtX (no change)"; \
echo "Gemini: $$tgtG (no change)"; \
Expand All @@ -187,6 +191,7 @@ build-main: build-network-check
echo "opencode: $$tgtO (no change)"; \
echo "pi: $$tgtP (no change)"; \
echo "dsh: $$tgtD (no change)"; \
echo "cursor: $$tgtCu (no change)"; \
echo "Already up-to-date"; \
else \
if [ "$$curC" = "$$tgtC" ]; then \
Expand Down Expand Up @@ -229,6 +234,11 @@ build-main: build-network-check
else \
echo "dsh: $$curD -> $$tgtD"; \
fi; \
if [ "$$curCu" = "$$tgtCu" ]; then \
echo "cursor: $$tgtCu (no change)"; \
else \
echo "cursor: $$curCu -> $$tgtCu"; \
fi; \
fi
@echo "Hint: override via GO_VERSION=... CLAUDE_CODE_VERSION=... or run 'make versions-pin'"
docker build $(DOCKER_BUILD_FLAGS) -f $(DOCKERFILE) $(MAIN_BUILD_ARGS) -t $(MAIN_IMAGE) .
Expand Down Expand Up @@ -530,6 +540,7 @@ help:
@echo " OPENCODE_VERSION opencode CLI version (default: $(OPENCODE_VERSION))"
@echo " PI_CODING_AGENT_VERSION pi CLI version (default: $(PI_CODING_AGENT_VERSION))"
@echo " DSH_VERSION dsh CLI version (default: $(DSH_VERSION))"
@echo " CURSOR_CLI_VERSION Cursor CLI version (default: $(CURSOR_CLI_VERSION))"
@echo " CCX_VERSION Atlas CLI version (default: $(CCX_VERSION))"
@echo " PLAYWRIGHT_VERSION Playwright version (default: $(PLAYWRIGHT_VERSION))"
@echo " CLOAKBROWSER_WRAPPER_VERSION CloakBrowser npm wrapper version (default: $(CLOAKBROWSER_WRAPPER_VERSION))"
Expand Down
Loading
Loading