Skip to content

feat(tools/whatsapp_data): expose local WhatsApp store to agent (#1341)#1373

Merged
senamakel merged 13 commits intotinyhumansai:mainfrom
oxoxDev:feat/1341-whatsapp-agent-tools
May 9, 2026
Merged

feat(tools/whatsapp_data): expose local WhatsApp store to agent (#1341)#1373
senamakel merged 13 commits intotinyhumansai:mainfrom
oxoxDev:feat/1341-whatsapp-agent-tools

Conversation

@oxoxDev
Copy link
Copy Markdown
Contributor

@oxoxDev oxoxDev commented May 8, 2026

Summary

  • Adds three read-only agent tools — whatsapp_data_list_chats, whatsapp_data_list_messages, whatsapp_data_search_messages — wrapping the existing JSON-RPC handlers so the orchestrator can route exact-lookup, per-chat read, and keyword-search intents to the local SQLite store.
  • Registers them in the default tool catalog and the orchestrator's [tools].named list. The internal-only whatsapp_data_ingest write path stays out of the agent surface.
  • Each response is wrapped with "provider": "whatsapp" so replies can cite WhatsApp as the source.
  • Documents the two-path storage model (direct store + memory tree) in docs/whatsapp-data-flow.md and adds matrix row 10.3.3.
  • Test-isolation fix: swaps whatsapp_data::global from OnceLock to RwLock<Option<...>> and adds a hidden reset_for_tests() so suites using their own tempdirs no longer inherit a stale sqlite handle.

Problem

whatsapp_data already persists chats/messages locally and exposes read-only RPC handlers (whatsapp_data_list_chats, whatsapp_data_list_messages, whatsapp_data_search_messages), but the orchestrator could not reach them: no Tool impl wrapped them, and src/openhuman/agent/agents/orchestrator/agent.toml didn't list them. The agent could only see WhatsApp through the memory tree, which is great for summaries but loses the per-message structure (sender JID, exact chat_id, individual timestamps) that exact-lookup intents like "what did Alice say last Friday" need. Closes #1341.

Solution

Three thin Tool impls under src/openhuman/tools/impl/whatsapp_data/, each modelled on src/openhuman/tools/impl/memory/tree/query_global.rs:

  1. Deserialise args into the matching *Request from whatsapp_data::types.
  2. Forward to whatsapp_data::rpc::*, unwrap the RpcOutcome envelope (the LLM does not need the logs side-channel).
  3. Emit a compact JSON object: { provider: "whatsapp", count, chats|messages: [...] } so provenance is unambiguous.

The new tools are registered in all_tools_with_runtime (src/openhuman/tools/ops.rs) alongside the memory_tree_* family; they are added to the orchestrator's named list. whatsapp_data_ingest is intentionally NOT wrapped — adding a Tool impl for it would reopen the read-only boundary that src/core/all.rs::build_internal_only_controllers enforces. A regression assertion in the new e2e test enforces that contract.

The whatsapp_data::global refactor was incidental: OnceLock plus per-call sqlite open meant a second e2e test using its own tempdir would inherit a handle pointing at an unlinked file ("Unable to open the database file"). Switching to RwLock<Option<...>> and adding #[doc(hidden)] pub fn reset_for_tests() keeps production semantics (init is a no-op once set) while letting tests rebind cleanly. The pre-existing whatsapp_data_ingest_and_query_e2e now resets before init too, so the suite is order-independent.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per docs/TESTING-STRATEGY.md — new whatsapp_data_agent_tools_e2e_1341 covers list/search/per-chat reads, missing-arg failures, empty-result envelope, and a boundary regression that asserts whatsapp_data_ingest is absent from the agent-facing schema set.
  • Diff coverage ≥ 80% — Each new Tool impl has unit tests for metadata, schema, and the invalid-args branch; whatsapp_data_agent_tools_e2e_1341 exercises every execute path. Local quality gates green: cargo test --lib openhuman::tools::implementations::whatsapp_data (9/9), cargo test --lib openhuman::whatsapp_data (8/8), cargo test --test json_rpc_e2e whatsapp (3/3). Coverage gate enforced in CI by .github/workflows/coverage.yml.
  • Coverage matrix updated — added row 10.3.3 WhatsApp Agent Retrieval to docs/TEST-COVERAGE-MATRIX.md.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related.
  • No new external network dependencies introduced (mock backend used per docs/TESTING-STRATEGY.md).
  • N/A: this change does not alter release-cut surfaces — agent tooling is core-only and the existing WhatsApp scanner pipeline is unchanged.
  • Linked issue closed via Closes #NNN in the ## Related section.

Impact

  • Runtime: desktop core only. Adds three Tool entries to the default catalog and three names to the orchestrator's tool list. No new background tasks, no new IPC, no UI surface.
  • Performance: each tool call is one RwLock::read plus one sqlite open_conn (existing pattern from the RPC layer). No additional sync overhead.
  • Security: read-only boundary is unchanged at the registry level, and explicitly tested. Local data stays local — no new network transport, no scanner write surface exposed.
  • Migration: none. The whatsapp_data::global change is an internal refactor; the public API (init, store, store_if_ready) preserved.
  • Compatibility: reset_for_tests() is pub so it is reachable from integration tests but doc-hidden and explicitly annotated as not for production use.

Related

  • Closes WhatsApp data is stored locally but unavailable to agent tasks #1341
  • Coverage matrix rows: 10.1.2 WhatsApp Connection (existing, untouched), 10.3.1 Incoming Message Sync (existing, untouched), 10.3.3 WhatsApp Agent Retrieval (new, this PR).
  • Architecture write-up: docs/whatsapp-data-flow.md.
  • Follow-up PR(s)/TODOs: none planned. If a future intent surfaces that the existing tools cannot serve cleanly (e.g. group-aware ranking), the right next step is a new memory_tree_* retrieval primitive rather than a new provider-specific tool.

AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A — GitHub issue only
  • URL: N/A

Commit & Branch

  • Branch: feat/1341-whatsapp-agent-tools
  • Commit SHA: 7b4443d1 (tip)

Validation Run

  • N/A: pnpm --filter openhuman-app format:check — frontend untouched.
  • N/A: pnpm typecheck — frontend untouched.
  • Focused tests: cargo test --lib openhuman::tools::implementations::whatsapp_data (9/9), cargo test --lib openhuman::whatsapp_data (8/8), cargo test --test json_rpc_e2e whatsapp (3/3 incl. the new whatsapp_data_agent_tools_e2e_1341).
  • Rust fmt/check (if changed): cargo fmt -- --check clean on the seven changed core files; cargo check --tests clean (only pre-existing warnings on main).
  • N/A: Tauri fmt/check — app/src-tauri/ untouched in this PR.

Validation Blocked

  • N/A — all gates green locally.

Behavior Changes

  • Intended behavior change: orchestrator can now answer WhatsApp exact-lookup, per-chat read, and keyword-search intents directly against the local store; replies cite WhatsApp via the new provider provenance tag.
  • User-visible effect: prompts like "summarise my WhatsApp messages with Alice" or "find action items from my WhatsApp" stop bouncing — they get grounded answers.

Parity Contract

  • Legacy behavior preserved: memory_tree_* tools continue to surface WhatsApp data via the scanner's existing memory_doc_ingest dual-write; the read-only/internal-only boundary at src/core/all.rs::build_internal_only_controllers is unchanged and explicitly tested.
  • Guard/fallback/dispatch parity checks: regression test asserts whatsapp_data_ingest is not advertised in all_whatsapp_data_controller_schemas(); existing whatsapp_data_ingest_and_query_e2e and whatsapp_memory_doc_ingest_e2e continue to pass after the global refactor.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none found.
  • Canonical PR: this one.
  • Resolution: N/A.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Three read-only WhatsApp query tools: list chats, list messages for a chat, and search messages.
  • Documentation

    • Added doc describing the WhatsApp ingestion pipeline, storage layout, and agent-facing retrieval surfaces.
  • Bug Fixes / Improvements

    • Search now matches sender names as well as message text for more complete results.
  • Tests

    • Added end-to-end tests validating tool behavior, provenance, schemas, ordering, search semantics, and improved test isolation.

oxoxDev and others added 7 commits May 8, 2026 17:20
…eset

Production callers still get strict idempotency: `init` is a no-op once a
store is set. Tests can now call `reset_for_tests()` between cases so they
do not inherit a stale handle pointing at a tempdir that has already been
dropped (`WhatsAppDataStore::open_conn()` reopens sqlite per call, so
that scenario surfaces as "Unable to open the database file" once the
file is unlinked).

Refs tinyhumansai#1341

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…inyhumansai#1341)

Wraps the existing `whatsapp_data_list_chats`, `whatsapp_data_list_messages`,
and `whatsapp_data_search_messages` RPC handlers as agent-callable Tool
impls. Each tool annotates its response with `"provider": "whatsapp"` so
the orchestrator can cite WhatsApp as the source. The matching write-path
controller `whatsapp_data_ingest` is intentionally NOT wrapped — adding a
Tool impl for it would reopen the read-only boundary the registry already
enforces (see `src/core/all.rs::build_internal_only_controllers`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ansai#1341)

Adds the three new read-only WhatsApp tools alongside the memory_tree_*
family in `all_tools_with_runtime`. The internal-only ingest handler
remains registered separately via `build_internal_only_controllers` and
is unreachable from this catalog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…sai#1341)

Adds the three direct WhatsApp tools to the orchestrator's `[tools].named`
list so it can route exact-lookup, per-chat read, and keyword-search
intents to the local SQLite store. Cross-source / action-item flows
continue to go through the existing `memory_tree_*` tools, which already
see whatsapp data because the scanner dual-writes via `memory_doc_ingest`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Captures why the scanner writes to both `whatsapp_data.db` (exact lookup)
and the memory tree (semantic / cross-source recall), how idempotency
keys keep the dual-write safe to retry, where the read-only boundary is
enforced, and which tool family covers which intent shape. Addresses the
"no duplicate-storage confusion" acceptance criterion.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `whatsapp_data_agent_tools_e2e_1341` exercising the three new
read-only tools against the global store: chat enumeration, per-chat
message read, keyword search (with account scoping and empty-result
case), and an explicit boundary regression that asserts
`whatsapp_data_ingest` is absent from the agent-facing controller schema
list. Also resets the global store at the start of
`whatsapp_data_ingest_and_query_e2e` so the suite is order-independent
under the new `RwLock<Option<...>>` global.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…yhumansai#1341)

Tracks the new agent-tool surface added by this PR alongside the existing
WhatsApp Connection (10.1.2) and Message Sync (10.3.1) rows.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@oxoxDev oxoxDev requested a review from a team May 8, 2026 11:55
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 8, 2026

Review Change Stack

📝 Walkthrough

Walkthrough

A new WhatsApp agent tool surface is added: three read-only tools (list chats, list messages, search messages) wrap WhatsApp RPC handlers with provenance. Module exports and registry wiring expose them to the orchestrator. Global store lifecycle was refactored for test isolation, search now matches sender/body, docs/coverage and E2E tests were added.

Changes

WhatsApp Agent Tools Integration

Layer / File(s) Summary
Documentation & Configuration
docs/whatsapp-data-flow.md, docs/TEST-COVERAGE-MATRIX.md, src/openhuman/agent/agents/orchestrator/agent.toml
Adds WhatsApp data-flow docs, maps agent-visible tool surface, and inserts whatsapp_data_list_chats, whatsapp_data_list_messages, whatsapp_data_search_messages into orchestrator tool allowlist; coverage matrix row added.
Tool: list_chats Implementation & Tests
src/openhuman/tools/impl/whatsapp_data/list_chats.rs
Implements WhatsAppDataListChatsTool with metadata, parameters schema (account_id, limit, offset optional), execute calling whatsapp_rpc::whatsapp_data_list_chats, returns provider:"whatsapp", and unit tests.
Tool: list_messages Implementation & Tests
src/openhuman/tools/impl/whatsapp_data/list_messages.rs
Implements WhatsAppDataListMessagesTool requiring chat_id, execute calls whatsapp_rpc::whatsapp_data_list_messages, returns provider:"whatsapp" and messages payload, plus unit tests.
Tool: search_messages Implementation & Tests
src/openhuman/tools/impl/whatsapp_data/search_messages.rs
Implements WhatsAppDataSearchMessagesTool requiring query, execute calls whatsapp_rpc::whatsapp_data_search_messages, logs counts, returns provider:"whatsapp", and unit tests.
Module Exports
src/openhuman/tools/impl/mod.rs, src/openhuman/tools/impl/whatsapp_data/mod.rs
Declare whatsapp_data submodule and re-export WhatsAppDataListChatsTool, WhatsAppDataListMessagesTool, WhatsAppDataSearchMessagesTool.
Tool Registry
src/openhuman/tools/ops.rs
Register the three WhatsApp tools in all_tools_with_runtime.
Global Store Test Isolation
src/openhuman/whatsapp_data/global.rs
Replace OnceLock with RwLock<Option<...>>, implement race-safe init, rework store()/store_if_ready(), and add reset_for_tests() to clear the global store for tests.
Store Search Behavior & Unit Test
src/openhuman/whatsapp_data/store.rs
search_messages now matches pattern against both body and sender (case-insensitive LIKE); adds unit test verifying sender-name matches.
E2E Test Coverage
tests/json_rpc_e2e.rs
Existing ingest test calls reset_for_tests() for isolation; new whatsapp_data_agent_tools_e2e_1341 validates ingest, tool outputs (provider/count/order/search scoping), controller schema advertising (no ingest), and tool metadata.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Orchestrator
  participant Tool
  participant RPC
  participant DB
  Client->>Orchestrator: user intent (e.g., "find messages")
  Orchestrator->>Tool: invoke whatsapp_data_* tool
  Tool->>RPC: whatsapp_rpc::... request
  RPC->>DB: query whatsapp_data.db
  DB-->>RPC: rows
  RPC-->>Tool: RpcOutcome
  Tool-->>Orchestrator: ToolResult {provider:"whatsapp", payload}
  Orchestrator-->>Client: reply (with provenance)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • senamakel
  • graycyrus

Poem

🐰 I hopped through code with tiny paws,

Found chats and messages in local laws.
Three new tools now gently sing,
Read‑only fetches, provenance ring.
Tests reset stores — a joyful spring!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(tools/whatsapp_data): expose local WhatsApp store to agent (#1341)' clearly and concisely summarizes the main change: exposing WhatsApp data tools to the agent via a new tools/whatsapp_data module implementation.
Linked Issues check ✅ Passed All coding requirements from issue #1341 are met: three read-only WhatsApp tools (list_chats, list_messages, search_messages) are implemented, registered in the tool catalog and orchestrator config, the read-only boundary is preserved (ingest remains internal), provenance tagging is added, documentation clarifies the dual-path storage model, test isolation is refactored for E2E reliability, and comprehensive unit and E2E tests validate the implementation.
Out of Scope Changes check ✅ Passed All changes are directly scoped to issue #1341: new WhatsApp tools, orchestrator config updates, documentation (test matrix and data-flow diagram), test utilities for isolation, and store enhancements for message search. No unrelated or extraneous modifications are present.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
src/openhuman/whatsapp_data/global.rs (1)

79-90: 🏗️ Heavy lift

Keep reset_for_tests() out of production builds.

This is now a public runtime API even though the doc comment says production callers must never invoke it. One accidental call can clear/rebind the process-global store mid-session and make later handlers fail or read from a different workspace. Please gate this behind a test-only feature or move it into a dedicated test-support surface instead of shipping it in the normal crate API.

One possible direction
-#[doc(hidden)]
-pub fn reset_for_tests() {
+#[cfg(any(test, feature = "test-utils"))]
+#[doc(hidden)]
+pub fn reset_for_tests() {
     if let Ok(mut guard) = GLOBAL_STORE.write() {
         *guard = None;
     }
 }

You'd then enable that feature from the integration-test target rather than exposing the hook in normal builds.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/whatsapp_data/global.rs` around lines 79 - 90, The public
function reset_for_tests currently ships in production; restrict it to test-only
usage by gating it with a compile-time cfg (e.g., #[cfg(any(test, feature =
"test-support"))]) or move it into a test-only module so it is not exported in
normal builds; update the crate feature list to add an optional "test-support"
feature and enable that feature from integration tests instead of leaving
reset_for_tests public, and ensure the symbol referencing GLOBAL_STORE remains
accessible under the same cfg so the implementation compiles only for test
builds.
src/openhuman/tools/impl/whatsapp_data/search_messages.rs (1)

51-69: ⚡ Quick win

Add error-path logging and safe correlation fields here.

This new tool flow only logs entry/exit, so invalid args and RPC failures disappear into a generic error string. Please log the failure branch too, and include non-PII correlation fields like account_id, chat_id, limit, and query_len rather than the raw query text.

Possible shape
     async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
         log::debug!("[tool][whatsapp_data] search_messages invoked");
         let req: SearchMessagesRequest = serde_json::from_value(args).map_err(|e| {
+            log::debug!("[tool][whatsapp_data] search_messages invalid_args error={e}");
             anyhow::anyhow!("invalid arguments for whatsapp_data_search_messages: {e}")
         })?;
         let outcome = whatsapp_rpc::whatsapp_data_search_messages(req)
             .await
-            .map_err(|e| anyhow::anyhow!("whatsapp_data_search_messages: {e}"))?;
+            .map_err(|e| {
+                log::debug!(
+                    "[tool][whatsapp_data] search_messages rpc_error account_id={:?} chat_id={:?} limit={:?} query_len={} error={e}",
+                    req.account_id,
+                    req.chat_id,
+                    req.limit,
+                    req.query.len()
+                );
+                anyhow::anyhow!("whatsapp_data_search_messages: {e}")
+            })?;
         let messages = outcome.value;
         log::debug!(
-            "[tool][whatsapp_data] search_messages returning count={}",
-            messages.len()
+            "[tool][whatsapp_data] search_messages returning account_id={:?} chat_id={:?} limit={:?} query_len={} count={}",
+            req.account_id,
+            req.chat_id,
+            req.limit,
+            req.query.len(),
+            messages.len()
         );

As per coding guidelines, "Use log / tracing at debug or trace level on RPC entry/exit, error paths, state transitions, and any branch that is hard to infer from tests; include stable prefixes (e.g. [domain], [rpc]) and correlation fields (request IDs, method names, entity IDs)."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/tools/impl/whatsapp_data/search_messages.rs` around lines 51 -
69, In execute (async fn execute in whatsapp_data/search_messages.rs) add
error-path logging and safe correlation fields: when serde_json::from_value
fails, log a prefixed debug/error message (e.g. "[tool][whatsapp_data][parse]")
including any available stable correlation IDs (if parsable) or at least a
request context placeholder; after parsing but before calling
whatsapp_rpc::whatsapp_data_search_messages, log an entry with prefix
"[tool][whatsapp_data][rpc]" and include non-PII fields from the parsed
SearchMessagesRequest (account_id, chat_id, limit, and query_len =
req.query.len() or 0) but do not log the raw query text; on RPC error (map_err
branch) log the error with a prefixed "[tool][whatsapp_data][rpc][error]" plus
the same correlation fields and then return the mapped anyhow error as before so
behavior is unchanged.
src/openhuman/tools/impl/whatsapp_data/list_messages.rs (1)

60-67: ⚡ Quick win

Add explicit error-path debug logs with stable correlation fields.

Entry/exit logs are present, but failures in argument parsing and RPC execution are currently silent in logs. Add debug/trace logging in both map_err branches so production triage can distinguish bad tool args vs upstream RPC failures.

Proposed patch
     async fn execute(&self, args: serde_json::Value) -> anyhow::Result<ToolResult> {
         log::debug!("[tool][whatsapp_data] list_messages invoked");
         let req: ListMessagesRequest = serde_json::from_value(args).map_err(|e| {
+            log::debug!(
+                "[tool][whatsapp_data] list_messages invalid_args method=whatsapp_data_list_messages error={}",
+                e
+            );
             anyhow::anyhow!("invalid arguments for whatsapp_data_list_messages: {e}")
         })?;
         let outcome = whatsapp_rpc::whatsapp_data_list_messages(req)
             .await
-            .map_err(|e| anyhow::anyhow!("whatsapp_data_list_messages: {e}"))?;
+            .map_err(|e| {
+                log::debug!(
+                    "[tool][whatsapp_data] list_messages rpc_error method=whatsapp_data_list_messages error={}",
+                    e
+                );
+                anyhow::anyhow!("whatsapp_data_list_messages: {e}")
+            })?;

As per coding guidelines: "Use log / tracing at debug or trace level on RPC entry/exit, error paths, state transitions... include stable prefixes and correlation fields."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/tools/impl/whatsapp_data/list_messages.rs` around lines 60 -
67, Add explicit debug/trace logging to the error branches in execute: when
serde_json::from_value fails in the map_err for ListMessagesRequest parsing, log
a stable prefix (e.g., "whatsapp_data:list_messages:parse_error") plus a
correlation field (e.g., a generated short request_id or the raw args summary)
and the parse error; similarly, when whatsapp_rpc::whatsapp_data_list_messages
returns Err in its map_err, log a stable prefix (e.g.,
"whatsapp_data:list_messages:rpc_error"), include the same correlation field and
the RPC error details before converting to anyhow::Error; update the execute
function to generate/propagate the correlation id and use log::debug! or trace!
for these paths so bad args and upstream failures are distinguishable.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/TEST-COVERAGE-MATRIX.md`:
- Around line 330-331: Change the duplicate matrix leaf label "10.3.3" for the
"Real-Time vs Delayed Sync" row to "10.3.4" in the TEST-COVERAGE-MATRIX.md table
(the row with the description "Real-Time vs Delayed Sync" and reference
`src/openhuman/channels/tests/runtime_dispatch.rs`), and update any
manually-maintained rolled-up totals or index references elsewhere in the matrix
to reflect the new numbering.

In `@docs/whatsapp-data-flow.md`:
- Around line 9-39: In docs/whatsapp-data-flow.md update the fenced diagram
block to include a language tag (for example "text") after the opening triple
backticks so the markdown linter stops flagging MD040; locate the diagram fenced
block (the ASCII diagram between the triple backticks) and change the opening
fence from ``` to ```text (no other changes).

---

Nitpick comments:
In `@src/openhuman/tools/impl/whatsapp_data/list_messages.rs`:
- Around line 60-67: Add explicit debug/trace logging to the error branches in
execute: when serde_json::from_value fails in the map_err for
ListMessagesRequest parsing, log a stable prefix (e.g.,
"whatsapp_data:list_messages:parse_error") plus a correlation field (e.g., a
generated short request_id or the raw args summary) and the parse error;
similarly, when whatsapp_rpc::whatsapp_data_list_messages returns Err in its
map_err, log a stable prefix (e.g., "whatsapp_data:list_messages:rpc_error"),
include the same correlation field and the RPC error details before converting
to anyhow::Error; update the execute function to generate/propagate the
correlation id and use log::debug! or trace! for these paths so bad args and
upstream failures are distinguishable.

In `@src/openhuman/tools/impl/whatsapp_data/search_messages.rs`:
- Around line 51-69: In execute (async fn execute in
whatsapp_data/search_messages.rs) add error-path logging and safe correlation
fields: when serde_json::from_value fails, log a prefixed debug/error message
(e.g. "[tool][whatsapp_data][parse]") including any available stable correlation
IDs (if parsable) or at least a request context placeholder; after parsing but
before calling whatsapp_rpc::whatsapp_data_search_messages, log an entry with
prefix "[tool][whatsapp_data][rpc]" and include non-PII fields from the parsed
SearchMessagesRequest (account_id, chat_id, limit, and query_len =
req.query.len() or 0) but do not log the raw query text; on RPC error (map_err
branch) log the error with a prefixed "[tool][whatsapp_data][rpc][error]" plus
the same correlation fields and then return the mapped anyhow error as before so
behavior is unchanged.

In `@src/openhuman/whatsapp_data/global.rs`:
- Around line 79-90: The public function reset_for_tests currently ships in
production; restrict it to test-only usage by gating it with a compile-time cfg
(e.g., #[cfg(any(test, feature = "test-support"))]) or move it into a test-only
module so it is not exported in normal builds; update the crate feature list to
add an optional "test-support" feature and enable that feature from integration
tests instead of leaving reset_for_tests public, and ensure the symbol
referencing GLOBAL_STORE remains accessible under the same cfg so the
implementation compiles only for test builds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 666c7ea7-b56f-4fa1-bc3b-af104df34376

📥 Commits

Reviewing files that changed from the base of the PR and between 26ff73a and 7b4443d.

📒 Files selected for processing (11)
  • docs/TEST-COVERAGE-MATRIX.md
  • docs/whatsapp-data-flow.md
  • src/openhuman/agent/agents/orchestrator/agent.toml
  • src/openhuman/tools/impl/mod.rs
  • src/openhuman/tools/impl/whatsapp_data/list_chats.rs
  • src/openhuman/tools/impl/whatsapp_data/list_messages.rs
  • src/openhuman/tools/impl/whatsapp_data/mod.rs
  • src/openhuman/tools/impl/whatsapp_data/search_messages.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/whatsapp_data/global.rs
  • tests/json_rpc_e2e.rs

Comment thread docs/TEST-COVERAGE-MATRIX.md Outdated
Comment thread docs/whatsapp-data-flow.md Outdated
oxoxDev and others added 2 commits May 8, 2026 18:03
…nsai#1341)

Person-name queries like "what did Alice say" only return rows when
search also looks at the `sender` column — the sender's own name almost
never appears inside the message body. Each branch of the dynamic-WHERE
match now reads `body LIKE ? OR sender LIKE ?` with the existing pattern
re-bound. Adds a `search_messages_matches_sender_name` unit test that
ingests a message whose body has no occurrence of the sender's name and
asserts the search still surfaces it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…yhumansai#1341)

Smoke testing turned up the orchestrator picking `search_messages` for
time-window queries like "who messaged me on WhatsApp in the last 3
hours" and getting empty results because the search hits message body
text only. Re-shapes the three tool descriptions so:

- `list_chats` advertises the recency / contact-resolution intents and
  notes the `last_message_ts DESC` ordering.
- `list_messages` becomes the explicit time-window read tool, with a
  `since_ts`/`until_ts` recipe paired with `current_time`.
- `search_messages` says "do NOT use for time-only queries" and calls
  out the new sender-column match landed in the previous commit.

Pure-text update — no behaviour change in the Rust code paths.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
src/openhuman/whatsapp_data/store.rs (2)

369-434: ⚡ Quick win

Add branch-selection diagnostics to search_messages.

This function now has four query branches but logs only the final row count. Add a debug entry log with stable fields (scope, has_account_filter, has_chat_filter, limit, query_len) so branch behavior is observable without logging raw query text.

Proposed patch
 pub fn search_messages(&self, req: &SearchMessagesRequest) -> Result<Vec<WhatsAppMessage>> {
     if req.query.trim().is_empty() {
         return Ok(vec![]);
     }
     let conn = self.open_conn()?;
     let limit = req.limit.unwrap_or(20) as i64;
     let pattern = format!("%{}%", req.query.replace('%', "\\%").replace('_', "\\_"));
+    let scope = match (&req.account_id, &req.chat_id) {
+        (Some(_), Some(_)) => "account+chat",
+        (Some(_), None) => "account",
+        (None, Some(_)) => "chat",
+        (None, None) => "all",
+    };
+    log::debug!(
+        "[whatsapp_data] search_messages start scope={} has_account_filter={} has_chat_filter={} limit={} query_len={}",
+        scope,
+        req.account_id.is_some(),
+        req.chat_id.is_some(),
+        limit,
+        req.query.chars().count()
+    );

As per coding guidelines: "Use log / tracing at debug or trace level on RPC entry/exit, error paths, state transitions, and any branch that is hard to infer from tests; include stable prefixes ... Never log ... full PII."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/whatsapp_data/store.rs` around lines 369 - 434, search_messages
currently executes one of four SQL branches but only logs the final row count;
add a debug log immediately after selecting the branch (before
executing/collecting rows) that records stable, non-PII diagnostics:
scope="search_messages", has_account_filter = req.account_id.is_some(),
has_chat_filter = req.chat_id.is_some(), limit = limit, query_len =
pattern.len() (or pattern.chars().count()) so branch selection is observable
without emitting raw query text or message bodies; place this debug call in the
same scope where msgs is determined (inside the match handling for
req.account_id/req.chat_id) or immediately after the match but before returning
msgs, using the module's logging facility (e.g., log::debug! or tracing::debug!)
and do not include any PII like account/chat IDs, message bodies, or sender
identifiers.

616-667: 🏗️ Heavy lift

store.rs is beyond the size guideline—split tests out of this module.

This file is already far above the 500-line target and keeps growing with additional tests. Move #[cfg(test)] mod tests to a sibling test module/file (e.g., store_tests.rs) so store runtime code stays focused and easier to navigate.

As per coding guidelines: "Keep source files to ≤ ~500 lines per file; split modules when growing larger."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/whatsapp_data/store.rs` around lines 616 - 667, The tests block
(including search_messages_matches_sender_name and other #[cfg(test)] items)
should be moved out of the oversized store.rs into a sibling test module file:
create a new file (e.g., store_tests.rs) containing #[cfg(test)] mod tests { use
super::*; /* paste all test functions (make_store(),
search_messages_matches_sender_name, etc.) and any test helpers */ } and remove
the in-file #[cfg(test)] mod tests from store.rs; ensure any symbols referenced
by tests (make_store, SearchMessagesRequest, upsert_messages, upsert_chats,
ChatMeta, IngestMessage) have the needed visibility (pub(crate) or pub) so the
tests can access them after moving.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/openhuman/whatsapp_data/store.rs`:
- Around line 369-434: search_messages currently executes one of four SQL
branches but only logs the final row count; add a debug log immediately after
selecting the branch (before executing/collecting rows) that records stable,
non-PII diagnostics: scope="search_messages", has_account_filter =
req.account_id.is_some(), has_chat_filter = req.chat_id.is_some(), limit =
limit, query_len = pattern.len() (or pattern.chars().count()) so branch
selection is observable without emitting raw query text or message bodies; place
this debug call in the same scope where msgs is determined (inside the match
handling for req.account_id/req.chat_id) or immediately after the match but
before returning msgs, using the module's logging facility (e.g., log::debug! or
tracing::debug!) and do not include any PII like account/chat IDs, message
bodies, or sender identifiers.
- Around line 616-667: The tests block (including
search_messages_matches_sender_name and other #[cfg(test)] items) should be
moved out of the oversized store.rs into a sibling test module file: create a
new file (e.g., store_tests.rs) containing #[cfg(test)] mod tests { use
super::*; /* paste all test functions (make_store(),
search_messages_matches_sender_name, etc.) and any test helpers */ } and remove
the in-file #[cfg(test)] mod tests from store.rs; ensure any symbols referenced
by tests (make_store, SearchMessagesRequest, upsert_messages, upsert_chats,
ChatMeta, IngestMessage) have the needed visibility (pub(crate) or pub) so the
tests can access them after moving.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93b95a59-cc3a-4941-a84a-4609162a694b

📥 Commits

Reviewing files that changed from the base of the PR and between 7b4443d and 9b03c82.

📒 Files selected for processing (4)
  • src/openhuman/tools/impl/whatsapp_data/list_chats.rs
  • src/openhuman/tools/impl/whatsapp_data/list_messages.rs
  • src/openhuman/tools/impl/whatsapp_data/search_messages.rs
  • src/openhuman/whatsapp_data/store.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/tools/impl/whatsapp_data/list_messages.rs
  • src/openhuman/tools/impl/whatsapp_data/search_messages.rs
  • src/openhuman/tools/impl/whatsapp_data/list_chats.rs

oxoxDev and others added 4 commits May 8, 2026 18:16
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tinyhumansai#1341)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rtions) (tinyhumansai#1341)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
src/openhuman/tools/impl/whatsapp_data/list_chats.rs (1)

37-40: ⚡ Quick win

Bound limit to keep tool responses predictable and safe.

Line 37 currently enforces only a minimum. Add a hard upper bound (schema + runtime validation) so a single call can’t request an excessively large result set and bloat tool output/context.

Proposed patch
             "limit": {
                 "type": "integer",
                 "minimum": 1,
+                "maximum": 200,
                 "description": "Maximum chats to return (default 50)."
             },
         let req: ListChatsRequest = serde_json::from_value(args).map_err(|e| {
             log::debug!("[tool][whatsapp_data] list_chats invalid_args error={e}");
             anyhow::anyhow!("invalid arguments for whatsapp_data_list_chats: {e}")
         })?;
+        if let Some(limit) = req.limit {
+            if limit > 200 {
+                return Err(anyhow::anyhow!(
+                    "invalid arguments for whatsapp_data_list_chats: `limit` must be <= 200"
+                ));
+            }
+        }

Also applies to: 51-63

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/openhuman/tools/impl/whatsapp_data/list_chats.rs` around lines 37 - 40,
The "limit" parameter currently has only a minimum; add a hard upper bound in
the JSON schema (add "maximum": 100 or your chosen cap) and enforce that cap at
runtime by introducing a MAX_LIMIT constant and validating the incoming limit in
the list_chats handler (or the function that parses request params) — return a
clear error if limit > MAX_LIMIT or clamp it to MAX_LIMIT before querying.
Update both the schema entry for "limit" and the runtime validation path in
list_chats (or the params parsing function) so schema and runtime behavior
match.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@src/openhuman/tools/impl/whatsapp_data/list_chats.rs`:
- Around line 37-40: The "limit" parameter currently has only a minimum; add a
hard upper bound in the JSON schema (add "maximum": 100 or your chosen cap) and
enforce that cap at runtime by introducing a MAX_LIMIT constant and validating
the incoming limit in the list_chats handler (or the function that parses
request params) — return a clear error if limit > MAX_LIMIT or clamp it to
MAX_LIMIT before querying. Update both the schema entry for "limit" and the
runtime validation path in list_chats (or the params parsing function) so schema
and runtime behavior match.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 60301b3b-8cdc-427b-8446-b14e04211e7c

📥 Commits

Reviewing files that changed from the base of the PR and between 9b03c82 and f6c255e.

📒 Files selected for processing (6)
  • docs/TEST-COVERAGE-MATRIX.md
  • docs/whatsapp-data-flow.md
  • src/openhuman/tools/impl/whatsapp_data/list_chats.rs
  • src/openhuman/tools/impl/whatsapp_data/list_messages.rs
  • src/openhuman/tools/impl/whatsapp_data/search_messages.rs
  • src/openhuman/whatsapp_data/global.rs
✅ Files skipped from review due to trivial changes (1)
  • docs/TEST-COVERAGE-MATRIX.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/tools/impl/whatsapp_data/search_messages.rs
  • src/openhuman/whatsapp_data/global.rs
  • src/openhuman/tools/impl/whatsapp_data/list_messages.rs

@senamakel senamakel merged commit 0f6cc58 into tinyhumansai:main May 9, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WhatsApp data is stored locally but unavailable to agent tasks

2 participants