Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
9c83a32
chore(staging): v0.61.7 [skip ci]
github-actions[bot] Jul 21, 2026
00708c3
fix(orchestration): async-by-default delegations with durable, resuma…
senamakel Jul 21, 2026
9420a29
revert(safety): remove Emergency Stop for desktop automation (#4600) …
senamakel Jul 21, 2026
09f552c
fix(transcript): lossless transcript restore — full-fidelity cold-boo…
senamakel Jul 21, 2026
684ad0d
perf(core): consolidate boot polls, silence per-snapshot log spam (#5…
senamakel Jul 21, 2026
f181115
test(learning): isolate startup subscriber test (#5082)
senamakel Jul 21, 2026
fcbcebe
feat(tui): feature-gated terminal chat UI (openhuman tui / chat) (#5084)
senamakel Jul 21, 2026
49a0d3b
fix: daemon-health identity race + Orchestration sub-tab follow-ups (…
senamakel Jul 21, 2026
00846fd
feat(transcript): derived transcript view — append-only session log a…
senamakel Jul 21, 2026
c7f67b7
feat(prompts): load AGENTS.md project instructions into the system pr…
senamakel Jul 21, 2026
4df2e24
fix(agents-md): security hardening dropped from #5087 merge (#5096)
senamakel Jul 21, 2026
c09bef2
chore(release): v0.62.0 [skip ci]
github-actions[bot] Jul 21, 2026
4efd80d
chore(core): remove zero-reference deps and redirect_links domain (#5…
YellowSnnowmann Jul 21, 2026
bea380d
feat(bench): embedded-RSS benchmark harness + report-only CI (#5046) …
YellowSnnowmann Jul 21, 2026
4019a9a
feat(core): gate the desktop-automation cluster behind a default-ON f…
YellowSnnowmann Jul 21, 2026
fc0222f
perf(agent): share one Arc<Config> across per-build tool/provider/ref…
YellowSnnowmann Jul 21, 2026
dfef12c
chore(release): v0.63.0 [skip ci]
github-actions[bot] Jul 21, 2026
9d14d91
chore(release): merge release v0.63.0 back into main
github-actions[bot] Jul 21, 2026
846db8a
feat(flows): render Workflow Copilot chat via the shared composer tra…
senamakel Jul 21, 2026
f92888a
feat(flows): add "Save & enable" to the Flow Canvas copilot proposal …
graycyrus Jul 21, 2026
7ec16e8
fix(agentbox): normalize GMI_MAAS_BASE_URL with trailing /v1 (#5091)
Horst1993 Jul 21, 2026
78949de
fix(conversations): stop the thread-goal footer text from auto-scroll…
jgentes Jul 21, 2026
86e1a4c
fix(approval): preserve replacement routes during cleanup (#4786)
samrusani Jul 21, 2026
e2f9014
fix(core): seed tool-execution timeout on the always-on boot path (#5…
oxoxDev Jul 21, 2026
dfa5738
fix(keyring): declare msg mutable for cfg(windows) push_str (E0596) (…
myi1 Jul 21, 2026
4b2d92c
fix(api): classify Atlas Cloud as inference provider (#4885)
binyangzhu000-sudo Jul 21, 2026
c309be8
fix(flows): show plain-language step labels in the workflow proposal …
graycyrus Jul 21, 2026
40852cd
feat(flows): let the copilot live test-run flows behind the approval …
graycyrus Jul 21, 2026
81f58ee
test(ci): exercise full validation suite (#5083)
senamakel Jul 22, 2026
1fb1538
fix(flows): live-refresh the runs rail when a run starts (B35) (#5099)
graycyrus Jul 22, 2026
31fca02
fix(inference): keep Claude CLI prompts out of Windows argv (#5103)
samrusani Jul 22, 2026
ddeee5b
feat(core): shed embedded deps behind inference/documents/crash-repor…
YellowSnnowmann Jul 22, 2026
06be00c
fix(tinyplace): wire handle transfer end-to-end (Closes #4929) (#4998)
M3gA-Mind Jul 22, 2026
f18414f
fix(voice): handle U16 sample format in the fallback capture path (#4…
mysma-9403 Jul 22, 2026
c5fea23
fix(mcp): hide raw registry errors (#4716)
samrusani Jul 22, 2026
63e4fe2
feat(security): opt-in auto-approve-all setting that bypasses the app…
graycyrus Jul 22, 2026
5fcde25
feat(dev): library-mode benchmark environment, fleet/budget gates, an…
senamakel Jul 22, 2026
0eeee12
feat(core): channels compile-time feature gate (#4801) — completes ep…
oxoxDev Jul 22, 2026
002530d
feat(flows): link to the saved workflow from the chat proposal card (…
graycyrus Jul 22, 2026
58494e1
fix(flows): shorter approval TTL for copilot live-run parks (#5112)
graycyrus Jul 22, 2026
9b72616
feat(flows): FlowRunFinished event — fully event-driven runs rail (co…
graycyrus Jul 22, 2026
41bad05
feat(flows): teach the workflow builder to pick specialist agents via…
graycyrus Jul 22, 2026
9e312b6
chore(deps): update vendored runtime crates (#5081)
senamakel Jul 22, 2026
353e07c
fix(embeddings): classify 403 'not an embeddings model' as model-inco…
M3gA-Mind Jul 22, 2026
f55376b
fix(react-ui): surface layered pipeline status in Data Sync (GH-4690)…
M3gA-Mind Jul 22, 2026
81ac128
fix(flows): reject an unknown agent_ref at author time instead of mid…
graycyrus Jul 22, 2026
5b8a9f2
fix(composio): drop non-existent Discord channel/message slugs from c…
YellowSnnowmann Jul 22, 2026
a8d13fa
fix(flows): repair main compile break from crossed PRs (#5128)
senamakel Jul 22, 2026
9bb59ca
feat(medulla_local): supervise a local medulla-serve child (Flavor A …
senamakel Jul 22, 2026
6c135e0
fix(composio): execute valid first calls + recover Kimi tool-call gar…
M3gA-Mind Jul 22, 2026
60a7618
feat(settings): add dismissible in-app GitHub star CTA (#5005) (#5110)
M3gA-Mind Jul 22, 2026
e3a7f52
chore(release): v0.63.1 [skip ci]
github-actions[bot] Jul 22, 2026
fb1f909
chore(release): merge release v0.63.1 back into main
github-actions[bot] Jul 22, 2026
c480e6d
fix(agents): run custom registry agents with their real tools (flows …
graycyrus Jul 22, 2026
2bcd344
refactor(memory): finish TinyCortex consolidation (#5140)
senamakel Jul 23, 2026
81d09b6
feat(tui): add logs-first tabbed CLI experience (#5131)
senamakel Jul 23, 2026
5041454
docs: Add CITATION.cff for software citation metadata (#5142)
mwakidenis Jul 23, 2026
faa49f3
chore: merge upstream/main into pr/5141 and resolve conflicts
senamakel Jul 23, 2026
a3efbc7
fix: address PR #5141 review comments
senamakel Jul 23, 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
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/tauri-cef-expected-sha
Original file line number Diff line number Diff line change
@@ -1 +1 @@
5ec3d8836cbae300e36b7a9c0d302a65de92c58d
11ef51edbeadcca4517b18a037fff858a5dfae0f
12 changes: 10 additions & 2 deletions .github/workflows/ci-full.yml
Original file line number Diff line number Diff line change
Expand Up @@ -287,13 +287,21 @@ jobs:
mkdir -p "$OPENHUMAN_WORKSPACE"
bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-web-session.sh

- name: Pack Playwright E2E failure artifacts
if: failure()
run: |
mkdir -p .ci/artifacts
tar -czf .ci/artifacts/openhuman-playwright-failure-logs.tar.gz \
-C "$OPENHUMAN_WORKSPACE" .
env:
OPENHUMAN_WORKSPACE: ${{ runner.temp }}/openhuman-playwright-workspace

- name: Upload Playwright E2E failure artifacts
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: e2e-playwright-failure-logs-${{ github.run_id }}
path: |
${{ runner.temp }}/openhuman-playwright-workspace/**
path: .ci/artifacts/openhuman-playwright-failure-logs.tar.gz
retention-days: 7
if-no-files-found: ignore

Expand Down
54 changes: 52 additions & 2 deletions .github/workflows/ci-lite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -443,9 +443,11 @@ jobs:
set -euo pipefail
EXPECTED=$(cat <<'EOF'
core/all_tests.rs
core/autocomplete_cli_adapter.rs
core/cli_tests.rs
core/jsonrpc_tests.rs
core/legacy_aliases.rs
core/runtime/context.rs
openhuman/agent/harness/builtin_definitions.rs
openhuman/agent/harness/definition_tests.rs
openhuman/agent/harness/session/tests.rs
Expand All @@ -462,14 +464,62 @@ jobs:
openhuman/x402/stub.rs
EOF
)
ACTUAL=$(grep -rlE '#\[cfg\((not\()?feature = "(voice|media|web3|meet|mcp|skills|flows)"' src --include='*.rs' \
ACTUAL=$(grep -rlE '#\[cfg\((not\()?feature = "(voice|media|web3|meet|mcp|skills|flows|channels|desktop-automation)"' src --include='*.rs' \
| xargs grep -lE '#\[test\]|#\[tokio::test\]|fn .*_test' 2>/dev/null | sed 's|^src/||' | sort -u)
if ! diff <(echo "$EXPECTED" | sed 's/^ *//' | sort -u) <(echo "$ACTUAL"); then
echo "::error::Gated-test file set changed. Update the EXPECTED allowlist in the rust-feature-gate-smoke lane, and extend the scoped 'cargo test' filter if the new module can carry an ungated-assert regression (see #5022)."
exit 1
fi
echo "gate-contract test coverage allowlist is current"

# Report-only (#5046). Measures steady-state RSS of an embedded openhuman_core
# agent roster and uploads the raw samples + a human summary. Deliberately NOT
# in `pr-ci-gate.needs`, so it can never fail a PR — it exists to accrue a
# baseline and its runner variance before the 30 MiB gate is flipped to
# blocking in a follow-up (add this job to pr-ci-gate + a threshold step then).
rust-rss-bench:
name: Rust RSS Benchmark (report-only)
needs: [changes]
if: needs.changes.outputs['rust-core'] == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 40
container:
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
env:
CARGO_INCREMENTAL: "0"
steps:
- name: Checkout code
uses: actions/checkout@v7
with:
fetch-depth: 1
persist-credentials: false
submodules: recursive

- name: Cache Rust build artifacts
uses: Swatinem/rust-cache@v2
with:
workspaces: |
. -> target
cache-on-failure: true
shared-key: pr-rust-rss-bench

# Fixture-contract signal: the gated bin's unit tests (build_roster,
# warm-up turn) never enter the default coverage lane, so run them here.
- name: Run rss-bench fixture tests
run: bash scripts/ci-cancel-aware.sh cargo test --features rss-bench --bin rss-bench

- name: Build stripped-release rss-bench
run: bash scripts/ci-cancel-aware.sh cargo build --release --features rss-bench --bin rss-bench

- name: Measure embedded RSS (5 fresh procs x {1,8} agents)
run: ./target/release/rss-bench --out bench-rss.json | tee -a "$GITHUB_STEP_SUMMARY"

- name: Upload raw RSS samples
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
with:
name: rss-bench-report
path: bench-rss.json

rust-core-coverage:
name: Rust Core Coverage (cargo-llvm-cov)
needs: [changes, rust-quality]
Expand Down Expand Up @@ -806,7 +856,7 @@ jobs:
key: tinycortex

- name: Run TinyCortex engine and Composio sync tests
run: bash scripts/ci-cancel-aware.sh cargo test --manifest-path vendor/tinycortex/Cargo.toml --features git-diff,sync
run: bash scripts/ci-cancel-aware.sh cargo test --manifest-path vendor/tinycortex/Cargo.toml --features git-diff,sync,persona

pr-ci-gate:
name: PR CI Gate
Expand Down
17 changes: 14 additions & 3 deletions .github/workflows/e2e-playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,10 @@ jobs:
runs-on: ubuntu-22.04
container:
image: ghcr.io/tinyhumansai/openhuman_ci:latest
timeout-minutes: 30
# The complete serial web suite currently takes about 45 minutes on the
# shared runner; keep the standalone diagnostic workflow aligned with the
# 90-minute budget used by CI Full.
timeout-minutes: 90
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -73,12 +76,20 @@ jobs:
mkdir -p "$OPENHUMAN_WORKSPACE"
bash scripts/ci-cancel-aware.sh bash app/scripts/e2e-web-session.sh

- name: Pack Playwright E2E failure artifacts
if: failure()
run: |
mkdir -p .ci/artifacts
tar -czf .ci/artifacts/openhuman-playwright-failure-logs.tar.gz \
-C "$OPENHUMAN_WORKSPACE" .
env:
OPENHUMAN_WORKSPACE: ${{ runner.temp }}/openhuman-playwright-workspace

- name: Upload Playwright E2E failure artifacts
if: failure()
uses: actions/upload-artifact@v7
with:
name: e2e-playwright-failure-logs-${{ github.run_id }}
path: |
${{ runner.temp }}/openhuman-playwright-workspace/**
path: .ci/artifacts/openhuman-playwright-failure-logs.tar.gz
retention-days: 7
if-no-files-found: ignore
9 changes: 6 additions & 3 deletions .github/workflows/e2e-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,8 @@ jobs:
- { name: provider-web, suites: "provider-web" }
- { name: webhooks, suites: "webhooks" }
- { name: connectors, suites: "connectors" }
- { name: commerce, suites: "payments,settings" }
- { name: payments, suites: "payments" }
- { name: settings, suites: "settings" }
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -765,7 +766,8 @@ jobs:
- { name: provider-web, suites: "provider-web" }
- { name: webhooks, suites: "webhooks" }
- { name: connectors, suites: "connectors" }
- { name: commerce, suites: "payments,settings" }
- { name: payments, suites: "payments" }
- { name: settings, suites: "settings" }
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down Expand Up @@ -976,7 +978,8 @@ jobs:
- { name: provider-web, suites: "provider-web" }
- { name: webhooks, suites: "webhooks" }
- { name: connectors, suites: "connectors" }
- { name: commerce, suites: "payments,settings" }
- { name: payments, suites: "payments" }
- { name: settings, suites: "settings" }
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand Down
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
worktrees/*
!worktrees/.gitkeep

# Workflow docs (local only)
workflow
create_issue
Expand Down Expand Up @@ -129,4 +132,6 @@ distribution.cer
# Release note previews
CHANGELOG.preview.md
*.profraw
*.diff
*.diff

.claude/worktrees/
34 changes: 22 additions & 12 deletions .husky/pre-push
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
#!/usr/bin/env sh

# Bail out immediately on Ctrl+C / SIGTERM. Without this trap, an interrupt
# only kills the current pnpm subprocess; the script then captures its 130
# exit, mistakes it for a normal failure, and runs the next pnpm step.
# Bail out immediately on Ctrl+C / SIGTERM.
abort() {
EXIT_CODE="$1"
echo
echo "Pre-push aborted."
trap - INT TERM
kill -- -$$ 2>/dev/null
exit 130
exit "$EXIT_CODE"
}
trap 'abort 130' INT
trap 'abort 143' TERM

# Commands run synchronously in the hook's foreground process group, so Ctrl+C
# reaches pnpm and its children. Do not mistake the resulting exit code for a
# normal check failure and continue with the next command.
run_check() {
"$@"
CHECK_EXIT=$?
case "$CHECK_EXIT" in
130|143) abort "$CHECK_EXIT" ;;
esac
return "$CHECK_EXIT"
}
trap abort INT TERM

# Windows Git Bash can miss Node/Pnpm in PATH when hooks run.
# Recover from common PATH drift by hydrating from where.exe.
Expand Down Expand Up @@ -65,7 +75,7 @@ fi

# Run format check first (capture exit code without breaking script)
set +e
pnpm format:check
run_check pnpm format:check
FORMAT_EXIT=$?
set -e

Expand All @@ -77,7 +87,7 @@ fi

# Run lint check (capture exit code without breaking script)
set +e
pnpm lint
run_check pnpm lint
LINT_EXIT=$?
set -e

Expand All @@ -89,19 +99,19 @@ fi

# Run TypeScript compile check (capture exit code without breaking script)
set +e
pnpm compile
run_check pnpm compile
COMPILE_EXIT=$?
set -e

# Run Clippy for both Rust codebases; Clippy also performs compile checks.
set +e
pnpm rust:clippy
run_check pnpm rust:clippy
RUST_CLIPPY_EXIT=$?
set -e

# Enforce scoped cmd-* tokens in components/commands/
set +e
pnpm --dir app run lint:commands-tokens
run_check pnpm --dir app run lint:commands-tokens
CMD_TOKENS_EXIT=$?
set -e

Expand Down
25 changes: 25 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,8 @@ GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml \
| `skills` | ON | `openhuman::skills` + `openhuman::skill_runtime` + `openhuman::skill_registry` domains — SKILL.md discovery/parse/install, workflow execution + run logs, remote catalogs, the `skill_setup` / `skill_executor` builtin agents, and the 16 skill agent tools | none (see below) |
| `flows` | ON | `openhuman::flows` (saved automation graphs — create/run/schedule, the `workflow_builder` + `flow_discovery` agents), `openhuman::tinyflows` (engine seam), `openhuman::rhai_workflows` (`.ragsh` language-workflow tool) | `tinyflows`, `jaq-core`, `jaq-std`, `jaq-json`, `rhai` |
| `mcp` | ON | `openhuman::mcp_server` (the `openhuman mcp` stdio/HTTP server), `openhuman::mcp_registry` (dynamic Smithery installs — `mcp_clients` RPC namespace, SQLite, boot spawn, supervisor, OAuth), `openhuman::mcp_audit` (write-audit log), and the static config-declared server set in `openhuman::mcp_client`. ~19 agent tools, ~20k LOC | **none** (see scope note) |
| `tui` | ON | `openhuman::tui` — the tabbed ratatui/crossterm CLI UI (Logs, Chat, Config, Settings), auto-opened by bare `openhuman` on interactive non-container hosts and forced with `openhuman tui` (alias `chat`). Runs the core in-process. No controllers, no agent tools. **Intentionally NOT forwarded to the desktop shell** (allowlisted in `check-feature-forwarding.mjs`). | `ratatui`, `crossterm` |
| `channels` | ON | `openhuman::channels` (external-messaging providers — Telegram/Discord/Slack/Signal/WhatsApp/iMessage/IRC/… — plus the channel runtime, controllers, host, proactive messaging + inbound dispatch) and the `webview_accounts` / `webview_apis` / `webview_notifications` / `whatsapp_data` webview-bridge domains (incl. the 3 `whatsapp_data_*` agent tools). **Carve-outs `channels::{traits, cli}` stay ungated.** | **none** (`tinychannels` is load-bearing) |

**Facade pattern (pathfinder for the other gates).** `pub mod voice;` is **always compiled** as a facade: the real submodules are `#[cfg(feature = "voice")]`, and a `#[cfg(not(feature = "voice"))] mod stub;` (`src/openhuman/voice/stub.rs`) re-exposes the same public surface that always-on / other-gated callers use (`server`, `dictation_listener`, `streaming`, `reply_speech`, `cloud_transcribe`, `cli`, `create_stt_provider`, `effective_stt_provider`, `publish_ptt_transcript_committed`) with no-op / `None` / disabled-error bodies. Callers therefore do **not** need per-call `#[cfg]`. When voice is off: the voice/audio controllers are unregistered (unknown-method over `/rpc`, absent from `/schema`), the `audio_generate_podcast` agent tools are absent, and `openhuman voice` returns a "voice disabled" error. Stub signatures must match the real ones exactly — the disabled build (`--no-default-features --features tokenjuice-treesitter`) is the **only** thing that catches drift, so run it before pushing any change to the voice surface.

Expand Down Expand Up @@ -306,6 +308,29 @@ Follows the voice facade+stub pattern for `mcp_server` / `mcp_registry` / `mcp_a

`src/core/all.rs` needs **no** `#[cfg]` for this gate: the stub aggregators return empty vecs, so the registration sites keep compiling unchanged.

#### The `tui` gate

The tabbed terminal UI (`openhuman`, or explicitly `openhuman tui` / alias `chat`) lives in `src/openhuman/tui/` and follows the **`mcp`/`voice` facade+stub** pattern: `pub mod tui;` is always compiled; the behavioural submodules (`app`, `render`, `state`, `terminal`, `runner`) are `#[cfg(feature = "tui")]`; and `#[cfg(not(feature = "tui"))] mod stub;` re-exposes the one symbol an always-compiled caller reaches — `run_from_cli` — with a build-fact error body (`"tui feature disabled at compile time … --features tui"`). Bare-command auto-launch requires terminal stdin/stdout and `HostKind::Cli`; Docker, CI, pipes, and `--no-tui` retain the non-TUI CLI path.

- **The `"tui" | "chat"` CLI arm in `src/core/cli.rs` is un-`#[cfg]`'d on purpose.** In a slim build it resolves to `tui::stub::run_from_cli`, which bails with the disabled-error rather than falling through to `unknown namespace: tui` (which reads like a typo, not a build fact). Same reasoning as the `mcp` arm. Pinned by `tui_subcommand_reports_disabled_build_when_gate_off` / `chat_alias_reports_disabled_build_when_gate_off` in `src/core/cli_tests.rs` (both `#[cfg(not(feature = "tui"))]`). `"tui" | "chat"` is also added to the banner-suppression `matches!` (a TUI owns the terminal — a banner would corrupt it).
- **No controllers, no agent tools, no `all.rs` changes.** The TUI is a pure *client* of existing registered controllers — it boots the core in-process (`CoreBuilder::new(HostKind::detect_standalone()).domains(DomainSet::full()).services(ServiceSet::none())`), sends chat turns through `web_chat`, reads a bounded in-memory copy of the file-only core log stream, edits only curated safe config getters/updaters, and invokes auth controllers for account/status actions. Never render `config.get` wholesale because the full snapshot can contain secrets.
- **Terminal hygiene is load-bearing.** `logging::init_for_tui` installs a **file-only** subscriber (never stderr) — a single core boot log on stdout/stderr would corrupt the alternate-screen UI. `terminal::TerminalGuard` restores raw mode + the main screen on `Drop`, and a panic hook chains a restore ahead of the default hook. All `[tui]` state-transition logs go to the file, never `println!`.
- **Intentionally NOT forwarded to the desktop shell** (the app ships its own Tauri UI). It carries the only current entry in `INTENTIONALLY_NOT_FORWARDED` in `scripts/ci/check-feature-forwarding.mjs`; the pure reducer lives in `src/openhuman/tui/state.rs` (`TranscriptState::apply_event`) with unit tests, so most behaviour is testable without a terminal.

Drops the exclusive `ratatui` + `crossterm` deps when off. Verify with `cargo tree -i ratatui --no-default-features --features tokenjuice-treesitter` (must return nothing).
#### The `channels` gate (#4801 — last child of #4795)

Leaf-gate pattern with **two ungated carve-outs and no stub file** — the reach-map put every gated symbol at a *registration/leaf* call site, so absence (unknown-method / omitted tool), not a disabled-error stub, is the correct off-state (same rationale as `flows` / `meet`).

- **Sheds ZERO dependencies — do NOT re-litigate.** `tinychannels` stays always-compiled regardless of the gate: `config/schema/channels.rs` re-exports its config types, `event_bus/events.rs`'s `DomainEvent` embeds `tinychannels::ChannelInboundEnvelope`, and `security/pairing.rs` re-exports its pairing helpers. The `channels = []` feature list is intentionally empty. The gate's value is compile-time surface + binary size. (`whatsapp-web` is a **refinement inside** the gate — `whatsapp-web = ["channels", "tinychannels/whatsapp-web"]`.)
- **Two ungated carve-outs.** `pub mod traits;` (a one-line `tinychannels` `Channel`/`SendMessage` re-export) and `pub mod cli;` (`CliChannel`, a dependency-free local stdin/stdout REPL) stay compiled in **all** builds — both are reached by the always-on agent-harness interactive loop (`agent::harness::session::runtime::run_interactive`). Same shape as the `meet_agent::wav` carve-out. `channels::mod.rs` `#[cfg(feature = "channels")]`s everything else; nothing inside the gated submodules changes.
- **The in-app web chat is NOT gated.** `openhuman::web_chat` (RPC namespace `channel`, decoupled from `channels/` in #5002 + #5003 which also moved `learning` out) is core product surface and stays always-compiled even though its runtime tag is `DomainGroup::Channels`. Its registration push in `src/core/all.rs` is deliberately left ungated; the both-ways test pins `channel` present with the feature OFF.
- **Three mis-housed imports were retargeted to `tinychannels` (no stub needed).** `cron/bus.rs` (`Channel`/`SendMessage`/`ChannelMessage`), `memory_conversations/bus.rs` (`ChannelMessage` + `context::conversation_history_key`), and `audio_toolkit/ops.rs` (`providers::email_channel::EmailChannel`) reached the gated domain only to pick up symbols that actually live in `tinychannels`; pointing them straight at the crate removes the always-on → gated edge (and the voice→channels cross-gate edge). The old `channels::` paths were 1-line delegations / `pub use` re-exports of exactly these.
- **Leaf-gated call sites** (each carries its own `#[cfg]`): the 5 controller-registration pushes in `src/core/all.rs` (channels controllers, `webview_apis`, `webview_notifications`, public + internal `whatsapp_data`), the `ChannelInboundSubscriber` + web-only-proactive block in `src/core/jsonrpc.rs`, `spawn_channels_service` in `src/core/runtime/services.rs`, the `whatsapp_data::global::init` block in `src/core/runtime/context.rs`, and the `whatsapp_data::tools::*` glob + 3 `WhatsAppData*Tool` registrations in `src/openhuman/tools/{mod,ops}.rs`. The `webview_accounts` / `webview_apis` / `webview_notifications` / `whatsapp_data` `pub mod` declarations in `src/openhuman/mod.rs` are leaf-gated too. String-match arms (`"channels" =>` descriptions, `whatsapp_data_` in `group_for_namespace`) stay **ungated** — they are data.
- **`start_bootstrap_jobs`' `services.channels` block keeps running slim** — it drives composio sync / workspace-memory sync / orchestration drain and names **no** `channels::` symbol, so it stays ungated by design.
- **No CLI change.** There is no `openhuman channels` subcommand; generic namespace resolution yields "unknown namespace" when off (the `flows` precedent — acceptable).
- **Both-ways tests.** `channels_controllers_{registered_when_feature_on,absent_when_feature_off}` in `src/core/all_tests.rs` pin the controller surface (the OFF half also asserts `channel`/web_chat survives), and `whatsapp_data_tools_{present_when_channels_on,absent_when_channels_off}` in `src/openhuman/tools/ops_tests.rs` pin the 3 agent tools (that module has the full-tool-list machinery). CI's smoke lane runs `cargo check` only, so run `cargo test --lib --no-default-features --features tokenjuice-treesitter core::all::tests` locally after touching any gated surface.

### Event bus (`src/core/event_bus/`)

Typed pub/sub + native request/response. Both singletons — use module-level functions.
Expand Down
Loading