Integrate embedded WhatsApp and text channels#89
Conversation
This comment was marked as low quality.
This comment was marked as low quality.
There was a problem hiding this comment.
Pull request overview
Integrates embedded channel transports (WhatsApp Web + Signal) and adds a new Linq-backed sms channel, while updating docs and smoke tooling to reflect the expanded channel set and improved delivery behavior.
Changes:
- Replaces the WhatsApp webhook sidecar with an embedded
WhatsAppWebChanneltransport loop and adds a new Linq webhook-basedsmschannel. - Moves Signal/WhatsApp outbound sending to
OutboundMessagetext fallbacks for consistent artifact rendering. - Adds Matrix 429 retry/backoff handling and updates install/docs + docker smoke scripts for the new channel and recipe smoke option.
Reviewed changes
Copilot reviewed 20 out of 21 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| scripts/manual-docker-test.sh | Adds optional agent recipe smoke run under env flag. |
| scripts/install-calciforge.sh | Updates sample config snippets for embedded WhatsApp/Signal and new sms channel. |
| docs/staging-test-matrix.md | Documents when to run recipe smoke as part of staging verification. |
| docs/index.md | Updates channel list + adds WhatsApp session and sms Linq examples; links to new agents/routing guide. |
| docs/channels/whatsapp.md | Rewrites WhatsApp setup guide for embedded session transport + migration notes. |
| docs/channels/sms.md | New setup guide for Linq-backed text/iMessage channel. |
| docs/agents.md | New guide documenting [[agents]], [[identities]], [[routing]] with full-config example. |
| docs/agent-adapters.md | Mentions running recipe smoke via the docker smoke path. |
| docs/README.md | Adds agents.md to docs index. |
| crates/calciforge/src/main.rs | Wires up sms channel startup/join; updates WhatsApp/Signal startup log text. |
| crates/calciforge/src/config.rs | Adds sms + embedded WhatsApp fields; adds doc TOML block parsing tests (sms + agents.md). |
| crates/calciforge/src/channels/whatsapp.rs | Replaces webhook receiver with embedded transport listener loop; uses OutboundMessage fallback rendering; adds migration rejection + tests. |
| crates/calciforge/src/channels/telemetry.rs | Updates module docs to include SMS in the cross-channel telemetry schema statement. |
| crates/calciforge/src/channels/sms.rs | New Linq webhook receiver + dispatch bridge using OutboundMessage fallbacks + signature verification support. |
| crates/calciforge/src/channels/signal.rs | Adds !secure handling + switches to OutboundMessage fallback rendering path. |
| crates/calciforge/src/channels/mod.rs | Updates module docs and exports new sms channel module. |
| crates/calciforge/src/channels/matrix.rs | Adds retry handling for Matrix 429 rate-limit responses. |
| crates/calciforge/Dockerfile | Exposes port 18798 for the SMS webhook listener. |
| crates/calciforge/Cargo.toml | Enables zeroclawlabs feature for WhatsApp Web transport. |
| README.md | Updates feature table to include text/iMessage routing. |
| Cargo.lock | Lockfile updates for dependency graph changes from WhatsApp Web enablement. |
56a2c0e to
b780340
Compare
b780340 to
b88d845
Compare
b88d845 to
749f060
Compare
749f060 to
d58b190
Compare
d58b190 to
d320b43
Compare
d320b43 to
2cd9203
Compare
2cd9203 to
3e480f3
Compare
| <div class="nav"> | ||
| <a href="https://github.com/bglusman/calciforge">GitHub</a> | ||
| <a href="https://github.com/bglusman/calciforge/blob/main/README.md">README</a> | ||
| <a href="https://github.com/bglusman/calciforge/tree/main/docs">Docs</a> | ||
| <a href="#quick-install-mac">Install</a> | ||
| <a href="agents.md">Agents</a> | ||
| </div> |
There was a problem hiding this comment.
The nav link points to agents.md, but the published GitHub Pages site will generate agents.html for that page. Linking to the .md path will likely 404 (or render raw source) in production; use agents.html (or a root-relative /agents.html) to match the rest of the site links.
| /// List ACPX sessions for an agent using the acpx CLI. | ||
| async fn list_acpx_sessions(&self, agent_name: &str) -> Result<Vec<String>, String> { | ||
| tokio::fs::create_dir_all(crate::adapters::acpx::ACPX_SESSION_DIR) | ||
| .await | ||
| .map_err(|e| format!("Failed to create acpx session dir: {}", e))?; | ||
|
|
||
| let output = tokio::process::Command::new("acpx") | ||
| .arg(agent_name) | ||
| .arg("sessions") | ||
| .arg("list") | ||
| .current_dir("/tmp") | ||
| .current_dir(crate::adapters::acpx::ACPX_SESSION_DIR) | ||
| .output() | ||
| .await | ||
| .map_err(|e| format!("Failed to run acpx: {}", e))?; |
There was a problem hiding this comment.
list_acpx_sessions awaits Command::output() with no timeout and without kill_on_drop(true). If acpx sessions list hangs (or the task is cancelled), this can stall !sessions handling and potentially leave an orphaned subprocess. Consider adding a reasonable timeout (similar to the adapter) and enabling kill-on-drop for the child process.
| if let Some(reply) = self.command_handler.handle(&text) { | ||
| debug!(identity = %identity.id, cmd = %text.trim(), "Text/iMessage: handled pre-auth command"); | ||
| let channel = self.clone(); | ||
| let target = reply_target.clone(); | ||
| tokio::spawn(async move { | ||
| channel.send_reply(&target, &reply).await; | ||
| }); | ||
| return; |
There was a problem hiding this comment.
SMS command handling sends replies but doesn't emit telemetry::command_reply_ready(...) events (unlike Telegram/WhatsApp). Since channels/telemetry.rs explicitly calls out cross-channel comparability (and now mentions SMS), consider adding command telemetry here (pre-auth, unknown, status, switch, etc.) so command latency/volume is observable consistently.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 46 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
crates/calciforge/src/adapters/acpx.rs:105
ensure_sessioninvokesacpx sessions ensure ...viaCommand::output().awaitwithoutkill_on_drop(true)and without using the adapter's existingtimeout_ms. Ifacpxblocks, this can hang dispatch permanently, and cancellation may leave the child process running. Consider settingkill_on_drop(true)and wrapping the wait intokio::time::timeout(Duration::from_millis(self.timeout_ms), ...), returning a clearAdapterError::Unavailableon timeout.
let output = Command::new("acpx")
.arg(&self.agent_name)
.arg("sessions")
.arg("ensure")
.arg("--name")
.arg(session_name)
.current_dir(&self.session_dir)
.envs(&self.env)
.output()
.await
.map_err(|e| AdapterError::Unavailable(format!("Failed to create session: {}", e)))?;
a9cc4b8 to
d46c860
Compare
Summary
zeroclawlabs::WhatsAppWebChannelsupport.kind = "sms"Text/iMessage channel for iMessage/RCS/SMS webhooks and replies.Verification
cargo check -p calciforgecargo test -p calciforge test_channel -- --nocapturecargo test -p calciforge test_agents_doc_full_config_toml_blocks_valid -- --nocapturecargo test -p calciforge channels::whatsapp::tests -- --nocapturecargo test -p calciforge channels::sms::tests -- --nocaptureCALCIFORGE_AGENT_RECIPE_SMOKE=1 scripts/manual-docker-test.shNotes
Live WhatsApp pairing and live Linq delivery still need provider-backed staging credentials. The mocked channel tests cover identity drops, reply target preservation, legacy WhatsApp config rejection, and artifact fallback rendering.