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
46 changes: 46 additions & 0 deletions .github/workflows/probe-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,43 @@ jobs:
mkdir -p "$HOME"
cargo test --locked -- --test-threads=4

core-only:
name: Linux x64 core without history build + test
# The build relay ships: the messaging runtime alone, with no
# `ai-hist`/relayhistory dependency (see docs/probe-move.md).
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
- uses: Swatinem/rust-cache@49a0bdc70d2e1b713ca9e2869b211fcce03d3c1c # v2
with:
workspaces: crates/probe
key: core-only
cache-bin: false
- name: No relayhistory in the dependency tree
run: |
tree="$(cargo tree --locked --no-default-features --features production-binaries -e normal,build)"
if grep -qE '(^|[^a-z-])(ai-hist|history-provider-sources) v' <<<"$tree"; then
echo "::error::The core without the history feature depends on relayhistory."
exit 1
fi
- name: Build
run: cargo build --locked --no-default-features --features production-binaries
- name: Contract matches the history build
run: |
cargo run --quiet --locked --no-default-features --features production-binaries \
--bin relay-contract-codegen -- --out "$RUNNER_TEMP/CoreContract.swift"
diff -u contract/CoreContract.swift "$RUNNER_TEMP/CoreContract.swift"
- name: Test
run: |
export RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
export HOME="$RUNNER_TEMP/probe-home"
mkdir -p "$HOME"
cargo test --locked --no-default-features --features production-binaries -- --test-threads=4

macos:
name: macOS arm64 build + test
runs-on: macos-latest
Expand All @@ -110,6 +147,9 @@ jobs:
cache-bin: false
- name: Build
run: cargo build --locked
# The core relay ships, without session history (see core-only).
- name: Build without history
run: cargo build --locked --no-default-features --features production-binaries
- name: Test
run: |
# Keep the toolchain where it was installed before hiding HOME.
Expand Down Expand Up @@ -144,3 +184,9 @@ jobs:
# close to Windows' 260-character limit.
CARGO_HOME: C:\ch
run: cargo build --locked
# The core relay ships, without session history (see core-only).
- name: Build without history
shell: bash
env:
CARGO_HOME: C:\ch
run: cargo build --locked --no-default-features --features production-binaries
58 changes: 54 additions & 4 deletions crates/probe/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,61 @@ name = "relay-contract-codegen"
path = "src/bin/relay-contract-codegen.rs"
required-features = ["production-binaries"]

# Tests of session history (capture, the upload queue, RelayHistory
# delivery); they need the `history` feature.
[[test]]
name = "build_retry"
required-features = ["history"]

[[test]]
name = "delivery_worker"
required-features = ["history"]

[[test]]
name = "destination"
required-features = ["history"]

[[test]]
name = "durable_delivery"
required-features = ["history"]

[[test]]
name = "history_flush"
required-features = ["history"]

[[test]]
name = "live_capture"
required-features = ["history"]

[[test]]
name = "probe_stop"
required-features = ["history"]

[[test]]
name = "rebuild"
required-features = ["history"]

[[test]]
name = "record_format"
required-features = ["history"]

[[example]]
name = "native_visibility_host"
required-features = ["history"]

[features]
# Ordinary builds keep every production executable. The desktop apps'
# crates opt out with --no-default-features when they only need the library.
default = ["production-binaries"]
default = ["production-binaries", "history"]
production-binaries = []
# Session history: local capture and upload to RelayHistory, the session
# catalog, history retention, and the remote (Devin Cloud, Claude web)
# connectors, all built on relayhistory's `ai-hist`. Without it the core is
# the messaging runtime alone (`relay serve`: discovery, native delivery and
# injection, harness setup, Relaycast, spawning, the bridge) and depends on
# nothing from relayhistory. relay builds it that way:
# cargo build --no-default-features --features production-binaries
history = ["dep:ai-hist", "dep:history-provider-sources", "dep:notify"]

[workspace]

