feat(composio): per-toolkit tool curation, user scopes, and Gmail HTML→markdown#643
Conversation
- Introduced `get_user_scopes` and `set_user_scopes` functions to manage per-toolkit user scope preferences, allowing for read, write, and admin classifications. - Updated `all_controller_schemas` and `all_registered_controllers` to include new schemas for user scope management. - Implemented `evaluate_tool_visibility` to determine tool visibility based on user-defined scopes, enhancing security and control over tool actions. - Added `UserScopePref` struct to store user preferences and integrated it with memory storage for persistence. - Enhanced existing tools to respect user scope preferences during execution, ensuring actions align with user-defined permissions. These changes improve the flexibility and security of toolkit interactions, allowing users to customize their access levels for different actions.
- Updated the GMAIL_CURATED constant to include additional tools for reading and writing emails, such as GMAIL_LIST_MESSAGES, GMAIL_LIST_THREADS, GMAIL_GET_ATTACHMENT, and GMAIL_FORWARD_MESSAGE. - Improved organization of tools by categorizing them under distinct sections for reading messages, managing drafts, and handling labels. - Enhanced admin tools with new functionalities like GMAIL_BATCH_DELETE_MESSAGES and GMAIL_UNTRASH_THREAD, improving overall email management capabilities. These changes provide a more comprehensive toolkit for interacting with Gmail, enhancing user experience and functionality.
- Reformatted the `ADMIN` and `GMAIL_CURATED` constants for better readability by aligning the entries vertically. - Enhanced the clarity of the `classify_unknown` function by improving the structure of the constants, making it easier to maintain and understand. - Updated test assertions in `toolkit_from_slug` for consistency in formatting, ensuring clearer test outputs. These changes improve the overall readability and maintainability of the codebase, aligning with ongoing refactoring efforts.
…ration - Introduced a new GitHub provider module, including a curated catalog of GitHub actions tailored for common tasks such as repository management, issue tracking, and pull request handling. - Implemented the `catalog_for_toolkit` function to allow fallback to a static curated list for toolkits without a native provider, ensuring consistent tool visibility and access. - Updated the `evaluate_tool_visibility` function to prioritize curated tools from registered providers, enhancing the overall user experience and security by enforcing whitelist checks. These changes expand the capabilities of the Composio toolkit, providing users with a comprehensive set of tools for interacting with GitHub, while maintaining a focus on user-defined permissions and visibility.
- Reformatted the `GITHUB_CURATED`, `NOTION_CURATED`, and other tool constants for better readability by aligning entries vertically. - Enhanced the clarity of the code structure, making it easier to maintain and understand. - Updated related documentation to reflect the changes in formatting and organization. These changes improve the overall readability and maintainability of the codebase, aligning with ongoing refactoring efforts.
- Added a new module `catalogs.rs` containing curated tool lists for Slack, Discord, Google Calendar, Google Drive, Google Docs, and more, enhancing the Composio toolkit's integration capabilities. - Updated the `mod.rs` file to include the new `catalogs` module and modified the `catalog_for_toolkit` function to support these curated lists, allowing for better organization and access to toolkit actions. - This addition improves the overall functionality and user experience by providing a comprehensive set of tools for interacting with popular platforms.
…esponses - Introduced a new `post_process` module to handle per-toolkit response modifications, specifically for converting HTML content to markdown format. - Enhanced the `ComposioExecuteTool` to apply post-processing on successful responses, improving the clarity and usability of data returned from Gmail. - Added tests to ensure the correct functionality of HTML detection and conversion, validating the integrity of the post-processing logic. These changes enhance the user experience by streamlining the handling of HTML content in responses, making it more suitable for further processing and display.
…ved readability - Moved HTML detection markers in the `looks_like_html` function to a more structured format, enhancing clarity and maintainability. - This change aligns with ongoing efforts to improve code organization and readability within the post-processing module.
…ol constants - Reformatted the `SLACK_CURATED`, `DISCORD_CURATED`, and `GOOGLECALENDAR_CURATED` constants for improved readability by aligning entries vertically. - This change enhances the clarity and maintainability of the code, aligning with ongoing refactoring efforts to improve code organization.
|
Warning Rate limit exceeded
Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 0 minutes and 18 seconds. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (8)
📝 WalkthroughWalkthroughAdds curated per-toolkit action catalogs, provider hooks, per-user KV-backed scope preferences, enforcement in list/execute flows, Gmail HTML→Markdown post-processing, frontend scope UI and RPCs, GitHub curated catalog, and a single Cargo dependency ( Changes
Sequence DiagramssequenceDiagram
actor User
participant Frontend as Frontend
participant Agent as Agent Layer
participant Tools as composio_list_tools
participant Catalog as catalog_for_toolkit
participant Backend as Composio Backend
participant Scope as UserScopePref
User->>Frontend: Open connect/manage screen
Frontend->>Agent: Request tool list
Agent->>Tools: List tools for toolkit
Tools->>Catalog: Get curated catalog
Catalog-->>Tools: CuratedTool slice
Tools->>Backend: List all tools
Backend-->>Tools: Full tool list
Tools->>Scope: Load user scope preferences
Scope-->>Tools: UserScopePref
Tools->>Tools: Filter tools by catalog & scope
Tools-->>Agent: Filtered tool list
Agent-->>Frontend: Display filtered tools and scope toggles
sequenceDiagram
actor User
participant Frontend as Frontend
participant Agent as Agent Layer
participant Execute as composio_execute
participant Catalog as catalog_for_toolkit
participant Scope as UserScopePref
participant Backend as Composio Backend
participant PostProc as post_process
User->>Frontend: Trigger tool execution
Frontend->>Agent: Execute slug
Agent->>Execute: Execute with slug
Execute->>Catalog: Get curated catalog
Catalog-->>Execute: CuratedTool slice or None
alt Not whitelisted
Execute-->>Agent: Error: tool not whitelisted
else Whitelisted
Execute->>Scope: Load user scope preferences
Scope-->>Execute: UserScopePref
alt Scope denied
Execute-->>Agent: Error: scope denied
else Scope allowed
Execute->>Backend: Execute action
Backend-->>Execute: Response data
Execute->>PostProc: Post-process by toolkit (Gmail: html→md)
PostProc-->>Execute: Processed data
Execute-->>Agent: Success result
end
end
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
…prefs Adds the three scope toggles to the connected-state of the ComposioConnectModal so users can gate which Composio actions the agent may invoke per integration. Loads the stored pref via `composio_get_user_scopes` once the modal lands in the connected phase and persists changes through `composio_set_user_scopes` with optimistic updates and rollback on error. - Adds `getUserScopes` / `setUserScopes` to composioApi. - Adds `ComposioUserScopePref` type mirror. - Renders accessible role="switch" toggles with hint text per row.
- Streamlined the definition of SCOPE_ROWS in ComposioConnectModal by removing unnecessary line breaks, enhancing code readability and maintainability. - Updated the agent.toml configuration to include "composio_execute" in the tools list, expanding the capabilities of the integrations agent.
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/openhuman/composio/providers/tool_scope.rs (1)
86-102: Potential toolkit extraction mismatch for multi-word toolkit slugs.
toolkit_from_slugextracts only the prefix before the first underscore, which works forGMAIL_*→"gmail"but may cause issues for toolkits with underscores in their names. For example:
MICROSOFT_TEAMS_GET_CHAT→"microsoft"(relies oncatalog_for_toolkitaccepting both"microsoft"and"microsoft_teams")The
catalog_for_toolkitfunction inmod.rsdoes handle"microsoft"as a fallback, so this works. However, if Composio ever usesGOOGLE_CALENDAR_*slugs (with underscore) instead ofGOOGLECALENDAR_*, those would extract to"google"and miss the catalog lookup entirely.Consider adding a brief doc comment noting this assumption about slug formats.
📝 Suggested documentation addition
/// Extract the toolkit slug from a Composio action slug. /// /// All Composio action slugs follow the convention `<TOOLKIT>_<VERB>_…` /// (e.g. `GMAIL_SEND_EMAIL` → `gmail`). Returns the lowercased prefix /// before the first underscore, or `None` if the slug has no underscore. +/// +/// **Note:** Multi-word toolkits like Microsoft Teams use compound prefixes +/// (`MICROSOFT_TEAMS_*` → `"microsoft"`). The caller should ensure +/// `catalog_for_toolkit` handles both forms (e.g. `"microsoft"` and +/// `"microsoft_teams"`). pub fn toolkit_from_slug(slug: &str) -> Option<String> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/providers/tool_scope.rs` around lines 86 - 102, toolkit_from_slug currently splits the slug at the first '_' and lowercases that prefix which breaks for multi-word toolkit names that themselves contain underscores; update the function's documentation (near toolkit_from_slug) to state the assumption that Composio toolkit identifiers must not contain underscores (e.g., that slugs are of the form `<TOOLKIT>_<VERB>_…` and that fallback handling is performed by catalog_for_toolkit for known aliases like "microsoft"), and mention that if multi-word toolkit names with underscores are expected the extraction logic (toolkit_from_slug) must be revised to consult catalog_for_toolkit or a mapping instead.src/openhuman/composio/providers/user_scopes.rs (1)
140-151: Consider logging the toolkit name consistently.The log at line 145 uses
%toolkit(the raw input) whileload()logs%key(the normalized key). For consistency and grep-ability, consider logging both or using the normalized key here too.📝 Suggested consistency improvement
None => { + let key = kv_key(toolkit); tracing::debug!( - toolkit = %toolkit, + toolkit = %key, "[composio][scopes] memory not ready, using default pref" ); UserScopePref::default() }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/providers/user_scopes.rs` around lines 140 - 151, The debug log in load_or_default uses the raw toolkit string but load() logs a normalized key, causing inconsistency; update load_or_default (and its tracing::debug call) to log the same normalized key that load() emits (or log both toolkit and normalized key) by reusing the same normalization logic used by load() so the tracing fields match (reference: load_or_default, load, toolkit, key, UserScopePref).Cargo.toml (1)
19-19: Considerhtml2mdcrate maintenance status before committing.The
0.2version spec is appropriate and will resolve to the latest 0.2.15 release. However, the crate has not been updated in over a year (last update January 2025). While it is stable and widely used (58 reverse dependencies, 500k+ downloads), more actively maintained alternatives exist, such ashtmd(v0.5.4, last updated April 2026). If long-term maintenance is a concern, evaluate switching to a more actively developed HTML-to-Markdown converter.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@Cargo.toml` at line 19, The Cargo.toml currently pins the html2md dependency as html2md = "0.2"; evaluate the crate's maintenance before committing by comparing it to the more actively maintained alternative (htmd v0.5.4): run cargo audit/ cargo outdated and test replacing html2md with htmd in Cargo.toml, update any code using the html2md API to the htmd equivalents, run the test suite and linting, and if htmd is compatible adopt htmd = "0.5" (or keep html2md = "0.2" with a comment documenting the maintenance decision).src/openhuman/composio/tools.rs (1)
98-103: Consider usingzipfor clearer intent.The index-based
retainpattern works correctly but is slightly fragile ifkeepandresp.toolsever got out of sync. Azip-based approach would be more idiomatic.♻️ Optional: Alternative using drain_filter pattern
- let mut idx = 0; - resp.tools.retain(|_| { - let keep_it = keep[idx]; - idx += 1; - keep_it - }); + let mut i = 0; + resp.tools.retain(|_| (keep.get(i).copied().unwrap_or(false), i += 1).0);Or if you prefer explicit iteration:
resp.tools = resp .tools .drain(..) .zip(keep) .filter_map(|(t, k)| k.then_some(t)) .collect();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/tools.rs` around lines 98 - 103, The current retain loop uses an external index (idx) to consult the `keep` boolean slice while mutating `resp.tools`, which is fragile; replace this with a zip-based approach that pairs each tool with its corresponding boolean (zip `resp.tools` with `keep`) and then filter/collect only the tools where the boolean is true (or use drain + zip + filter_map) so you avoid the manual `idx` and make intent clearer; update the code around `resp.tools`/`keep` and remove `idx` and the `retain` call.src/openhuman/composio/schemas.rs (1)
54-55: Add regression coverage for the two new schema keys.Good wiring in Line 54–55 and Line 105–112. One follow-up:
every_known_schema_key_resolves(Line 619+) still omitsget_user_scopesandset_user_scopes, so a future match-arm regression for these keys would not be caught.✅ Suggested test update
let keys = [ "list_toolkits", "list_connections", "authorize", "delete_connection", "list_tools", "execute", "get_user_profile", "sync", "list_trigger_history", + "get_user_scopes", + "set_user_scopes", ];Also applies to: 105-112
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/schemas.rs` around lines 54 - 55, The test every_known_schema_key_resolves is missing the new schema keys get_user_scopes and set_user_scopes; update that test to include schemas("get_user_scopes") and schemas("set_user_scopes") in its expected set so the match-arm regression for those keys will be caught (search for the function/ test every_known_schema_key_resolves and add the two schemas("...") entries to the list/collection it validates).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/openhuman/composio/providers/post_process.rs`:
- Line 76: The current byte-slice `&s[..4096]` can panic on UTF-8 boundaries;
change the truncation to use a UTF-8-safe boundary: compute the safe end via
`s.floor_char_boundary(4096)` (or use `s.get(..4096)` and fall back) and then
set `head` using that boundary (i.e., replace the `let head = if s.len() > 4096
{ &s[..4096] } else { s };` logic to use `s.floor_char_boundary(4096)` or
`s.get(..4096).unwrap_or(s)` so `head` is always a valid UTF-8 slice).
In `@src/openhuman/composio/schemas.rs`:
- Around line 531-551: The new RPC handlers handle_get_user_scopes and
handle_set_user_scopes lack structured debug/trace logs; add stable-prefixed
logs at entry (including method name and toolkit value), on important branches
(e.g., when pref is loaded, when memory client is missing), on errors (log the
error with correlation fields) and on successful exit (including returned pref),
using the same grep-friendly prefix (e.g., "composio:scopes:") and include
fields method and toolkit so the memory-not-ready branch (the
crate::openhuman::memory::global::client_if_ready() None case) logs a clear
error before returning. Use the existing logger used in this module for
structured key=value fields and ensure all new log statements appear in both
handle_get_user_scopes and handle_set_user_scopes around async awaits and before
each early return.
---
Nitpick comments:
In `@Cargo.toml`:
- Line 19: The Cargo.toml currently pins the html2md dependency as html2md =
"0.2"; evaluate the crate's maintenance before committing by comparing it to the
more actively maintained alternative (htmd v0.5.4): run cargo audit/ cargo
outdated and test replacing html2md with htmd in Cargo.toml, update any code
using the html2md API to the htmd equivalents, run the test suite and linting,
and if htmd is compatible adopt htmd = "0.5" (or keep html2md = "0.2" with a
comment documenting the maintenance decision).
In `@src/openhuman/composio/providers/tool_scope.rs`:
- Around line 86-102: toolkit_from_slug currently splits the slug at the first
'_' and lowercases that prefix which breaks for multi-word toolkit names that
themselves contain underscores; update the function's documentation (near
toolkit_from_slug) to state the assumption that Composio toolkit identifiers
must not contain underscores (e.g., that slugs are of the form
`<TOOLKIT>_<VERB>_…` and that fallback handling is performed by
catalog_for_toolkit for known aliases like "microsoft"), and mention that if
multi-word toolkit names with underscores are expected the extraction logic
(toolkit_from_slug) must be revised to consult catalog_for_toolkit or a mapping
instead.
In `@src/openhuman/composio/providers/user_scopes.rs`:
- Around line 140-151: The debug log in load_or_default uses the raw toolkit
string but load() logs a normalized key, causing inconsistency; update
load_or_default (and its tracing::debug call) to log the same normalized key
that load() emits (or log both toolkit and normalized key) by reusing the same
normalization logic used by load() so the tracing fields match (reference:
load_or_default, load, toolkit, key, UserScopePref).
In `@src/openhuman/composio/schemas.rs`:
- Around line 54-55: The test every_known_schema_key_resolves is missing the new
schema keys get_user_scopes and set_user_scopes; update that test to include
schemas("get_user_scopes") and schemas("set_user_scopes") in its expected set so
the match-arm regression for those keys will be caught (search for the function/
test every_known_schema_key_resolves and add the two schemas("...") entries to
the list/collection it validates).
In `@src/openhuman/composio/tools.rs`:
- Around line 98-103: The current retain loop uses an external index (idx) to
consult the `keep` boolean slice while mutating `resp.tools`, which is fragile;
replace this with a zip-based approach that pairs each tool with its
corresponding boolean (zip `resp.tools` with `keep`) and then filter/collect
only the tools where the boolean is true (or use drain + zip + filter_map) so
you avoid the manual `idx` and make intent clearer; update the code around
`resp.tools`/`keep` and remove `idx` and the `retain` call.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: dc4c1b61-4696-476c-92cb-a18918544bfb
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
Cargo.tomlsrc/openhuman/composio/providers/catalogs.rssrc/openhuman/composio/providers/github/mod.rssrc/openhuman/composio/providers/github/tools.rssrc/openhuman/composio/providers/gmail/mod.rssrc/openhuman/composio/providers/gmail/provider.rssrc/openhuman/composio/providers/gmail/tools.rssrc/openhuman/composio/providers/mod.rssrc/openhuman/composio/providers/notion/mod.rssrc/openhuman/composio/providers/notion/provider.rssrc/openhuman/composio/providers/notion/tools.rssrc/openhuman/composio/providers/post_process.rssrc/openhuman/composio/providers/tool_scope.rssrc/openhuman/composio/providers/traits.rssrc/openhuman/composio/providers/user_scopes.rssrc/openhuman/composio/schemas.rssrc/openhuman/composio/tools.rs
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
app/src/components/composio/ComposioConnectModal.tsx (1)
1-1:⚠️ Potential issue | 🟡 MinorPipeline failure: Run Prettier to fix formatting.
The CI check indicates Prettier formatting issues. Run
prettier --writeto fix code style before merging.cd app && yarn prettier --write src/components/composio/ComposioConnectModal.tsx src/lib/composio/composioApi.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/composio/ComposioConnectModal.tsx` at line 1, CI reports Prettier formatting failures for the Composio component files; run Prettier to auto-fix formatting issues by executing the suggested command in the repo root (apply to src/components/composio/ComposioConnectModal.tsx and src/lib/composio/composioApi.ts) so the ComposioConnectModal component and related composioApi module conform to the project's Prettier rules before merging.
🧹 Nitpick comments (3)
app/src/components/composio/ComposioConnectModal.tsx (2)
367-372: Add debug logging for scope toggle operations.Per coding guidelines, critical checkpoints should be logged. The scope toggle flow involves RPC calls and state transitions but has no client-side logging for debugging.
Add logging in handleToggleScope
const handleToggleScope = useCallback( async (key: keyof ComposioUserScopePref) => { if (!scopes || savingScope) return; + console.debug('[composio][scopes] toggling', { toolkit: toolkit.slug, key, from: scopes[key] }); const optimistic: ComposioUserScopePref = { ...scopes, [key]: !scopes[key] }; setScopes(optimistic); setSavingScope(key); setScopeError(null); try { const persisted = await setUserScopes(toolkit.slug, optimistic); + console.debug('[composio][scopes] persisted', { toolkit: toolkit.slug, persisted }); setScopes(persisted); } catch (err) { // Roll back on failure so the toggle reflects reality. + console.warn('[composio][scopes] save failed, rolling back', { toolkit: toolkit.slug, key, err }); setScopes(scopes); const msg = err instanceof Error ? err.message : String(err); setScopeError(`Couldn't save ${key} scope: ${msg}`); } finally { setSavingScope(null); } }, [savingScope, scopes, toolkit.slug] );As per coding guidelines: "Add substantial, development-oriented logs while implementing features or fixes so issues are easy to trace end-to-end".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/composio/ComposioConnectModal.tsx` around lines 367 - 372, Add development-oriented debug logs to the scope toggle flow by instrumenting the handleToggleScope function: log the incoming scope id/identifier and intended new state before the RPC call, log when savingScope state is set/cleared (savingScope), log the RPC response and the updated scopes array on success, and log the full error object and scopeError update on failure; reference ScopeToggles, scopes, savingScope, scopeError and ensure logs include identifying info (scope id/name and timestamps) and error.stack or error.message for traceability.
26-30: Useimport typefor type-only import.
ComposioUserScopePrefis only used as a type annotation in this file (state type, props type, function parameter type). It should be imported withimport typeper coding guidelines.Fix import
-import { - type ComposioConnection, - type ComposioUserScopePref, - deriveComposioState, -} from '../../lib/composio/types'; +import type { ComposioConnection, ComposioUserScopePref } from '../../lib/composio/types'; +import { deriveComposioState } from '../../lib/composio/types';As per coding guidelines: "Use
import typefor TypeScript type-only imports where appropriate".🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/composio/ComposioConnectModal.tsx` around lines 26 - 30, The import currently mixes a type-only symbol with a runtime value; change the import so that ComposioUserScopePref is imported using "import type" (leaving deriveComposioState as a normal import) — e.g., split the line so type-only ComposioUserScopePref (and any other purely-annotative types like ComposioConnection if applicable) are imported with "import type" while deriveComposioState remains a regular import to satisfy the coding guideline.app/src/lib/composio/composioApi.ts (1)
103-125: Add debug logging for traceability.Per coding guidelines, new/changed flows should include debug logs with grep-friendly prefixes for correlation. These RPC calls are critical for the scope enforcement feature but lack any client-side logging.
Add debug logging
export async function getUserScopes(toolkit: string): Promise<ComposioUserScopePref> { + console.debug('[composio][rpc] getUserScopes', { toolkit }); const raw = await callCoreRpc<unknown>({ method: 'openhuman.composio_get_user_scopes', params: { toolkit }, }); - return unwrapCliEnvelope<ComposioUserScopePref>(raw); + const result = unwrapCliEnvelope<ComposioUserScopePref>(raw); + console.debug('[composio][rpc] getUserScopes result', { toolkit, result }); + return result; } export async function setUserScopes( toolkit: string, pref: ComposioUserScopePref ): Promise<ComposioUserScopePref> { + console.debug('[composio][rpc] setUserScopes', { toolkit, pref }); const raw = await callCoreRpc<unknown>({ method: 'openhuman.composio_set_user_scopes', params: { toolkit, ...pref }, }); - return unwrapCliEnvelope<ComposioUserScopePref>(raw); + const result = unwrapCliEnvelope<ComposioUserScopePref>(raw); + console.debug('[composio][rpc] setUserScopes result', { toolkit, result }); + return result; }As per coding guidelines: "Add substantial, development-oriented logs while implementing features or fixes so issues are easy to trace end-to-end" and "use grep-friendly log prefixes ([feature], domain name, or JSON-RPC method)".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/lib/composio/composioApi.ts` around lines 103 - 125, Add grep-friendly debug logs to both getUserScopes and setUserScopes: before calling callCoreRpc log the outgoing JSON-RPC method and toolkit (e.g. processLogger.debug("[composio] calling openhuman.composio_get_user_scopes toolkit=%s", toolkit)), and after unwrapCliEnvelope log the returned pref/response (e.g. processLogger.debug("[composio] openhuman.composio_get_user_scopes response=%o", result)). Do the same for openhuman.composio_set_user_scopes in setUserScopes (log method, toolkit and pref being sent, then log the unwrapped response). Use the existing processLogger (or equivalent) and include the RPC method name in the message for easy grep correlation.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/lib/composio/composioApi.ts`:
- Around line 98-109: Doc comment for the `execute` function is misplaced above
`getUserScopes`; move it so the comment immediately precedes the `execute`
function, or alternatively relocate `getUserScopes` (and `setUserScopes` if
nearby) into the "Read operations" section so the comment ordering reflects
intent; update placement around the symbols `execute`, `getUserScopes`, and
`setUserScopes` to ensure the execute docstring documents `execute` and the read
functions are grouped under the read section.
---
Outside diff comments:
In `@app/src/components/composio/ComposioConnectModal.tsx`:
- Line 1: CI reports Prettier formatting failures for the Composio component
files; run Prettier to auto-fix formatting issues by executing the suggested
command in the repo root (apply to
src/components/composio/ComposioConnectModal.tsx and
src/lib/composio/composioApi.ts) so the ComposioConnectModal component and
related composioApi module conform to the project's Prettier rules before
merging.
---
Nitpick comments:
In `@app/src/components/composio/ComposioConnectModal.tsx`:
- Around line 367-372: Add development-oriented debug logs to the scope toggle
flow by instrumenting the handleToggleScope function: log the incoming scope
id/identifier and intended new state before the RPC call, log when savingScope
state is set/cleared (savingScope), log the RPC response and the updated scopes
array on success, and log the full error object and scopeError update on
failure; reference ScopeToggles, scopes, savingScope, scopeError and ensure logs
include identifying info (scope id/name and timestamps) and error.stack or
error.message for traceability.
- Around line 26-30: The import currently mixes a type-only symbol with a
runtime value; change the import so that ComposioUserScopePref is imported using
"import type" (leaving deriveComposioState as a normal import) — e.g., split the
line so type-only ComposioUserScopePref (and any other purely-annotative types
like ComposioConnection if applicable) are imported with "import type" while
deriveComposioState remains a regular import to satisfy the coding guideline.
In `@app/src/lib/composio/composioApi.ts`:
- Around line 103-125: Add grep-friendly debug logs to both getUserScopes and
setUserScopes: before calling callCoreRpc log the outgoing JSON-RPC method and
toolkit (e.g. processLogger.debug("[composio] calling
openhuman.composio_get_user_scopes toolkit=%s", toolkit)), and after
unwrapCliEnvelope log the returned pref/response (e.g.
processLogger.debug("[composio] openhuman.composio_get_user_scopes response=%o",
result)). Do the same for openhuman.composio_set_user_scopes in setUserScopes
(log method, toolkit and pref being sent, then log the unwrapped response). Use
the existing processLogger (or equivalent) and include the RPC method name in
the message for easy grep correlation.
🪄 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: defaults
Review profile: CHILL
Plan: Pro
Run ID: 127d9891-a73d-4c28-b659-63c2d75b7439
📒 Files selected for processing (3)
app/src/components/composio/ComposioConnectModal.tsxapp/src/lib/composio/composioApi.tsapp/src/lib/composio/types.ts
✅ Files skipped from review due to trivial changes (1)
- app/src/lib/composio/types.ts
…gent prompt The agent prompt for integrations_agent was rendering every action returned by the backend's `composio_list_tools` for each connected toolkit, bypassing the curation/scope filter that the meta-tool layer applies. Concretely the GitHub integrations_agent prompt was showing ~500 actions including non-curated entries like GITHUB_ADD_OR_UPDATE_TEAM_REPOSITORY_PERMISSIONS. Adds `is_action_visible_with_pref(slug, pref)` — a sync helper that mirrors the meta-tool layer's decision logic — and applies it in: - `fetch_connected_integrations_uncached` (bulk session-cached path) - `fetch_toolkit_actions` (per-toolkit spawn-time path) One pref load per toolkit (not per action) keeps the cost minimal.
…filter Simplified the action visibility filter in `fetch_connected_integrations_uncached` by consolidating the filter logic into a single line. This change enhances code readability while maintaining the existing functionality of applying user preferences to the displayed actions.
…e preamble Text-mode subagent prompts were rendering the tool catalog twice: once in the prompt template's `## Tools` section (with the richer `Call as: NAME[arg|arg]` signatures from `prompts::ToolsSection::build`) and once in `### Available Tools` under `## Tool Use Protocol` (`Parameters: name:type, ...` format). For an integrations_agent toolkit spawn (~50 actions) this doubled the tool listing bytes for no informational gain. Keep only the protocol preamble (essential for text mode); the catalog stays in `## Tools`. Removes `summarise_parameters` and `first_line_truncated` which were the sole consumers, plus the now-unused `std::fmt::Write` import.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/openhuman/composio/providers/mod.rs (1)
51-74: Minor: Doc comment block placement is disjointed.The doc comment on lines 51-62 describes
catalog_for_toolkit(mentioning "Static toolkit → curated catalog map" and the lookup key convention), but this function appears at line 91. Meanwhile, lines 63-74 documentis_action_visible_with_prefwhich immediately follows at line 75.Consider moving the
catalog_for_toolkitdoc block to immediately precede that function:📝 Suggested reordering
-/// Static toolkit → curated catalog map. -/// -/// This is consulted by the meta-tool layer alongside any registered -/// provider's [`ComposioProvider::curated_tools`]. It lets toolkits -/// without a full native provider (e.g. `github`, which has no sync -/// logic yet) still benefit from curated whitelisting. -/// -/// Lookup key is the lowercased prefix returned by -/// [`toolkit_from_slug`] applied to the action slug — e.g. -/// `GOOGLECALENDAR_CREATE_EVENT` → `"googlecalendar"`. Multi-segment -/// prefixes like `MICROSOFT_TEAMS_*` are matched via their first -/// segment with an extra arm. /// Synchronous visibility check for a Composio action slug given a /// pre-loaded user scope preference. /// ... pub fn is_action_visible_with_pref(slug: &str, pref: &UserScopePref) -> bool { // ... } +/// Static toolkit → curated catalog map. +/// +/// This is consulted by the meta-tool layer alongside any registered +/// provider's [`ComposioProvider::curated_tools`]. ... pub fn catalog_for_toolkit(toolkit: &str) -> Option<&'static [CuratedTool]> {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/providers/mod.rs` around lines 51 - 74, The doc block describing "Static toolkit → curated catalog map" and lookup key conventions should be moved so it immediately precedes the catalog_for_toolkit function (symbol: catalog_for_toolkit) instead of being placed above is_action_visible_with_pref; then ensure the shorter doc block about the synchronous visibility check remains directly above is_action_visible_with_pref (symbol: is_action_visible_with_pref) so each function's documentation is adjacent to its implementation and the comments reflect the correct function responsibilities.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/openhuman/composio/providers/mod.rs`:
- Around line 51-74: The doc block describing "Static toolkit → curated catalog
map" and lookup key conventions should be moved so it immediately precedes the
catalog_for_toolkit function (symbol: catalog_for_toolkit) instead of being
placed above is_action_visible_with_pref; then ensure the shorter doc block
about the synchronous visibility check remains directly above
is_action_visible_with_pref (symbol: is_action_visible_with_pref) so each
function's documentation is adjacent to its implementation and the comments
reflect the correct function responsibilities.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3e8fc4bc-ba04-4ffc-84cb-4231acbd2a3c
📒 Files selected for processing (4)
app/src/components/composio/ComposioConnectModal.tsxsrc/openhuman/agent/agents/integrations_agent/agent.tomlsrc/openhuman/composio/ops.rssrc/openhuman/composio/providers/mod.rs
✅ Files skipped from review due to trivial changes (1)
- src/openhuman/agent/agents/integrations_agent/agent.toml
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/components/composio/ComposioConnectModal.tsx
…bug) Real fixes: - post_process: walk back to UTF-8 char boundary before truncating to 4096 bytes; previous `&s[..4096]` could panic mid-codepoint. Adds regression test that places a 3-byte char straddling the cutoff. - composioApi: move misplaced `execute` docstring back above `execute` (it had drifted above the new `getUserScopes`). - schemas: include `get_user_scopes` / `set_user_scopes` in the `every_known_schema_key_resolves` test. Diagnosability: - schemas: structured `[composio:scopes]` debug/error logs at entry, exit, and every early-return in `handle_get_user_scopes` / `handle_set_user_scopes` (method + toolkit + pref fields). The memory-not-ready branch now logs an error before returning. - composioApi + ConnectModal: grep-friendly `[composio][scopes]` debug logs around getUserScopes / setUserScopes RPC round-trips and the toggle handler (old → new state, persisted result, errors). - user_scopes: load_or_default now logs the same normalized `key` that `load()` does, so traces correlate across both code paths. Cleanups: - tools: replace external `idx` + `Vec::retain` in `filter_list_tools_response` with a drain + zip + filter_map pattern. - tool_scope: document the no-underscore-in-toolkit-name assumption of `toolkit_from_slug`, and call out the `microsoft_teams` alias. - Cargo.toml: comment on the html2md vs htmd choice. Pushback (no change): - Reviewer asked to split the type-only import in ConnectModal into a separate `import type` line; ESLint's `no-duplicate-imports` rejects that, so the inline `type` form (functionally identical) stays.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/openhuman/agent/harness/subagent_runner.rs (1)
1717-1742: Update stale rustdoc and remove vestigialspecsparameter.Line 1733 no longer uses tool specs, but the rustdoc above still describes per-parameter rendering. Please align docs with current behavior and consider making this helper zero-arg to avoid misleading call patterns.
Suggested refactor
-/// Format a set of `ToolSpec`s as an XML tool-use protocol block +/// Format the XML tool-use protocol block for text mode. /// appended to the system prompt in text mode. Mirrors /// [`crate::openhuman::agent::dispatcher::XmlToolDispatcher::prompt_instructions`] /// — same `<tool_call>{…}</tool_call>` format so the existing /// `parse_tool_calls` helper understands what the model emits. /// -/// Per-parameter rendering is intentionally **compact**: name, type, a -/// "required" marker, and a short one-line description if present. We -/// do **not** serialise the full JSON schema. Composio/Fireworks action -/// schemas for toolkits like Gmail or Notion run multiple KB each — -/// embedding them verbatim blows up the prompt past the model's -/// context window (282k+ tokens for 26 Gmail tools vs a 196k cap). -/// The compact listing keeps the model informed enough to call tools -/// correctly while staying within budget. If the model needs deeper -/// schema detail it can surface the error and the orchestrator will -/// clarify on the next turn. -pub(crate) fn build_text_mode_tool_instructions(_specs: &[ToolSpec]) -> String { +/// Tool names/signatures are rendered by the prompt's `## Tools` section; +/// this helper only adds call-format instructions. +pub(crate) fn build_text_mode_tool_instructions() -> String {- sys.content - .push_str(&build_text_mode_tool_instructions(tool_specs)); + sys.content + .push_str(&build_text_mode_tool_instructions());As per coding guidelines: “New or changed behavior must ship with matching documentation: add concise rustdoc / code comments where the flow is not obvious, and update AGENTS.md, architecture docs, or feature docs when repository rules or user-visible behavior change.”
Also applies to: 1733-1733
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/agent/harness/subagent_runner.rs` around lines 1717 - 1742, The rustdoc for build_text_mode_tool_instructions is stale and the function no longer uses the _specs parameter; remove the unused parameter from the function signature (change pub(crate) fn build_text_mode_tool_instructions(_specs: &[ToolSpec]) -> String to a zero-arg signature), update all call sites that pass ToolSpec slices to call the new zero-arg build_text_mode_tool_instructions(), and revise the function's rustdoc to remove references to per-parameter rendering and explain that it only emits the protocol explanation (the full tool catalog is provided by prompts::ToolsSection::build); ensure any tests or docs referencing the old signature are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@src/openhuman/agent/harness/subagent_runner.rs`:
- Around line 1717-1742: The rustdoc for build_text_mode_tool_instructions is
stale and the function no longer uses the _specs parameter; remove the unused
parameter from the function signature (change pub(crate) fn
build_text_mode_tool_instructions(_specs: &[ToolSpec]) -> String to a zero-arg
signature), update all call sites that pass ToolSpec slices to call the new
zero-arg build_text_mode_tool_instructions(), and revise the function's rustdoc
to remove references to per-parameter rendering and explain that it only emits
the protocol explanation (the full tool catalog is provided by
prompts::ToolsSection::build); ensure any tests or docs referencing the old
signature are updated accordingly.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: ccb9f909-d4a1-43df-b22d-86c60c03eff6
📒 Files selected for processing (2)
src/openhuman/agent/harness/subagent_runner.rssrc/openhuman/composio/ops.rs
✅ Files skipped from review due to trivial changes (1)
- src/openhuman/composio/ops.rs
Summary
read/write/admin.composio-user-scopesnamespace), keyed by toolkit. Defaults to{read:true, write:true, admin:false}. Enforced at bothcomposio_list_tools(filter) andcomposio_execute(reject) insrc/openhuman/composio/tools.rs.openhuman.composio_get_user_scopesandopenhuman.composio_set_user_scopesfor the React settings UI to wire up.composio_executeresponses; first impl converts Gmail HTML bodies to markdown viahtml2md, walking the JSON defensively so it survives Composio response-shape changes.Problem
Composio publishes 60+ actions per toolkit. The agent saw all of them on every turn — burning tokens and frequently picking irrelevant or destructive actions (delete-thread when it meant move-to-trash, modify-IMAP-settings when it meant draft-email). There was also no user-facing knob to gate side-effectful actions per integration. On Gmail, message bodies arrived as raw HTML, costing tokens and making the model parse
<table>/<style>noise to find the actual text.Solution
Curation layer (
src/openhuman/composio/providers/):tool_scope.rs—ToolScope::{Read,Write,Admin},CuratedTool { slug, scope },classify_unknown()heuristic for un-curated toolkits,toolkit_from_slug()extractor.gmail/tools.rs,notion/tools.rs,github/tools.rs— provider-owned curated catalogs.catalogs.rs— single file holding the other 24 toolkit catalogs (catalog-only, no native provider yet).catalog_for_toolkit(toolkit)inproviders/mod.rs— static dispatch (handles bothgoogle_calendar↔googlecalendaraliasing andMICROSOFT_TEAMS_*extracting to"microsoft"via the action-slug prefix rule).ComposioProvider::curated_tools()(defaultNone) — registered providers can also override.Scope layer (
providers/user_scopes.rs):UserScopePref { read, write, admin }, KV-backed (mirrorssync_state.rs's pattern).load_or_default(toolkit)resolves the active memory client and falls back to defaults if memory isn't initialised yet.get_user_scopes/set_user_scopesincomposio/schemas.rs, registered through the existing controller registry per CLAUDE.md's controller-only exposure rule.Enforcement (
composio/tools.rs):evaluate_tool_visibility(slug)resolves toolkit → catalog → curated entry → user pref. ReturnsAllow/BlockedByScope/NotCurated/PassthroughCheckScope.filter_list_tools_responsedrops blocked/non-curated entries from the schema returned to the agent.composio_executerejects blocked / non-curated slugs with structured error messages before the backend call.Post-processing (
providers/post_process.rs):post_process(toolkit, slug, value)dispatcher; gmail walks the JSON, detects HTML strings via cheap substring heuristic (<html,<body,<div,<p>,<table>, …) bounded to the first 4KB, and converts viahtml2md::parse_html.composio_executeafter a successful response (errors pass through verbatim).html2md = \"0.2\"(pure Rust, no system deps).Heavy
[composio][scopes]and[composio][post-process]debug logs at filter / enforcement / conversion points so the reduction in tool count and HTML→markdown size are visible per turn.Submission Checklist
cargo test --lib openhuman::composio::passes (190 tests, including new coverage fortool_scope,user_scopes,post_process).get_user_scopes/set_user_scopescontrollers and end-to-end execute filtering is a follow-up; the unit-level coverage exercises the full filter/enforce logic.//!on every new module;///on the new public types and functions.Impact
composio_list_toolspayload drops from "every action this toolkit publishes" to a curated subset (typically 15–30 actions vs. 60+). Gmail HTML→markdown additionally cuts message-body bytes substantially on emails with marketing markup.adminscope for that toolkit. Defaultread+writekeeps day-to-day flows working.classify_unknownheuristic against the user pref.Related
app/; JSON-RPC E2E coverage for the new controllers; consider per-toolkit post-processors for other HTML-heavy responses (outlook bodies, notion page exports).Summary by CodeRabbit