scv-cli 0.3.1

A small, extensible terminal agent runtime with a TUI and headless server
# Release and Compatibility

Status: final design

The current workspace release is `0.3.1`. All crates share that version, and
dependencies between workspace packages use exact `=0.3.1` pins.

SCV supports the latest patch release of stable Rust 1.88 or newer on:

- macOS 13 or newer on Apple Silicon and x86-64;
- glibc-based Linux on x86-64 and ARM64.

The release workflow builds and tests four target archives:

- `scv-aarch64-apple-darwin.tar.gz`;
- `scv-x86_64-apple-darwin.tar.gz`;
- `scv-aarch64-unknown-linux-gnu.tar.gz`;
- `scv-x86_64-unknown-linux-gnu.tar.gz`.

Each archive contains `scv`, `scv-server`, `README.md`, `LICENSE`, and
`NOTICE`. Checksums are published beside the archives. Release builds use Cargo
locked mode. The project does not ship a curl-to-shell installer.

SCV is licensed under the Apache License 2.0. The root `LICENSE` contains the
unmodified Apache 2.0 license text, `NOTICE` identifies SCV and any required
third-party notices, and the workspace and every published Cargo package set
`license = "Apache-2.0"`. Dependency license checks reject packages whose terms
are incompatible with Apache-2.0 distribution.

The root `README.md` is the installation and quick-start contract. It includes
prerequisites, provider configuration, source build, binary usage, safety
limits, extension entry points, development checks, architecture links,
contribution guidance, and license information.

Installed clients can update with `scv update`. The command uses crates.io by
default, accepts a Cargo index override through `[update].index_url`,
`SCV_CARGO_INDEX_URL`, or `--index-url`, and installs the published `scv-cli`
binary through Cargo. It restarts an active systemd user daemon after
installation. A foreground `scv run` daemon requires an explicit restart.
Existing TUI clients reconnect and start fresh sessions without restoring
server history or automatically replaying submitted or queued work.

Multiple SCV profiles may run concurrently. Select one with `--scv-home` or
`SCV_HOME`; each profile has an independent socket, systemd user unit, provider
configuration, model selection, credentials, channel state, and nested-agent
state. Custom profile selectors are persisted by `scv start`/`restart`, and
`scv update` restarts only the selected daemon.

Before enabling supervised WeChat accounts, stop any `0.1.9` standalone bridge
processes manually: they do not honor the new account locks. Legacy credentials
and unbound delivery state are loaded conservatively; changing an account's
identity or API origin requires explicit logout before login. See
[channel identity and durable state](channels.md#identity-and-durable-state).

## Upgrading to 0.3.1

`0.3.1` keeps the instance layout (`CONFIG_LAYOUT` 1) and protocol version 3,
so a planned restart from `0.3.0` checks the new release and can roll it back.
Configuration is unchanged.

What changes for a person running SCV:

- **One `agent` tool delegates to every agent.** The model calls `agent` with
  the agent's name (`claude`, `codex`, `grok`, `dsh`, `pi`, or `scv`) instead
  of one `agent_<name>` tool per agent; the old names are gone, with no
  aliases. `agent_wait`, `agent_status`, and `agent_cancel` keep their names.
  A call that names no agent uses the first offered agent in `[agent] prefer`.
  Update skills, `AGENTS.md` files, and prompts that name `agent_codex` or
  `agent_claude` to say the `agent` tool with agent `codex` or `claude`.
  DeepSeek Harness over ACP no longer takes `model` or `effort`, which its
  configuration already rejected.
- **SCV's own WeChat messages** (notices, busy and failure replies, and
  questions) go out in a Markdown code block that starts with `system msg:`.
  Model answers are sent exactly as written, and Feishu is unchanged.
- **Planned restarts** stop waiting for a live delegated agent that is between
  turns, but keep waiting while a nested SCV's own background jobs run or wait
  to be reported. The daemon that schedules a restart applies its own rules,
  so the restart into `0.3.1` still follows those of `0.3.0`.