Expand All @@ -31,13 +81,13 @@ production-binaries = []
# `history-provider-sources` is not published to crates.io and its helper
# surface carries ai-hist types (`Arguments.observation`), so both deps
# must resolve to one ai-hist package — registry + git would build two.
ai-hist = { git = "https://github.com/AgentWorkforce/relayhistory", rev = "be827d1e84e6f57de9c0e9c2ac5224369644cf2e", features = ["opencode-backup", "git-hooks", "fs-events"] }
ai-hist = { git = "https://github.com/AgentWorkforce/relayhistory", rev = "be827d1e84e6f57de9c0e9c2ac5224369644cf2e", features = ["opencode-backup", "git-hooks", "fs-events"], optional = true }
# The remote source connectors probe composes (`devin-cloud`). Not on
# crates.io — the same pin keeps it on the ai-hist above.
history-provider-sources = { git = "https://github.com/AgentWorkforce/relayhistory", rev = "be827d1e84e6f57de9c0e9c2ac5224369644cf2e" }
history-provider-sources = { git = "https://github.com/AgentWorkforce/relayhistory", rev = "be827d1e84e6f57de9c0e9c2ac5224369644cf2e", optional = true }
# The watcher ai-hist's own watch is built on, for selected mode's catalog
# (src/probe/inventory.rs), which ai-hist's sync-driven watch cannot serve.
notify = "8"
notify = { version = "8", optional = true }
# Safe PTY ownership and shutdown signals for native Muse spawning.
portable-pty = "=0.8.1"
anyhow = "1"
Expand Down
72 changes: 66 additions & 6 deletions crates/probe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,79 @@ aligns.
| ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/probe/` | The `agent-relay-probe` binary: bridge commands, the collector's delivery loop, `live` capture, progress, and `history`, its one path into `SessionStore` |
| `src/relay/` | The relay core daemon (`relay serve`): workspace and account, Relaycast, delivery and injection per harness, harness setup (hooks/plugins), spawning, the bridge and its contract (`relay::contract`) |
| `src/cloud*`, `src/delivery*`, `src/destination*`, `src/retention*` | Cloud sign-in, the upload queue and its worker, the RelayHistory receiver, history retention |
| `src/cloud*`, `src/delivery*`, `src/destination*`, `src/retention*` | Cloud sign-in; with `history`, the upload queue and its worker, the RelayHistory receiver, history retention |
| `src/install*` | Where a packaged desktop install keeps its executables (`agent-relay`, `agent-relay-probe`); the core's launch-at-login entry uses it |
| `src/session_model*` | The session catalog's view model (rows, the session search language, upload states) the core's catalog query filters by |
| `src/bin/relay-contract-codegen.rs` | Generates the Swift bridge contract the apps consume |
| `contract/CoreContract.swift` | The checked-in generated contract; CI fails on drift |
| `scripts/` | `generate-contract.sh`, `check-open-source.sh` |
| `tests/` | Integration tests, with in-process fakes of Cloud, Relaycast and RelayHistory (`tests/support`) |

## Two builds: the core, and the core with session history

Session history (capturing local sessions and uploading them to RelayHistory)
is the only part of the crate built on relayhistory's `ai-hist` and
`history-provider-sources`. It sits behind the `history` Cargo feature, which
is on by default:

| Build | Command | relayhistory | Who ships it |
| -------------- | --------------------------------------------------------------------------- | ------------------------------------- | ---------------------------------------------- |
| Core | `cargo build --locked --no-default-features --features production-binaries` | none | relay (`relay init`, `relay serve --headless`) |
| Core + history | `cargo build --locked` | `ai-hist`, `history-provider-sources` | relay-desktop |

The core alone is the whole messaging runtime: `relay serve` (discovery,
native delivery and injection into running sessions, harness setup and
hooks, Relaycast and the workspace, spawning, the bridge and its contract),
`relay call`, the harness hooks, `connect` and the install helpers. The
binary's history commands (`cloud`, `run`, `start`, `stop`, `status`,
`installs`, `sessions`, `sharing`, `history flush`, ...) exist only with
`history`.

The bridge contract is the same in both builds: it is generated from the
method registry, which no feature changes, and CI checks that both builds
generate the checked-in `contract/CoreContract.swift`. Without `history`:

- the history-only methods (`relay::contract::HISTORY_METHODS`: every
`uploader.*` method and `sessions.transcript`) answer the typed error
`history_unavailable` (not retryable), before their parameters are read;
- `settings.set` of `history_retention_days` answers `history_unavailable`
before anything in its batch is saved, and `settings.get` reports its
default (30);
- `status.get` reports `daemon.history_version` as null, which is how a
client can tell the build;
- Devin Cloud and claude.ai web sessions are not discovered (both are read
from the history catalog), and nothing is sent to Devin Cloud: a send to
a cloud-shaped Devin id keeps the local answer (`Waiting` while no local
session holds it), since a stopped local session can have that id shape;
- agent-led setup (`/setup/*`) does not wait for an uploader, and
`POST /setup/sharing` answers `setup_unavailable`;
- Hermes session titles are not editable (Hermes's roots resolve through
`ai-hist`);
- the uploader finds no install, so nothing is uploaded and no background
history monitor runs. The core uses the workspace the `agent-relay` CLI
selects (`src/relay/workspace/cli.rs`), with no sign-in needed. Cloud
sign-in (`account.sign_in`) remains the fallback when the CLI names no
workspace: it adopts the workspace, then ends in `error` with the
`history_unavailable` sentence, because its last step sets up the uploader
(`cloud install`).

`.github/workflows/probe-ci.yml`'s `core-only` job builds and tests this
build on Linux, fails if `ai-hist` or `history-provider-sources` appear in its
dependency tree, and checks it generates the same contract. The macOS and
Windows jobs build it too.

## Build and test

```sh
cargo build --manifest-path crates/probe/Cargo.toml --locked
cargo build --manifest-path crates/probe/Cargo.toml --locked --release --bin agent-relay-probe
# Tests read and write under HOME: use an empty one.
# Tests read and write under HOME: use an empty one, keeping the toolchain
# (rustup and cargo) where it was installed under the real HOME.
export RUSTUP_HOME="${RUSTUP_HOME:-$HOME/.rustup}" CARGO_HOME="${CARGO_HOME:-$HOME/.cargo}"
HOME="$(mktemp -d)" cargo test --manifest-path crates/probe/Cargo.toml --locked
# The core without session history (what relay ships):
HOME="$(mktemp -d)" cargo test --manifest-path crates/probe/Cargo.toml --locked \
--no-default-features --features production-binaries
Comment thread
kjgbot marked this conversation as resolved.
Resolved
```

The relayhistory git dependencies fetch over HTTPS; if Cargo's built-in Git
Expand Down Expand Up @@ -93,11 +151,13 @@ its own copy:

- **Rust (GTK app):** the Linux/Windows app becomes its own crate that
depends on `agent-relay-probe` as a library (a git dependency pinned to a
relay tag or commit, `default-features = false` to skip the binaries) and
imports `agent_relay_probe::{install, session_model, relay::…}`.
relay tag or commit, `default-features = false, features = ["history"]`
to skip the binaries but keep session history) and imports
`agent_relay_probe::{install, session_model, relay::…}`.
- **Daemon binary:** the apps bundle `agent-relay-probe` built from this
crate with `AGENT_RELAY_PROBE_VERSION=<desktop CalVer>`, or the binary from
a relay release once relay publishes it.
crate with the default features (history included) and
`AGENT_RELAY_PROBE_VERSION=<desktop CalVer>`. relay's own release ships
the core-only build.
- **Swift (macOS/iPad):** `CoreContract.swift` comes from
`contract/CoreContract.swift` (or `generate-contract.sh --app-root`).

Expand Down
7 changes: 6 additions & 1 deletion crates/probe/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,12 @@ fn main() {
println!("cargo:rustc-env=RELAY_DESKTOP_REVISION={revision}");
windows_resources(&version);

// Cargo.lock is the resolved dependency, not the manifest's version range.
// Only the `history` feature links RelayHistory, so only it reports a
// history version. Cargo.lock is the resolved dependency, not the
// manifest's version range.
if env::var_os("CARGO_FEATURE_HISTORY").is_none() {
return;
}
let lock = fs::read_to_string(
PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join("Cargo.lock"),
)
Expand Down
Loading
Loading