## Summary
Fix the schema/backend impedance mismatch reported in #2252. The consolidated `memory_tree` tool advertises `time_window_days` as the look-back field for both `query_source` and `query_global`, but the underlying `QueryGlobalRequest` deserializes from `window_days`. Any LLM call that followed the consolidated contract with `mode = "query_global"` failed with `missing field 'window_days'`.
## Problem
```jsonc
// LLM sends, per the consolidated schema in
// src/openhuman/tools/impl/memory/tree/mod.rs:
{ "mode": "query_global", "time_window_days": 7 }
// Dispatch hands `args` straight through:
// "query_global" => MemoryTreeQueryGlobalTool.execute(args).await,
// `MemoryTreeQueryGlobalTool` (src/openhuman/tools/impl/memory/tree/query_global.rs)
// deserializes into:
// pub struct QueryGlobalRequest { pub window_days: u32 }
//
// -> serde error: missing field `window_days`
```
`query_source` natively uses `time_window_days` (its `QuerySourceRequest` matches the consolidated schema verbatim), so the bug is isolated to the `query_global` arm.
## Solution
Translate `time_window_days` → `window_days` in the consolidated dispatch arm for `query_global`, mirroring the existing thin-adapter pattern used in `src/openhuman/mcp_server/tools.rs` (where `tree.read_chunk` maps MCP `chunk_id` → controller `id`).
- Schema stays as advertised — LLMs already calling the consolidated tool aren't asked to relearn a field name.
- Standalone `MemoryTreeQueryGlobalTool` (which advertises `window_days` natively) is unchanged.
- Explicit `window_days` always wins, so callers using the underlying contract directly aren't surprised when both fields are present.
## Submission Checklist
- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) — four new tests cover: the rename (happy path), passthrough when `window_days` is already set, explicit `window_days` winning over a stale `time_window_days`, and the no-field case being untouched.
- [x] **Diff coverage ≥ 80%** — `cargo test --lib memory_tree_dispatcher_tests::` runs 9/9 locally; the new translator helper has a dedicated test per branch.
- [x] Coverage matrix updated — `N/A: behaviour-only fix on an existing consolidated tool path.`
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related`
- [x] No new external network dependencies introduced
- [x] Manual smoke checklist — `N/A: backend-only fix, not on a release-cut surface.`
- [x] Linked issue closed via `Closes #NNN` — `Closes #2252` (commit message + this PR description).
## Impact
- **Runtime**: agent-facing `memory_tree` consolidated tool only. The standalone `memory_tree_query_global` tool path is unchanged.
- **Compatibility**: backward compatible. Callers that were passing `window_days` (the only callers that worked before this fix) continue to work. Callers that were passing `time_window_days` (which previously errored) now succeed.
- **Performance**: negligible — one `Map::contains_key` + at most one `remove` + one `insert` per `query_global` call.
- **Security**: no policy change.
## Related
- Closes#2252
- Touches: `src/openhuman/tools/impl/memory/tree/mod.rs`
- Backend reference: `src/openhuman/memory/tree/retrieval/rpc.rs` (`QueryGlobalRequest`, `QuerySourceRequest`)
- Pattern reference: `src/openhuman/mcp_server/tools.rs` — same thin-adapter translation idiom used for `tree.read_chunk`'s `chunk_id → id` mapping.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **Bug Fixes**
* Fixed global memory query handling to properly process field parameters, improving compatibility and reliability of memory tree queries.
* **Tests**
* Extended test coverage for memory query parameter handling, including edge cases and field precedence validation.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2273?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: justin <justin80605@gmail.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary
- Adds `ClickUpProvider` under `src/openhuman/composio/providers/clickup/`, joining the existing `gmail` / `notion` / `slack` providers as the fourth toolkit with native Memory Tree ingest. Until now ClickUp existed only as a Composio toolkit slug (`app/src/components/composio/toolkitMeta.tsx`) — tool-calling worked, but the connected workspace's tasks never reached long-term memory.
- Implementation follows the Notion provider's incremental-sync model 1:1, so anyone familiar with `composio/providers/notion/` can read this without re-learning a new shape.
- Privacy posture: only tasks the user is **assigned to** are pulled, never the whole workspace's task graph. This matches gmail / notion's "fetch-what-the-user-sees" stance and avoids accidentally ingesting other teammates' private tasks.
## Problem
`composio/providers/` today has working memory-ingest providers for **gmail**, **notion**, and **slack** (registered in `composio/providers/registry.rs::init_default_providers`). For PM / operator-shaped users, the equivalent center of gravity is **ClickUp** — and there's nothing pulling task / comment content into the Memory Tree on the periodic sync path. Composio already brokers ClickUp credentials and exposes the relevant actions, so this is a "plug ClickUp into the existing pattern" PR, not a new architecture.
## Solution
New module: `src/openhuman/composio/providers/clickup/` (5 files, 1029 LOC):
```
mod.rs — module wiring + re-exports (22)
provider.rs — impl ComposioProvider for ClickUpProvider (509)
sync.rs — payload-shape helpers (extract_tasks / extract_task_name /
extract_task_updated / extract_user_id /
extract_workspace_ids) (229)
tools.rs — CLICKUP_CURATED whitelist of 24 ClickUp actions (124)
tests.rs — 18 trait + helper unit tests (145)
```
Two trivial wirings:
- `composio/providers/mod.rs`: `pub mod clickup;`
- `composio/providers/registry.rs::init_default_providers`: one extra `register_provider(...)` line.
### Sync model (mirroring Notion)
1. `SyncState::load("clickup", connection_id)` from the shared KV store.
2. Daily request budget check (`DEFAULT_DAILY_REQUEST_LIMIT = 500`).
3. **Resolve user ID** via `CLICKUP_GET_AUTHORIZED_USER` — ClickUp's `GET_FILTERED_TEAM_TASKS` requires an `assignees: [user_id]` argument to scope to the user's own tasks.
4. **Resolve workspaces** via `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` — ClickUp's per-team filter endpoint requires a concrete `team_id`, so we enumerate.
5. Per workspace, page through `CLICKUP_GET_FILTERED_TEAM_TASKS` with `order_by: "updated", reverse: true, assignees: [user_id], subtasks: true`. Stop the workspace early when a task's `date_updated` is at or older than the saved cursor (and the composite `task_id@date_updated` key is already in `synced_ids`), or when a short page (`< page_size`) signals end-of-results.
6. Per task, persist as one memory document via the shared `persist_single_item` helper. Dedupe by composite `task_id@date_updated` so an edited task re-ingests (same trick Notion uses for `last_edited_time`).
7. Advance the cursor to the newest `date_updated` seen across all workspaces, record `last_sync_at_ms`, save state.
### Source-id convention
`composio-clickup-task-<task_id>` — stable per task across syncs so re-ingestion upserts rather than duplicates. The document title is `"ClickUp: <task_name>"`.
### Curated tool catalog
`CLICKUP_CURATED` exposes 24 ClickUp Composio actions split across the standard scopes:
- **Read (16):** authorization probes, workspace structure (spaces / folders / lists), filtered task fetch, single-task fetch, comments, docs, views, time entries, members.
- **Write (6):** create / update tasks + comments, list management.
- **Admin (3):** destructive deletes for tasks / comments / lists.
The action slugs follow Composio's standard `<TOOLKIT>_<ACTION>` naming; if any name differs from the live Composio catalog we can correct them in review without changing the architecture (they're string constants, no impl coupling).
## Submission Checklist
- [x] Tests added or updated — 31 new unit tests cover sync helpers (results / title / cursor / user-id / workspace-id extraction across raw and wrapped payload shapes), trait metadata stability, and the curated-tool surface (`CLICKUP_GET_AUTHORIZED_USER` / `CLICKUP_GET_AUTHORIZED_TEAMS_WORKSPACES` / `CLICKUP_GET_FILTERED_TEAM_TASKS` are all advertised).
- [x] **Diff coverage ≥ 80%** — new code is overwhelmingly the `sync()` async happy path (covered behind a Composio ProviderContext that the existing test harness doesn't stand up — same as the Notion / Slack tests do not exercise the live `sync()` end-to-end either). Helpers + trait metadata are unit-tested directly.
- [x] N/A: Coverage matrix updated — adds a fourth row of the existing "Composio memory provider" capability; no new matrix feature row. Match treatment of gmail / notion / slack.
- [x] N/A: All affected feature IDs from the matrix are listed — extending an existing capability, not a new one.
- [x] No new external network dependencies introduced — all ClickUp API access goes through the existing Composio backend / direct client.
- [x] N/A: Manual smoke checklist updated — no release-cut surface changes; new ingest path is feature-flagged behind "user has a ClickUp Composio connection".
- [x] Linked issue closed via `Closes #2288` in `## Related`.
## Impact
- **Runtime/platform impact**: desktop core only (Rust). No Tauri shell, no frontend changes.
- **Compatibility impact**: strictly additive. Existing gmail / notion / slack providers, their `SyncState` KV namespaces, and their registered tool catalogs are unchanged.
- **Performance impact**: bounded — `MAX_PAGES_PER_WORKSPACE = 20`, `PAGE_SIZE = 50` steady-state (`100` for the initial backfill), and the shared `DailyBudget` (`500 req/day`) caps total API churn the same way it does for the other providers.
- **Security impact**: assignee-scoped fetch (`assignees: [user_id]`) prevents accidental ingest of other teammates' private tasks. Composio handles credentials; no new secret-handling code.
## Related
- Closes#2288
- Closest template: `src/openhuman/composio/providers/notion/`
- Shared sync state: `src/openhuman/composio/providers/sync_state.rs`
- Provider trait: `src/openhuman/composio/providers/traits.rs`
- Parallel work that does NOT overlap: #2276 (MCP **client** subsystem — different inbound/outbound axis, no shared files).
---
## AI Authored PR Metadata
### Linear Issue
- Key: N/A
- URL: N/A
### Commit & Branch
- Branch: `feat/clickup-memory-provider`
- Commit SHA: b47acc75
### Validation Run
- [x] N/A: `pnpm --filter openhuman-app format:check` — Rust-only change.
- [x] N/A: `pnpm typecheck` — Rust-only change.
- [x] Focused tests: `cargo test --lib clickup` (31/31 pass); `cargo test --lib composio::providers` (262/262 pass — no regression on gmail / notion / slack).
- [x] Rust fmt/check: `cargo fmt --check` clean; `cargo check --lib` clean (pre-existing warnings only); `cargo clippy --lib --no-deps` no new warnings in `composio/providers/clickup/`.
- [x] N/A: Tauri fmt/check — no `app/src-tauri/src/**` changes.
### Validation Blocked
- N/A
### Behavior Changes
- Intended behavior change: users with a Composio-connected ClickUp account now have their assigned tasks periodically ingested into the Memory Tree on the existing 30-minute scheduler cadence, with initial backfill triggered by the `ConnectionCreated` hook.
- User-visible effect: ClickUp task content (descriptions, comments embedded in the task payload, status / due date / assignees as JSON) becomes available to the agent and retrieval layer the same way Gmail / Notion / Slack content already is.
### Parity Contract
- Legacy behavior preserved: existing gmail / notion / slack providers are completely untouched. Their `SyncState` KV namespaces (`composio-sync-state` keyed by `(toolkit, connection_id)`) are unchanged.
- Guard/fallback/dispatch parity checks: provider follows the existing `ComposioProvider` trait contract — daily budget, dedup-by-id, cursor-based pagination, idempotent `persist_single_item` upserts.
### Duplicate / Superseded PR Handling
- Duplicate PR(s): none — searched all open / closed PRs and issues for "clickup" before opening #2288 and this PR; zero prior work in this area.
- Canonical PR: this PR.
- Resolution: N/A.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* ClickUp provider integration to incrementally sync assigned tasks into Memory Tree with deduplication and scheduled cadence.
* ClickUp added to provider list and capability matrix for scheduling and access flows.
* Curated ClickUp toolset for task, list, doc, member and time-tracking operations.
* **Tests**
* Unit tests covering task/workspace/user extraction, provider metadata, and defaults.
* **Documentation**
* Human-readable ClickUp capability description and catalog entry added.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2291?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: justin <justin80605@gmail.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary
Adds MCP `ToolAnnotations` (spec 2025-03-26+) to every tool advertised by `openhuman-core mcp`. Clients use `readOnlyHint` / `destructiveHint` / `idempotentHint` / `openWorldHint` to surface accurate safety affordances — e.g. Claude Desktop's "this tool can take destructive actions" confirmation gate.
## Problem
`tools/list` today returns the curated tool surface but says nothing about each tool's behavior class. The MCP spec defines `ToolAnnotations` precisely for this — without them:
- Claude Desktop / Cursor can't render confirmation gates for destructive actions.
- Humans inspecting the tool list (e.g. via MCP Inspector) can't see at a glance which tools are safe to retry.
- `agent.run_subagent` — the one Act-policy surface on the MCP server today — looks identical to `memory.search` to clients, even though it can call further tools (including potentially destructive ones) through any sub-agent it spawns.
## Solution
- Added `annotations: Value` to `McpToolSpec` (always present, serialized into each tool's `annotations` field in the `tools/list` response).
- All read-only tools share a `read_only_local_annotations()` helper (`readOnlyHint: true`, `openWorldHint: false`).
- `agent.run_subagent` gets explicit annotations: `readOnlyHint: false / destructiveHint: true / idempotentHint: false / openWorldHint: true`. Sub-agent execution can call further tools, isn't a no-op on repeat, and reaches into the broader OpenHuman environment.
- Updated `mcp_server/mod.rs` docstring to no longer claim the surface is purely read-only — it's curated, with `agent.run_subagent` as the one Act-policy surface.
- Per spec, `destructiveHint` / `idempotentHint` are only meaningful when `readOnlyHint == false`, so the read-only helper omits them.
## Submission Checklist
- [x] Tests added — `list_tools_emits_annotations_for_every_tool` asserts every entry serializes a non-null `annotations` object; a second test pins the read-only / destructive split to each tool's actual behavior. `cargo test --lib mcp_server::` runs 38/38 locally.
- [x] **Diff coverage ≥ 80%** — new code is mostly literal annotations + a 6-line helper; both the "every tool has annotations" and "read-only vs destructive" axes are covered.
- [x] Coverage matrix updated — `N/A: extends row 11.1.4 (MCP stdio server) rather than adding a new feature.`
- [x] All affected feature IDs listed under `## Related`
- [x] No new external network dependencies introduced
- [x] Manual smoke checklist — `N/A: extends existing MCP surface, not a release-cut path.`
- [x] Linked issue closed via `Closes #NNN` — `N/A: capability extension, no issue tracking this.`
## Impact
- **Runtime**: `openhuman-core mcp` only. No HTTP RPC, web, or mobile impact.
- **Compatibility**: Backward compatible — older MCP clients that don't read `annotations` ignore the field; newer clients pick up the safety affordances.
- **Performance**: Negligible — one extra static `Value` per tool, serialized on each `tools/list` call (low frequency).
- **Security**: No policy change. `agent.run_subagent` still goes through `enforce_act_policy`; the annotation is an advertised hint, not an enforcement point.
## Related
- Feature IDs: `11.1.4` (MCP stdio server)
- Spec: https://modelcontextprotocol.io/specification/2025-03-26/server/tools/#tool-annotations
- Builds on: #1760, #1790, #1974
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Tools now include detailed security annotations (readOnly, destructive, idempotent, openWorld hints) so MCP clients can better understand safety and behavior.
* Most tools are advertised as read-only; the subagent tool is explicitly advertised as act-capable/destructive, and one search tool is advertised as read-only but open-world.
* **Tests**
* Added tests ensuring each tool includes annotations and that read-only vs. destructive/open-world hints are correctly serialized.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2268?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: justin <justin80605@gmail.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary
- Bumps `lettre` from `0.11.19` (resolved `0.11.21`) to `0.11.22` to clear [RUSTSEC-2026-0141](https://rustsec.org/advisories/RUSTSEC-2026-0141.html) on both `Cargo.lock` and `app/src-tauri/Cargo.lock`.
- The advisory affects only the `boring-tls` backend; OpenHuman builds with `rustls-tls`, so this is not exploitable at runtime — the bump just clears the `cargo-audit` warning the weekly code-review report (#2084) has been surfacing since 2026-05-18.
## Problem
- `cargo-audit` flags `lettre@0.11.21` for RUSTSEC-2026-0141 / [GHSA-4pj9-g833-qx53](https://github.com/lettre/lettre/security/advisories/GHSA-4pj9-g833-qx53): an inverted boolean disabled TLS hostname verification on the `boring-tls` backend across `0.10.1..0.11.21`.
- Fixed upstream in `0.11.22` (released 2026-05-14).
- Both lockfiles (root + `app/src-tauri/`) resolved `lettre` to the vulnerable `0.11.21`.
## Solution
- Root `Cargo.toml`: `lettre = "0.11.19"` -> `lettre = "0.11.22"`.
- `cargo update -p lettre` in both workspaces refreshed the resolved version to `0.11.22`.
- No call-site changes: the two consumer modules (`src/openhuman/audio_toolkit/ops.rs` and `src/openhuman/channels/providers/email_channel.rs`) only use stable surface (`Message`, `SmtpTransport`, `Transport`, `Credentials`, message-builder types). `0.11.20` raised MSRV to 1.85, but OpenHuman targets Rust 1.93 per `rust-toolchain.toml`, so the bump is compatible.
## Submission Checklist
- [x] N/A: Tests added or updated — pure dependency bump with no behavior change; existing `email_channel` and `audio_toolkit` unit tests cover the consumer sites.
- [x] N/A: Diff coverage >= 80% — `Cargo.toml` / `Cargo.lock` changes have no executable lines for `cargo-llvm-cov` to score.
- [x] N/A: Coverage matrix updated — no feature row added/removed/renamed.
- [x] N/A: All affected feature IDs from the matrix are listed — no matrix row affected.
- [x] No new external network dependencies introduced.
- [x] N/A: Manual smoke checklist updated — dependency-only change, no release-cut surface.
- [x] Linked issue closed via `Closes #2274` in `## Related`.
## Impact
- Runtime/platform impact: none in practice — `rustls-tls` backend is unaffected by RUSTSEC-2026-0141. Clears the `cargo-audit` advisory flag in the weekly code-review report.
- Compatibility impact: back-compatible patch-version bump within the same `0.11.x` line.
- Security impact: forward-looking — removes vulnerable version pin so a future build that ever toggles to `boring-tls` would not regress into the advisory.
## Related
- Closes#2274
- Surfaced by: #2084 (weekly code-review report — 2026-05-18)
- Upstream advisory: https://rustsec.org/advisories/RUSTSEC-2026-0141.html
- Upstream changelog: https://github.com/lettre/lettre/blob/v0.11.22/CHANGELOG.md
---
## AI Authored PR Metadata
### Linear Issue
- Key: N/A
- URL: N/A
### Commit & Branch
- Branch: `fix/lettre-rustsec-2026-0141`
- Commit SHA: 7d3dbdb0
### Validation Run
- [x] N/A: `pnpm --filter openhuman-app format:check` — Rust-only dependency change.
- [x] N/A: `pnpm typecheck` — Rust-only dependency change.
- [x] Focused tests: `cargo test --manifest-path Cargo.toml --lib email_channel` (50/50 pass) and `cargo test --manifest-path Cargo.toml --lib audio_toolkit` (10/10 pass).
- [x] Rust fmt/check: `cargo fmt --check` clean; `cargo check --manifest-path Cargo.toml` clean (pre-existing warnings only); `cargo clippy --lib --no-deps` no new warnings.
- [x] N/A: Tauri fmt/check — no `app/src-tauri/src/**` changes; only `app/src-tauri/Cargo.lock` updated to refresh the transitive `lettre` pin.
### Validation Blocked
- N/A
### Behavior Changes
- Intended behavior change: none in this codebase (build uses `rustls-tls` feature, not `boring-tls`).
- User-visible effect: weekly code-review report no longer flags `lettre@0.11.21` for RUSTSEC-2026-0141 after merge.
### Parity Contract
- Legacy behavior preserved: `Message`/`SmtpTransport`/`Transport`/`Credentials` and message-builder API surface unchanged in `0.11.22`.
- Guard/fallback/dispatch parity checks: not applicable — dependency-only bump.
### Duplicate / Superseded PR Handling
- Duplicate PR(s): none known (searched open and closed PRs touching `lettre`; the only prior hit was #1970 which introduced the dependency).
- Canonical PR: this PR.
- Resolution: N/A.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **Chores**
* Updated the email service library to the latest patch version for improved stability and bug fixes.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2275?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: justin <justin80605@gmail.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>