- **`scv status`** counts live agents between turns as idle
  (`Delegations: 1 running, 2 idle, …`).

A rollback to `0.3.0` reads everything `0.3.1` saves; its notice of stopped
background jobs names a job `0.3.1` recorded as `agent` instead of the agent.

What changes for code that embeds SCV's crates:

- `JobChange` has an `agent` field, and its `tool` is `agent` for jobs the
  `agent` tool starts. Readers fall back to the `agent_` prefix of `tool` for
  jobs from older releases.
- Daemon status adds `idle` to the delegation summary, and
  `idle_since_unix_seconds` and `background_jobs` to each delegation entry.

## Upgrading to 0.3.0

`0.3.0` keeps the instance layout (`CONFIG_LAYOUT` 1) and protocol version 3,
so a planned restart from `0.2.1` checks the new release and can roll it back,
and existing credentials, delivery state, and settings are read unchanged. A
running TUI still needs a restart after the update.

What changes for a person running SCV:

- **Chat channels answer only the account owner by default.** An account
  table without `senders` answers only its owner; messages from anyone else
  are checkpointed with no reply and no model turn. Set
  `senders = "anyone"` in `[channels.<channel>.<account>]` (or run
  `scv channels run <channel> --senders anyone`) to answer every sender
  again. An account whose sign-in recorded no owner answers nobody;
  `scv channels status` shows a `Note:` line for it. A release before `0.3.0`
  refuses an account table that sets `senders`, so remove the key before
  rolling back.
- **Voice messages without a platform transcript** (every Feishu voice
  message, and a WeChat one whose transcript is missing) get the fixed reply
  "SCV cannot listen to voice messages yet. Please type your message
  instead." instead of a model turn.
- **`scv confirm`** asks the owner a yes-or-no question in chat, and the
  feature flow's `publish.sh` uses it when an agent SCV delegated to
  publishes. The daemon and CLI that ask must both be `0.3.0` or newer, so
  `0.3.0` itself is published from a terminal.
- **`[agents.<name>]`** accepts `model` and `effort` defaults for the work
  its `use_for` matches.
- **Log targets** of the WeChat and Feishu transports are now
  `scv_channels::wechat` and `scv_channels::feishu`; update any `RUST_LOG`
  filter that names `scv_clawbot` or `scv_feishu`.

What changes for code that embeds SCV's crates:

- `scv-clawbot` and `scv-feishu` are the `wechat` and `feishu` modules of
  `scv-channels`, behind Cargo features of the same names (both on by
  default), with one `Channel` trait and `scv_channels::run` for every
  account. The old crates' final `0.3.0` releases contain no code.
- `scv-server` no longer exports the CLI's administration helpers, and every
  crate takes the instance `Layout` explicitly instead of reading `SCV_HOME`.
- Errors are typed: `scv_protocol::ErrorCode` and `ToolErrorKind`,
  `scv_core::ToolError { kind, message }` and `ToolOutput::failure`, and
  `scv_client::ControlError`. Background jobs report typed `JobStatus` and
  `JobChange` values on `tool.completed.jobs`.
- Items no other crate uses are no longer `pub`.

## Upgrading to 0.2.0

