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
4 changes: 2 additions & 2 deletions .deva.example
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,10 @@ EPHEMERAL=false
# Hybrid Agent Setup:
#
# Hybrid is the DEFAULT. deva walks every populated subdir under
# ~/.config/deva/ (claude, codex, gemini, grok) and mounts each agent's
# ~/.config/deva/ (claude, codex, gemini, grok, kimi) 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 on first run.
# ~/.claude, ~/.codex, ~/.gemini, ~/.grok, ~/.kimi-code on first run.
#
# No .deva entries required for the common case. To opt OUT of
# hybrid for a single invocation, pass --config-home DIR to
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
--build-arg CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
--build-arg GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
--build-arg GROK_CLI_VERSION="${{ steps.pins.outputs.grok_cli_version }}" \
--build-arg KIMI_CODE_VERSION="${{ steps.pins.outputs.kimi_code_version }}" \
--build-arg CCX_VERSION="${{ steps.pins.outputs.ccx_version }}" \
--build-arg COPILOT_API_VERSION="${{ steps.pins.outputs.copilot_api_version }}" \
.
Expand All @@ -102,6 +103,7 @@ jobs:
CODEX_VERSION="${{ steps.pins.outputs.codex_version }}" \
GEMINI_CLI_VERSION="${{ steps.pins.outputs.gemini_cli_version }}" \
GROK_CLI_VERSION="${{ steps.pins.outputs.grok_cli_version }}" \
KIMI_CODE_VERSION="${{ steps.pins.outputs.kimi_code_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 All @@ -127,6 +129,7 @@ jobs:
deva.sh codex -Q -- --version
deva.sh gemini -Q -- --version
deva.sh grok -Q -- --version
deva.sh kimi -Q -- --version

- name: Smoke Claude --chrome mount assembly
shell: bash
Expand Down Expand Up @@ -166,6 +169,14 @@ jobs:
set -euo pipefail
./scripts/test-install-agent-tooling.sh

- name: Smoke kimi auth wiring
shell: bash
run: |
set -euo pipefail
DEVA_DOCKER_IMAGE=deva-smoke \
DEVA_DOCKER_TAG=ci \
./scripts/test-kimi-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 @@ -55,6 +55,7 @@ jobs:
codex_version: ${{ steps.versions.outputs.codex_version }}
gemini_cli_version: ${{ steps.versions.outputs.gemini_cli_version }}
grok_cli_version: ${{ steps.versions.outputs.grok_cli_version }}
kimi_code_version: ${{ steps.versions.outputs.kimi_code_version }}
ccx_version: ${{ steps.versions.outputs.ccx_version }}
copilot_api_version: ${{ steps.versions.outputs.copilot_api_version }}
steps:
Expand Down Expand Up @@ -82,6 +83,7 @@ jobs:
- Codex: \`${{ steps.versions.outputs.codex_version }}\`
- Gemini CLI: \`${{ steps.versions.outputs.gemini_cli_version }}\`
- Grok CLI: \`${{ steps.versions.outputs.grok_cli_version }}\`
- Kimi Code: \`${{ steps.versions.outputs.kimi_code_version }}\`
- ccx: \`${{ steps.versions.outputs.ccx_version }}\`
- Copilot API: \`${{ steps.versions.outputs.copilot_api_version }}\`
- Stamp: \`${{ steps.versions.outputs.stamp }}\`
Expand Down Expand Up @@ -143,6 +145,7 @@ jobs:
CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }}
GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }}
GROK_CLI_VERSION=${{ needs.resolve-versions.outputs.grok_cli_version }}
KIMI_CODE_VERSION=${{ needs.resolve-versions.outputs.kimi_code_version }}
CCX_VERSION=${{ needs.resolve-versions.outputs.ccx_version }}
COPILOT_API_VERSION=${{ needs.resolve-versions.outputs.copilot_api_version }}

Expand Down Expand Up @@ -197,6 +200,7 @@ jobs:
CODEX_VERSION=${{ needs.resolve-versions.outputs.codex_version }}
GEMINI_CLI_VERSION=${{ needs.resolve-versions.outputs.gemini_cli_version }}
GROK_CLI_VERSION=${{ needs.resolve-versions.outputs.grok_cli_version }}
KIMI_CODE_VERSION=${{ needs.resolve-versions.outputs.kimi_code_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 @@ -51,6 +51,7 @@ jobs:
codex_version: ${{ steps.pins.outputs.codex_version }}
gemini_cli_version: ${{ steps.pins.outputs.gemini_cli_version }}
grok_cli_version: ${{ steps.pins.outputs.grok_cli_version }}
kimi_code_version: ${{ steps.pins.outputs.kimi_code_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 @@ -86,6 +87,7 @@ jobs:
- Codex: \`${{ steps.pins.outputs.codex_version }}\`
- Gemini CLI: \`${{ steps.pins.outputs.gemini_cli_version }}\`
- Grok CLI: \`${{ steps.pins.outputs.grok_cli_version }}\`
- Kimi Code: \`${{ steps.pins.outputs.kimi_code_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 @@ -150,6 +152,7 @@ jobs:
CODEX_VERSION=${{ needs.load-version-pins.outputs.codex_version }}
GEMINI_CLI_VERSION=${{ needs.load-version-pins.outputs.gemini_cli_version }}
GROK_CLI_VERSION=${{ needs.load-version-pins.outputs.grok_cli_version }}
KIMI_CODE_VERSION=${{ needs.load-version-pins.outputs.kimi_code_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 @@ -206,6 +209,7 @@ jobs:
CODEX_VERSION=${{ needs.load-version-pins.outputs.codex_version }}
GEMINI_CLI_VERSION=${{ needs.load-version-pins.outputs.gemini_cli_version }}
GROK_CLI_VERSION=${{ needs.load-version-pins.outputs.grok_cli_version }}
KIMI_CODE_VERSION=${{ needs.load-version-pins.outputs.kimi_code_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
31 changes: 31 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
- kimi agent (Moonshot Kimi Code CLI, #455): `deva.sh kimi` launches the
official `@moonshot-ai/kimi-code` CLI like claude/codex/gemini/grok.
`oauth` default (device-code `/login`, mounts `~/.kimi-code`) and
`api-key`. kimi reads no API key from the shell — its only shell channel
is the `KIMI_MODEL_*` family — so api-key mode maps `KIMI_CODE_API_KEY`
onto `KIMI_MODEL_{NAME,API_KEY,PROVIDER_TYPE,BASE_URL}` (default model
`k3`, coding endpoint `https://api.kimi.com/coding/v1`; override with
`DEVA_KIMI_MODEL` / `DEVA_KIMI_BASE_URL`). The key is synthesized
in-memory, never written to `config.toml`, so api-key mode mounts no
`~/.kimi-code` and nothing lands on disk. Unlike grok, kimi's npm bin is
a plain symlink (no self-update trampoline), so no binary-pinning guard
is needed. `KIMI_CODE_VERSION` pinned at 0.28.0 through versions.env,
Makefile, Dockerfile(+rust), version-pins/upgrade/update scripts,
ci/nightly/release workflows; tests added (release-utils registry,
version-upgrade mock, install-tooling, and `test-kimi-auth.sh`).