`0.2.0` changes where an instance keeps its files, as described in
[instance layout](configuration.md#instance-layout), and reads only the new
layout: it moves nothing itself. A daemon started on an old home finds no
channel accounts or agent sign-ins, logs a warning for each old path, and
`scv config show` lists them under "Not used by SCV". Move them once, with the
daemon stopped, for each instance home (`~/.scv` and any `--scv-home`):

```bash
scv stop                                   # or: systemctl --user stop scv.service
cp -a ~/.scv ~/.scv.bak-0.1                # keep a copy until 0.2.0 works
cd ~/.scv
mv adapters agents                         # delegated agents' homes
mkdir -m 700 -p credentials state/channels
for channel in wechat feishu; do
  [ -d channels/$channel/accounts ] && mv channels/$channel/accounts credentials/$channel
  [ -d channels/$channel/state ] && mv channels/$channel/state state/channels/$channel
done
[ -d run/delegations ] && mv run/delegations state/
[ -d run/conversations ] && mv run/conversations state/
rm -f server.sock server.lock              # recreated under state/
```

Then write each `channels/<channel>/settings/<account>.json` as a table in
`config.toml` and remove the old `channels/` and `run/` directories. For
example `{"enabled":true,"workspace":"/srv/work","remote_tools":"owner"}` for
`wechat/default` becomes:

```toml
[channels.wechat.default]
enabled = true
workspace = "/srv/work"
remote_tools = "owner"
```

The nested SCV's home moves with `agents/` (`agents/scv`); inside it, rename
its own `adapters` to `agents` too. Old lock files (`channels/*/locks`,
`channels/*/transactions`) are not needed. State from before `0.1.35` lives in
`clawbot/` rather than `channels/wechat/`, with the same subdirectories.
Start the daemon with `scv start --workspace ...` and check `scv config show`
and `scv channels status`. To go back, stop the daemon, restore the copy, and
install the earlier release.

## Publication and checks

The end-to-end landing flow for this repository is the `feature-flow` agent
skill at `.agents/skills/feature-flow/SKILL.md`. Codex reads it from
`.agents/skills`, and Claude Code from the `.claude/skills` symlink to the same
directory. The flow develops in a sibling worktree and passes the checks
below. It lands each change on `origin/main` as one squashed commit, through a
`gh` pull request when `gh` is signed in or a fast-forward push otherwise, then
publishes, installs the release, and restarts the local daemon. Its scripts
cover the steps that are easy to get wrong:

- `publish.sh`: a resumable publish in dependency order; run by an agent SCV
  delegated to, it first asks the owner yes or no in chat (`scv confirm`)
  and publishes only on yes. An installed `scv` older than 0.3.0 cannot ask,
  so the release that introduces `scv confirm` is published from a terminal;
- `deploy.sh`: keep the running binary as `<binary>.prev`, install, and ask
  the daemon to restart when idle (`scv restart --when-idle`), which checks
  and, on failure, rolls back the release; from a terminal it then waits for
  the new version and connected accounts. A daemon older than that (0.2.0
  and earlier) is restarted by the script itself, without the watchdog;
- `host.sh`: run landing commands from agents that SCV started with a private
  home.

Publish from a clean verified checkout after authenticating with `cargo login`.
Wait for each dependency version to become available before publishing its
dependents:

```bash
cargo publish --locked -p scv-core
cargo publish --locked -p scv-protocol
cargo publish --locked -p scv-client
cargo publish --locked -p scv-provider-openai
cargo publish --locked -p scv-tools
cargo publish --locked -p scv-channels
cargo publish --locked -p scv-server
cargo publish --locked -p scv-tui
cargo publish --locked -p scv-cli
```

Required checks remain:

```bash
cargo fmt --check
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo test --workspace --locked
cargo deny check advisories bans licenses sources
cargo build --release --locked
git diff --check
```

`cargo-deny` is required in CI and runs locally when installed. These commands
are release requirements, not a record of a successful run.

## Compatibility policy

- The JSONL protocol used by both Unix sockets and stdio is versioned
  independently from the crate version; version 2 added daemon management and
  version 3 added `tool.progress`. Clients and the server share one binary, so a
  running TUI from an older release must be restarted after an update.
- Additive object fields do not change the protocol version.
- Removing a field, changing its meaning, or changing message ordering requires
  a protocol version increase.
- Configuration rejects unknown keys in v0.x so misspellings do not silently
  weaken behavior.
- Rust traits are extension seams but do not promise a stable third-party ABI
  before 1.0.
- Linux and macOS are release-gated; other platforms are best effort until they
  join the CI matrix.