### Fixed
- `--trace` launch killed by `cp: cannot create regular file
'/usr/local/share/ca-certificates/cctrace-mitm.crt': File exists` (#414):
the persistent-container flow runs the entrypoint's `setup_trace_ca`
concurrently — once in PID 1 (`docker run -d ... tail -f /dev/null` boot)
and once in the `docker exec` entrypoint that starts the agent — and GNU
cp creates the destination `O_EXCL`, so the loser of the race died under
`set -e`. The CA install is now serialized with `flock` and skips the
copy when the installed cert is already current. The first cut of the
flock subshell ended on `[ "$VERBOSE" = "true" ] && echo`, whose failed
test became the subshell's exit status and `set -e` killed every
non-verbose traced launch right after the banner; the subshell now
exits 0 explicitly.

## [0.16.0] - 2026-07-14

### Added
Expand Down
15 changes: 15 additions & 0 deletions DEV-LOGS.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,21 @@
- Minimal markdown markers, no unnecessary formatting, minimal emojis.
- Reference issue numbers in the format `#<issue-number>` for easy linking.

# [2026-07-21] Dev Log: traced launches die silently in freshly built images #414
- Why: after rebuilding images, every `--trace` launch died with "failed to launch ephemeral container" and no error.
- What:
- The #414 flock rework ended the CA-install subshell on `[ "$VERBOSE" = "true" ] && echo ...`; with VERBOSE unset the failed test became the subshell's exit status, and under `set -e` the entrypoint died right after the agent banner — silently, only in freshly built images (the entrypoint is baked). Fix: explicit `exit 0` before the subshell closes. Verified A/B: baked image dies, overlay-fixed image runs `deva.sh claude --trace --rm -Q -- --version` end-to-end (cctrace MITM up, trace file written). Rebuild images to pick this up.
- Result: traced launch verified live against the host daemon.

# [2026-07-20] Dev Log: add kimi agent (Moonshot Kimi Code CLI) #455
- Why: Moonshot shipped an official coding-agent CLI (@moonshot-ai/kimi-code, bin `kimi`); deva should launch it like claude/codex/gemini/grok. Traced the grok integration (commit 93a59e0) via ccx as the template.
- What:
- agents/kimi.sh: oauth default (mount ~/.kimi-code; device-code `kimi login`, no browser needed) + api-key; appends `--yolo` (auto-approve tool calls, still allows clarifying questions — not `--auto`) since the container is the sandbox.
- The auth divergence that broke the premise: kimi reads NO API key from the shell (docs: `export KIMI_API_KEY` does nothing; creds only from ~/.kimi-code/config.toml). The one shell channel is the `KIMI_MODEL_*` family, which synthesizes an in-memory provider. So api-key mode maps `KIMI_CODE_API_KEY` -> `KIMI_MODEL_{NAME=k3,API_KEY,PROVIDER_TYPE=kimi,BASE_URL=https://api.kimi.com/coding/v1}` (override via DEVA_KIMI_MODEL/DEVA_KIMI_BASE_URL). Verified end-to-end with a real sk-kim… key: the key auths only against api.kimi.com/coding/v1 (moonshot.ai rejects it), models k3/kimi-for-coding/highspeed, and the key is never persisted to config.toml -> no ~/.kimi-code mount in api-key mode, nothing on disk.
- deva.sh: canonical entry .kimi-code, autolink ~/.kimi-code, KIMI_* env filtering, config-home mkdir/warn/dispatch. Simpler than grok — no default_credential_target_path (env-only key), so no auth.json overlay; kimi's npm bin is a plain symlink to dist/main.mjs (no self-update trampoline, native/ only darwin+win32), so no pin_platform_binary and no update-guard tmpfs.
- KIMI_CODE_VERSION=0.28.0 pinned through versions.env, Makefile, Dockerfile(+rust), version-pins/upgrade/update scripts, release-utils TOOL_REGISTRY, ci/nightly/release workflows, install.sh + install-agent-tooling.sh. Tests: release-utils registry counts, version-upgrade mock (curl fixtures + outage label), install-tooling stub, and new scripts/test-kimi-auth.sh (12 assertions on the KIMI_MODEL_* wiring, redaction, naming, no-mount, overrides, missing-key error).
- Result: deva.sh kimi works in both auth modes (DEVA_NO_DOCKER dry-run + local `kimi -p` through KIMI_MODEL_* verified); full test suite green. Docker image build not verifiable in this container (no daemon); the fan-out is validated by the version tooling + assertions. NOTE: docs/authentication.md documents the unique no-shell-env auth model so users don't hit the same KIMI_CODE_API_KEY confusion.

# [2026-07-10] Dev Log: deva-tmux — tmux as a built-in module, both transports #412
- Why: tmux interaction was four loose scripts with zero deva.sh integration; #406 built the right transport (ssh) but deleted the socat fallback and stayed a loose script. Direction split matters: host->container (deva.sh shell) and agent<->agent (tmux-bridge) keep the sandbox; container->host breaks it and must be opt-in.
- What:
Expand Down
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -212,12 +212,14 @@ ARG CLAUDE_CODE_VERSION=2.1.143
ARG CODEX_VERSION=0.131.0
ARG GEMINI_CLI_VERSION=0.42.0
ARG GROK_CLI_VERSION=0.2.93
ARG KIMI_CODE_VERSION=0.28.0

# Record key tool versions as labels for quick inspection
LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}
LABEL org.opencontainers.image.grok_cli_version=${GROK_CLI_VERSION}
LABEL org.opencontainers.image.kimi_code_version=${KIMI_CODE_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 @@ -12,6 +12,7 @@ ARG CLAUDE_CODE_VERSION=2.1.143
ARG CODEX_VERSION=0.131.0
ARG GEMINI_CLI_VERSION=0.42.0
ARG GROK_CLI_VERSION=0.2.93
ARG KIMI_CODE_VERSION=0.28.0
ARG CCX_VERSION=v0.7.0
Comment on lines 14 to 16
ARG PLAYWRIGHT_VERSION=1.60.0
ARG RUST_TOOLCHAINS="stable"
Expand All @@ -22,6 +23,7 @@ LABEL org.opencontainers.image.claude_code_version=${CLAUDE_CODE_VERSION}
LABEL org.opencontainers.image.codex_version=${CODEX_VERSION}
LABEL org.opencontainers.image.gemini_cli_version=${GEMINI_CLI_VERSION}
LABEL org.opencontainers.image.grok_cli_version=${GROK_CLI_VERSION}
LABEL org.opencontainers.image.kimi_code_version=${KIMI_CODE_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 @@ -35,6 +35,7 @@ CCTRACE_VERSION ?= 0.16.0
CODEX_VERSION ?= 0.131.0
GEMINI_CLI_VERSION ?= 0.42.0
GROK_CLI_VERSION ?= 0.2.93
KIMI_CODE_VERSION ?= 0.28.0
CCX_VERSION ?= v0.1.4
COPILOT_API_VERSION ?= 0ea08febdd7e3e055b03dd298bf57e669500b5c1
PLAYWRIGHT_VERSION ?= 1.60.0
Expand Down Expand Up @@ -84,6 +85,7 @@ AGENT_BUILD_ARGS := \
--build-arg CODEX_VERSION=$(CODEX_VERSION) \
--build-arg GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION) \
--build-arg GROK_CLI_VERSION=$(GROK_CLI_VERSION) \
--build-arg KIMI_CODE_VERSION=$(KIMI_CODE_VERSION) \
--build-arg CCX_VERSION=$(CCX_VERSION)

MAIN_BUILD_ARGS := $(TOOLCHAIN_BUILD_ARGS) $(AGENT_BUILD_ARGS) \
Expand All @@ -107,6 +109,7 @@ VERSION_QUERY_OVERRIDES := \
$(if $(filter command line environment environment\ override override,$(origin CODEX_VERSION)),CODEX_VERSION=$(CODEX_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin GEMINI_CLI_VERSION)),GEMINI_CLI_VERSION=$(GEMINI_CLI_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin GROK_CLI_VERSION)),GROK_CLI_VERSION=$(GROK_CLI_VERSION)) \
$(if $(filter command line environment environment\ override override,$(origin KIMI_CODE_VERSION)),KIMI_CODE_VERSION=$(KIMI_CODE_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 @@ -150,14 +153,16 @@ build-main: build-network-check
prev_codex=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.codex_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_gemini=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.gemini_cli_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_grok=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.grok_cli_version" }}' $(MAIN_IMAGE) 2>/dev/null || true); \
prev_kimi=$$(docker inspect --format='{{ index .Config.Labels "org.opencontainers.image.kimi_code_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"); \
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); tgtK=$$(fmt "$(GROK_CLI_VERSION)"); \
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ] && [ "$$curK" = "$$tgtK" ]; then \
curC=$$(fmt "$$prev_claude"); curX=$$(fmt "$$prev_codex"); curG=$$(fmt "$$prev_gemini"); curK=$$(fmt "$$prev_grok"); curKi=$$(fmt "$$prev_kimi"); \
tgtC=$$(fmt "$(CLAUDE_CODE_VERSION)"); tgtX=$$(fmt "$(CODEX_VERSION)"); tgtG=$$(fmt "$(GEMINI_CLI_VERSION)"); tgtK=$$(fmt "$(GROK_CLI_VERSION)"); tgtKi=$$(fmt "$(KIMI_CODE_VERSION)"); \
if [ "$$curC" = "$$tgtC" ] && [ "$$curX" = "$$tgtX" ] && [ "$$curG" = "$$tgtG" ] && [ "$$curK" = "$$tgtK" ] && [ "$$curKi" = "$$tgtKi" ]; then \
echo "Claude: $$tgtC (no change)"; \
echo "Codex: $$tgtX (no change)"; \
echo "Gemini: $$tgtG (no change)"; \
echo "Grok: $$tgtK (no change)"; \
echo "Kimi: $$tgtKi (no change)"; \
echo "Already up-to-date"; \
else \
if [ "$$curC" = "$$tgtC" ]; then \
Expand All @@ -180,6 +185,11 @@ build-main: build-network-check
else \
echo "Grok: $$curK -> $$tgtK"; \
fi; \
if [ "$$curKi" = "$$tgtKi" ]; then \
echo "Kimi: $$tgtKi (no change)"; \
else \
echo "Kimi: $$curKi -> $$tgtKi"; \
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 @@ -439,6 +449,7 @@ help:
@echo " CODEX_VERSION Codex CLI version (default: $(CODEX_VERSION))"
@echo " GEMINI_CLI_VERSION Gemini CLI version (default: $(GEMINI_CLI_VERSION))"
@echo " GROK_CLI_VERSION Grok CLI version (default: $(GROK_CLI_VERSION))"
@echo " KIMI_CODE_VERSION Kimi Code CLI version (default: $(KIMI_CODE_VERSION))"
@echo " CCX_VERSION Atlas CLI version (default: $(CCX_VERSION))"
@echo " PLAYWRIGHT_VERSION Playwright version (default: $(PLAYWRIGHT_VERSION))"
@echo " RUST_TOOLCHAINS Rust toolchains to install (default: $(RUST_TOOLCHAINS))"
Expand Down
Loading
Loading