Repository navigation
feat(composio): client-side trigger enable/disable toggles - #1110
Conversation
Mirror backend PR #671's new trigger management endpoints in the core RPC layer and surface them as per-trigger toggles inside the connection modal. Replaces the now-removed server-side fire-and-forget trigger provisioning that ran on every authorize. - Rust core: composio_list_available_triggers, composio_list_triggers, composio_enable_trigger, composio_disable_trigger pass-throughs. - Frontend: matching wrappers in composioApi + a Triggers section in ComposioConnectModal that loads the catalog + active list in parallel and flips toggles via enable/disable RPCs. - Triggers with requiredConfigKeys render as disabled rows with a "Needs configuration" hint; richer config UI deferred.
…verage
- Extract TriggerToggles into its own module so it can be unit-tested
in isolation; the modal now imports it.
- 15 RTL tests cover loading / empty / error states, signature matching
for static + GitHub triggers, the requiredConfigKeys "needs config"
hint, and the enable/disable optimistic flip.
- 7 mock-backend tests for each new ComposioClient method (input
validation + HTTP envelope round-trips).
- 6 mock-backend tests for the new ops (list/enable/disable + 4xx surfacing).
- Schema tests for the 4 new controllers' input shapes and registration.
- Mock backend gains /agent-integrations/composio/{connections,
triggers,triggers/available} so the WDIO spec and any future spec
can drive the trigger flow deterministically.
- New WDIO spec drives list/enable/disable RPCs against the running
core sidecar and asserts the modal renders the Triggers section
for an ACTIVE Gmail connection.
diff-cover ≥83% on changed lines (only handler bodies in schemas.rs
remain uncovered — they require a tokio runtime + full config).
📝 WalkthroughWalkthroughThis PR adds trigger management to the Composio integration, enabling users to enable/disable webhook-like triggers per toolkit connection. It introduces a new TypeScript component, API wrappers, Rust client methods, RPC operations/schemas, mock backend handlers, and comprehensive tests spanning frontend, backend, and e2e. ChangesTrigger Management Feature
Estimated code review effort🎯 4 (Complex) | ⏱️ ~40 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Review rate limit: 1/5 review remaining, refill in 44 minutes and 12 seconds. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
src/openhuman/composio/types.rs (1)
152-228: ⚡ Quick winAdd serde coverage for new trigger DTO field mappings.
The new structs rely on multiple renamed optional fields (
defaultConfig,requiredConfigKeys,connectionId,triggerConfig) but there’s no direct unit coverage for these mappings yet.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/types.rs` around lines 152 - 228, Add unit tests that serialize/deserialize JSON to verify the serde rename/default/optional behavior for the new trigger DTO fields: test ComposioAvailableTrigger handles "defaultConfig" -> default_config and "requiredConfigKeys" -> required_config_keys (including absent/null cases), test ComposioAvailableTriggerRepo round-trips owner/repo, test ComposioActiveTrigger handles "connectionId" -> connection_id and "triggerConfig" -> trigger_config and "state" optional handling, and test responses ComposioAvailableTriggersResponse, ComposioActiveTriggersResponse, ComposioEnableTriggerResponse ("triggerId"/"connectionId") and ComposioDisableTriggerResponse (deleted default) properly map JSON names to struct fields; locate tests around types.rs structs (ComposioAvailableTrigger, ComposioActiveTrigger, ComposioEnableTriggerResponse, ComposioDisableTriggerResponse, etc.) and assert serde_json::from_str and to_string round-trips and optional/skip_serializing_if behavior.src/openhuman/composio/ops_tests.rs (1)
690-700: ⚡ Quick winAssert forwarded query params in the available-triggers ops test.
This handler currently doesn’t verify
toolkit/connectionId, so forwarding regressions can slip through unnoticed.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/composio/ops_tests.rs` around lines 690 - 700, The test HTTP handler (closure starting at get(|Query(q): Query<HashMap<String, String>>| async move { ...) must verify forwarded query params; update it to assert the presence (and optionally values) of "toolkit" and "connectionId" in the Query<HashMap<String, String>> q and include both in the JSON response (e.g. add "_echoed_connectionId": q.get("connectionId") alongside the existing "_echoed_toolkit") so the test can assert forwarding, or explicitly panic/assert when those keys are missing/mismatched.
🤖 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/test/e2e/specs/composio-triggers-flow.spec.ts`:
- Around line 149-153: The test uses a raw WDIO selector/element.click for the
"Manage" button (selector 'button=Manage'), which breaks platform-agnostic E2E
rules; replace that block to call the shared helper clickNativeButton('Manage')
(or the existing wrapper) and await it, ensuring the helper is imported from the
element-helpers module and used instead of el/isExisting/click; keep any
surrounding try/await logic but use clickNativeButton so the spec remains
platform-agnostic and consistent with other tests (you can also call
waitForWebView before/after if needed).
In `@scripts/mock-api-core.mjs`:
- Around line 1152-1154: The mock API currently masks missing required fields by
defaulting parsedBody?.slug to "UNKNOWN" and parsedBody?.connectionId to "" and
still returns a triggerId; change this to validate the enable-trigger payload
and reject requests with missing or empty required fields instead of
auto-filling them: check parsedBody.slug and parsedBody.connectionId (and any
other required fields) and if absent or blank return an HTTP 400 response with a
clear error message, only generate the triggerId (`triggerId =
\`ti-${Date.now()}\``) after validation succeeds so invalid requests do not
produce fake triggers.
- Around line 1178-1182: The handler always returns { deleted: true } even when
no trigger was removed; update the logic around
parseBehaviorJson("composioActiveTriggers", []), the active and next arrays, and
the setMockBehavior call so you compute whether a deletion occurred (e.g.,
compare active.length vs next.length or check existence of id in active) and
return json(res, 200, { success: true, data: { deleted: <boolean> } }) with the
correct boolean; ensure you still call setMockBehavior("composioActiveTriggers",
JSON.stringify(next)) only when appropriate and keep the same response shape and
use of id, active, next, setMockBehavior, and json.
In `@src/openhuman/composio/client.rs`:
- Around line 266-268: The DELETE path currently inserts raw trigger_id into the
URL in the call to
self.raw_delete::<ComposioDisableTriggerResponse>(&format!(...)), which will
break on reserved characters; percent-encode trigger_id before formatting the
path (e.g., use a percent-encoding utility such as the percent-encoding or
url::form_urlencoded helper) and pass the encoded value into the format! call so
the path becomes "/agent-integrations/composio/triggers/{encoded_trigger_id}"
when invoking raw_delete::<ComposioDisableTriggerResponse>.
In `@src/openhuman/composio/ops.rs`:
- Around line 367-370: The current return always logs "composio: disabled
trigger {trigger_id}" regardless of resp.deleted; update the RpcOutcome::new
call to produce the message based on resp.deleted (e.g., if resp.deleted is
true, message "composio: disabled trigger {trigger_id}", otherwise "composio:
did not disable trigger {trigger_id}" or include the boolean), by branching or
formatting before calling RpcOutcome::new and using that computed string in the
vec![...] so the log accurately reflects resp.deleted; reference resp,
trigger_id, and RpcOutcome::new to locate and change the code.
In `@src/openhuman/composio/schemas.rs`:
- Around line 448-462: The schema comment for the FieldSchema "connection_id"
and the runtime in handle_list_available_triggers are out of sync: the doc says
connection_id is required for toolkit "github" but
handle_list_available_triggers doesn't enforce it. Fix by either (A) updating
the FieldSchema comment to state that connection_id is optional for all toolkits
(leave runtime unchanged), or (B) adding explicit validation in
handle_list_available_triggers to require a connection_id when toolkit ==
"github" (return a clear validation error or Result::Err) so runtime matches the
schema; reference the FieldSchema entry for "connection_id" and the function
handle_list_available_triggers when making the change.
---
Nitpick comments:
In `@src/openhuman/composio/ops_tests.rs`:
- Around line 690-700: The test HTTP handler (closure starting at get(|Query(q):
Query<HashMap<String, String>>| async move { ...) must verify forwarded query
params; update it to assert the presence (and optionally values) of "toolkit"
and "connectionId" in the Query<HashMap<String, String>> q and include both in
the JSON response (e.g. add "_echoed_connectionId": q.get("connectionId")
alongside the existing "_echoed_toolkit") so the test can assert forwarding, or
explicitly panic/assert when those keys are missing/mismatched.
In `@src/openhuman/composio/types.rs`:
- Around line 152-228: Add unit tests that serialize/deserialize JSON to verify
the serde rename/default/optional behavior for the new trigger DTO fields: test
ComposioAvailableTrigger handles "defaultConfig" -> default_config and
"requiredConfigKeys" -> required_config_keys (including absent/null cases), test
ComposioAvailableTriggerRepo round-trips owner/repo, test ComposioActiveTrigger
handles "connectionId" -> connection_id and "triggerConfig" -> trigger_config
and "state" optional handling, and test responses
ComposioAvailableTriggersResponse, ComposioActiveTriggersResponse,
ComposioEnableTriggerResponse ("triggerId"/"connectionId") and
ComposioDisableTriggerResponse (deleted default) properly map JSON names to
struct fields; locate tests around types.rs structs (ComposioAvailableTrigger,
ComposioActiveTrigger, ComposioEnableTriggerResponse,
ComposioDisableTriggerResponse, etc.) and assert serde_json::from_str and
to_string round-trips and optional/skip_serializing_if behavior.
🪄 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: c2c689c1-0fe2-4ef3-b95b-2349035c12f0
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (15)
app/src/components/composio/ComposioConnectModal.tsxapp/src/components/composio/TriggerToggles.test.tsxapp/src/components/composio/TriggerToggles.tsxapp/src/lib/composio/composioApi.test.tsapp/src/lib/composio/composioApi.tsapp/src/lib/composio/types.tsapp/test/e2e/specs/composio-triggers-flow.spec.tsscripts/mock-api-core.mjssrc/openhuman/composio/client.rssrc/openhuman/composio/client_tests.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/ops_tests.rssrc/openhuman/composio/schemas.rssrc/openhuman/composio/schemas_tests.rssrc/openhuman/composio/types.rs
| try { | ||
| const el = await $('button=Manage'); | ||
| if (el && (await el.isExisting())) { | ||
| await el.click(); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Use the shared element helper for the “Manage” click path.
Line 150 uses a raw WDIO selector/click instead of the e2e helper layer. Please switch this interaction to clickNativeButton (or an existing helper wrapper) so this spec stays platform-agnostic and consistent with other specs.
As per coding guidelines: “Use element helpers (clickNativeButton, waitForWebView, clickToggle) from app/test/e2e/helpers/element-helpers.ts in E2E tests; never use raw XCUIElementType*” and “Ensure E2E specs are runnable in isolation with platform-agnostic helpers”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/test/e2e/specs/composio-triggers-flow.spec.ts` around lines 149 - 153,
The test uses a raw WDIO selector/element.click for the "Manage" button
(selector 'button=Manage'), which breaks platform-agnostic E2E rules; replace
that block to call the shared helper clickNativeButton('Manage') (or the
existing wrapper) and await it, ensuring the helper is imported from the
element-helpers module and used instead of el/isExisting/click; keep any
surrounding try/await logic but use clickNativeButton so the spec remains
platform-agnostic and consistent with other tests (you can also call
waitForWebView before/after if needed).
| const slug = parsedBody?.slug ?? "UNKNOWN"; | ||
| const connectionId = parsedBody?.connectionId ?? ""; | ||
| const triggerId = `ti-${Date.now()}`; |
There was a problem hiding this comment.
Reject invalid enable-trigger payloads instead of auto-filling placeholders.
Line 1152-Line 1154 currently turns missing required fields into "UNKNOWN" / "", which can let broken client requests pass in E2E unexpectedly.
Suggested fix
- const slug = parsedBody?.slug ?? "UNKNOWN";
- const connectionId = parsedBody?.connectionId ?? "";
+ const slug = String(parsedBody?.slug ?? "").trim();
+ const connectionId = String(parsedBody?.connectionId ?? "").trim();
+ if (!slug || !connectionId) {
+ json(res, 400, {
+ success: false,
+ error: "slug and connectionId are required",
+ });
+ return;
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const slug = parsedBody?.slug ?? "UNKNOWN"; | |
| const connectionId = parsedBody?.connectionId ?? ""; | |
| const triggerId = `ti-${Date.now()}`; | |
| const slug = String(parsedBody?.slug ?? "").trim(); | |
| const connectionId = String(parsedBody?.connectionId ?? "").trim(); | |
| if (!slug || !connectionId) { | |
| json(res, 400, { | |
| success: false, | |
| error: "slug and connectionId are required", | |
| }); | |
| return; | |
| } | |
| const triggerId = `ti-${Date.now()}`; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/mock-api-core.mjs` around lines 1152 - 1154, The mock API currently
masks missing required fields by defaulting parsedBody?.slug to "UNKNOWN" and
parsedBody?.connectionId to "" and still returns a triggerId; change this to
validate the enable-trigger payload and reject requests with missing or empty
required fields instead of auto-filling them: check parsedBody.slug and
parsedBody.connectionId (and any other required fields) and if absent or blank
return an HTTP 400 response with a clear error message, only generate the
triggerId (`triggerId = \`ti-${Date.now()}\``) after validation succeeds so
invalid requests do not produce fake triggers.
| const active = parseBehaviorJson("composioActiveTriggers", []); | ||
| const next = active.filter((t) => t.id !== id); | ||
| setMockBehavior("composioActiveTriggers", JSON.stringify(next)); | ||
| json(res, 200, { success: true, data: { deleted: true } }); | ||
| return; |
There was a problem hiding this comment.
Return accurate delete results for unknown trigger IDs.
Line 1181 always returns deleted: true, even when no trigger matched. That can mask disable-flow bugs.
Suggested fix
const active = parseBehaviorJson("composioActiveTriggers", []);
const next = active.filter((t) => t.id !== id);
+ const deleted = next.length !== active.length;
+ if (!deleted) {
+ json(res, 404, { success: false, error: "Trigger not found" });
+ return;
+ }
setMockBehavior("composioActiveTriggers", JSON.stringify(next));
- json(res, 200, { success: true, data: { deleted: true } });
+ json(res, 200, { success: true, data: { deleted: true } });📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const active = parseBehaviorJson("composioActiveTriggers", []); | |
| const next = active.filter((t) => t.id !== id); | |
| setMockBehavior("composioActiveTriggers", JSON.stringify(next)); | |
| json(res, 200, { success: true, data: { deleted: true } }); | |
| return; | |
| const active = parseBehaviorJson("composioActiveTriggers", []); | |
| const next = active.filter((t) => t.id !== id); | |
| const deleted = next.length !== active.length; | |
| if (!deleted) { | |
| json(res, 404, { success: false, error: "Trigger not found" }); | |
| return; | |
| } | |
| setMockBehavior("composioActiveTriggers", JSON.stringify(next)); | |
| json(res, 200, { success: true, data: { deleted: true } }); | |
| return; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@scripts/mock-api-core.mjs` around lines 1178 - 1182, The handler always
returns { deleted: true } even when no trigger was removed; update the logic
around parseBehaviorJson("composioActiveTriggers", []), the active and next
arrays, and the setMockBehavior call so you compute whether a deletion occurred
(e.g., compare active.length vs next.length or check existence of id in active)
and return json(res, 200, { success: true, data: { deleted: <boolean> } }) with
the correct boolean; ensure you still call
setMockBehavior("composioActiveTriggers", JSON.stringify(next)) only when
appropriate and keep the same response shape and use of id, active, next,
setMockBehavior, and json.
| self.raw_delete::<ComposioDisableTriggerResponse>(&format!( | ||
| "/agent-integrations/composio/triggers/{trigger_id}" | ||
| )) |
There was a problem hiding this comment.
Percent-encode trigger_id before building the DELETE path.
Line 266-Line 268 currently injects raw trigger_id into the URL path. Reserved characters would break routing.
Suggested fix
- self.raw_delete::<ComposioDisableTriggerResponse>(&format!(
- "/agent-integrations/composio/triggers/{trigger_id}"
- ))
+ let trigger_id = urlencoding::encode(trigger_id);
+ self.raw_delete::<ComposioDisableTriggerResponse>(&format!(
+ "/agent-integrations/composio/triggers/{trigger_id}"
+ ))🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/openhuman/composio/client.rs` around lines 266 - 268, The DELETE path
currently inserts raw trigger_id into the URL in the call to
self.raw_delete::<ComposioDisableTriggerResponse>(&format!(...)), which will
break on reserved characters; percent-encode trigger_id before formatting the
path (e.g., use a percent-encoding utility such as the percent-encoding or
url::form_urlencoded helper) and pass the encoded value into the format! call so
the path becomes "/agent-integrations/composio/triggers/{encoded_trigger_id}"
when invoking raw_delete::<ComposioDisableTriggerResponse>.
| Ok(RpcOutcome::new( | ||
| resp, | ||
| vec![format!("composio: disabled trigger {trigger_id}")], | ||
| )) |
There was a problem hiding this comment.
Make disable log message reflect resp.deleted.
Line 367-Line 370 always logs “disabled trigger …” even if backend returns deleted: false.
Suggested fix
- Ok(RpcOutcome::new(
- resp,
- vec![format!("composio: disabled trigger {trigger_id}")],
- ))
+ let log = if resp.deleted {
+ format!("composio: disabled trigger {trigger_id}")
+ } else {
+ format!("composio: trigger {trigger_id} not deleted")
+ };
+ Ok(RpcOutcome::new(resp, vec![log]))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Ok(RpcOutcome::new( | |
| resp, | |
| vec![format!("composio: disabled trigger {trigger_id}")], | |
| )) | |
| let log = if resp.deleted { | |
| format!("composio: disabled trigger {trigger_id}") | |
| } else { | |
| format!("composio: trigger {trigger_id} not deleted") | |
| }; | |
| Ok(RpcOutcome::new(resp, vec![log])) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/openhuman/composio/ops.rs` around lines 367 - 370, The current return
always logs "composio: disabled trigger {trigger_id}" regardless of
resp.deleted; update the RpcOutcome::new call to produce the message based on
resp.deleted (e.g., if resp.deleted is true, message "composio: disabled trigger
{trigger_id}", otherwise "composio: did not disable trigger {trigger_id}" or
include the boolean), by branching or formatting before calling RpcOutcome::new
and using that computed string in the vec![...] so the log accurately reflects
resp.deleted; reference resp, trigger_id, and RpcOutcome::new to locate and
change the code.
| "List the catalog of triggers the caller can enable for a toolkit. \ | ||
| For GitHub, fans out into per-repo entries (requires connection_id).", | ||
| inputs: vec![ | ||
| FieldSchema { | ||
| name: "toolkit", | ||
| ty: TypeSchema::String, | ||
| comment: "Toolkit slug, e.g. 'gmail' or 'github'.", | ||
| required: true, | ||
| }, | ||
| FieldSchema { | ||
| name: "connection_id", | ||
| ty: TypeSchema::Option(Box::new(TypeSchema::String)), | ||
| comment: | ||
| "Optional connection id. Required to enumerate GitHub per-repo entries.", | ||
| required: false, |
There was a problem hiding this comment.
Align GitHub connection_id requirement between schema docs and runtime behavior.
Line 448-Line 462 documents connection_id as required for GitHub, but Line 753-Line 767 does not enforce that condition. Either enforce it in handle_list_available_triggers or relax the schema description.
Also applies to: 753-767
🤖 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 448 - 462, The schema comment
for the FieldSchema "connection_id" and the runtime in
handle_list_available_triggers are out of sync: the doc says connection_id is
required for toolkit "github" but handle_list_available_triggers doesn't enforce
it. Fix by either (A) updating the FieldSchema comment to state that
connection_id is optional for all toolkits (leave runtime unchanged), or (B)
adding explicit validation in handle_list_available_triggers to require a
connection_id when toolkit == "github" (return a clear validation error or
Result::Err) so runtime matches the schema; reference the FieldSchema entry for
"connection_id" and the function handle_list_available_triggers when making the
change.
* feat(remotion): Ghosty character library with transparent MOV variants (tinyhumansai#1059) Co-authored-by: WOZCODE <contact@withwoz.com> * feat(composio/gmail): sync into memory tree (Slack-parity) (tinyhumansai#1056) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(scheduler-gate): throttle background AI on battery / busy CPU (tinyhumansai#1062) * fix(core,cef): run core in-process and stop orphaning CEF helpers on Cmd+Q (tinyhumansai#1061) * ci: add dedicated staging release workflow (tinyhumansai#1066) * fix(sentry): Rust source context + per-release deploy marker (tinyhumansai#405) (tinyhumansai#1067) * fix(welcome): re-enable OAuth buttons with focus/timeout recovery (tinyhumansai#1049) (tinyhumansai#1069) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * chore(dependencies): update pnpm-lock.yaml and Cargo.lock for package… (tinyhumansai#1082) * fix(onboarding): personalize welcome agent greeting with user identity (tinyhumansai#1078) * fix(chat): make agent message bubbles fit content width (tinyhumansai#1083) * Feat/dmg checks (tinyhumansai#1084) * fix(linux): Add X11 platform flags to .deb package launcher (tinyhumansai#1087) Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> * fix(sentry): auto-send React events; collapse core→tauri for desktop (tinyhumansai#1086) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(cef): run blank reload guard on the CEF UI thread (tinyhumansai#1092) * fix(app): reload webview instead of restart_app in dev mode (tinyhumansai#1068) (tinyhumansai#1071) * fix(linux): deliver X11 ozone flags via custom .desktop template (tinyhumansai#1091) * fix(webview-accounts): retry data-dir purge so CEF handle race doesn't leak cookies (tinyhumansai#1076) (tinyhumansai#1081) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * fix(webview/slack): media perms + deep-link isolation (tinyhumansai#1074) (tinyhumansai#1080) Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * ci(release): split staging vs production workflows; promote staging tags (tinyhumansai#1094) * Update release-staging.yml (tinyhumansai#1097) * chore(staging): v0.53.5 * chore(staging): v0.53.6 * ci(staging): cut staging from main; add act local-debug helper (tinyhumansai#1099) * chore(staging): v0.53.7 * fix(ci): correct sentry-cli download URL and trap scope (tinyhumansai#1100) * chore(staging): v0.53.8 * feat(chat): forward thread_id to backend for KV cache locality (tinyhumansai#1095) * fix(ci): bump pinned sentry-cli to 3.4.1 (2.34.2 was never published) (tinyhumansai#1102) * chore(staging): v0.53.9 * fix(ci): drop bash trap in upload_sentry_symbols.sh; inline cleanup (tinyhumansai#1103) * chore(staging): v0.53.10 * refactor(session): flatten session_raw/, switch md to YYYY_MM_DD (tinyhumansai#1098) * Add full Composio managed-auth toolkit catalog (tinyhumansai#1093) * ci: add diff-aware 80% coverage gate (Vitest + cargo-llvm-cov) (tinyhumansai#1104) * feat(scripts): pnpm work + pnpm debug for agent-driven workflows (tinyhumansai#1105) * ci: pull pnpm into CI image, drop redundant setup steps (tinyhumansai#1107) * docs: add Cursor Cloud specific instructions to AGENTS.md (tinyhumansai#1106) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * chore(staging): v0.53.11 * docs: surface 80% coverage gate and scripts/debug runners (tinyhumansai#1108) * feat(app): show Composio integrations as sorted icon grid on Skills (tinyhumansai#1109) Co-authored-by: Cursor Agent <cursoragent@cursor.com> * feat(composio): client-side trigger enable/disable toggles (tinyhumansai#1110) * feat(skills): channels grid + integrations card polish; tolerant Composio trigger decode (tinyhumansai#1112) * chore(staging): v0.53.12 * feat(home): early-bird banner + assistant→agent terminology (tinyhumansai#1113) * feat(updater): in-app auto-update with auto-download + restart prompt (tinyhumansai#677) (tinyhumansai#1114) * chore(claude): add ship-and-babysit slash command (tinyhumansai#1115) * feat(home): EarlyBirdyBanner + agent terminology + LinkedIn enrichment model pin (tinyhumansai#1118) * fix(chat): single onboarding thread in sidebar after wizard (tinyhumansai#1116) Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> * fix: filter out global namespace from citation chips (tinyhumansai#1124) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> * feat(nav): enable Memory tab in BottomTabBar (tinyhumansai#1125) * feat(memory): singleton ingestion + status RPC + UI pill (tinyhumansai#1126) * feat(human): mascot tab with viseme-driven lipsync (staging only) (tinyhumansai#1127) * Fix CEF zombie processes on full app close and restart (tinyhumansai#1128) Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> * Update issue templates for GitHub issue types (tinyhumansai#1146) * feat(human): expand mascot expressions and tighten reply-speech state machine (tinyhumansai#1147) * feat(memory): ingestion pipeline + tree-architecture docs + ops/schemas split (tinyhumansai#1142) * feat(threads): surface live subagent work in parent thread (tinyhumansai#1122) (tinyhumansai#1159) * fix(human): keep mascot mouth animating when TTS ships no viseme data (tinyhumansai#1160) * feat(composio): consume backend markdownFormatted for LLM output (tinyhumansai#1165) * fix(subagent): lazy-register toolkit actions filtered out of fuzzy top-K (tinyhumansai#1162) * feat(memory): user-facing long-term memory window preset (tinyhumansai#1137) (tinyhumansai#1161) * fix(tauri-shell): proactively kill stale openhuman RPC on startup (tinyhumansai#1166) * chore(staging): v0.53.13 * fix(composio): per-action tool consumes backend markdownFormatted (tinyhumansai#1167) * fix(threads): persist selectedThreadId across reloads (tinyhumansai#1168) * feat(memory_tree): switch embed model to bge-m3 (1024-dim, 8K context) (tinyhumansai#1174) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(agent): drop redundant [Memory context] recall injection (tinyhumansai#1173) * chore(memory_tree): drop body-read timeouts on Ollama HTTP calls (tinyhumansai#1171) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(transcript): emit thread_id + fix orchestrator missing cost (tinyhumansai#1169) * fix(composio/gmail): phase out html2md, prefer text/plain MIME part (tinyhumansai#1170) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): markdown output for internal tool results (tinyhumansai#1172) * feat(security): enforce prompt-injection guard before model and tool execution (tinyhumansai#1175) * fix(cef): popup paint dies after first frame — skip blank-page guard for popups (tinyhumansai#1079) (tinyhumansai#1182) Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> * chore(sentry): rename OPENHUMAN_SENTRY_DSN → OPENHUMAN_CORE_SENTRY_DSN (tinyhumansai#1186) * feat(remotion): add yellow mascot character with all animation variants (tinyhumansai#1193) Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(composio): hide raw connection ID, derive friendly label (tinyhumansai#1153) (tinyhumansai#1185) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(windows): align install.ps1 MSI with per-machine scope (tinyhumansai#913) (tinyhumansai#1187) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(tauri): deterministic CEF teardown on full app close (tinyhumansai#1120) (tinyhumansai#1189) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(composio): cap Gmail HTML body before strip (crash mitigation) (tinyhumansai#1191) Co-authored-by: Cursor <cursoragent@cursor.com> * fix(auth): stop stale chat threads after signup (tinyhumansai#1192) Co-authored-by: Cursor <cursoragent@cursor.com> * feat(sentry): staging-only "Trigger Sentry Test" button (tinyhumansai#1072) (tinyhumansai#1183) * chore(staging): v0.53.14 * chore(staging): v0.53.15 * feat(composio): format trigger slugs into human-readable labels (tinyhumansai#1129) (tinyhumansai#1179) Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> * fix(ui): hide unsupported permission UI on non-macOS for Screen Intelligence (tinyhumansai#1194) Co-authored-by: Cursor <cursoragent@cursor.com> * chore(tauri-shell): retire embedded Gmail webview-account flow (tinyhumansai#1181) * feat(onboarding): replace welcome-agent bot with react-joyride walkthrough (tinyhumansai#1180) * chore(release): v0.53.16 * fix(threads): preserve selectedThreadId on cold-boot identity hydration (tinyhumansai#1196) * feat(core): version/shutdown/update RPCs + mid-thread integration refresh (tinyhumansai#1195) * fix(mascot): swap to yellow mascot via @remotion/player (tinyhumansai#1200) * feat(memory_tree): cloud-default LLM, queue priority, entity filter, Memory tab UI (tinyhumansai#1198) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * Persist turn state + restore conversation history on cold-boot (tinyhumansai#1202) * feat(mascot): floating desktop mascot via native NSPanel + WKWebView (macOS) (tinyhumansai#1203) * fix(memory/tree): emit summary children as Obsidian wikilinks (tinyhumansai#1210) Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(tools): coding-harness baseline primitives (tinyhumansai#1205) (tinyhumansai#1208) * docs: add Codex PR checklist for remote agents --------- Co-authored-by: Steven Enamakel <31011319+senamakel@users.noreply.github.com> Co-authored-by: WOZCODE <contact@withwoz.com> Co-authored-by: sanil-23 <sanil@vezures.xyz> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Co-authored-by: Cyrus Gray <144336577+graycyrus@users.noreply.github.com> Co-authored-by: CodeGhost21 <164498022+CodeGhost21@users.noreply.github.com> Co-authored-by: oxoxDev <164490987+oxoxDev@users.noreply.github.com> Co-authored-by: Mega Mind <146339422+M3gA-Mind@users.noreply.github.com> Co-authored-by: Gaurang Patel <ptelgm.yt@gmail.com> Co-authored-by: unn-Known1 <unn-known1@users.noreply.github.com> Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai> Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com> Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Steven Enamakel <senamakel@users.noreply.github.com> Co-authored-by: Steven Enamakel's Droid <enamakel.agent@tinyhumans.ai> Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> Co-authored-by: senamakel-droid <281415773+senamakel-droid@users.noreply.github.com> Co-authored-by: YellowSnnowmann <167776381+YellowSnnowmann@users.noreply.github.com> Co-authored-by: Neil <neil@maha.xyz> Co-authored-by: Neel Mistry <neelmistry@Neels-MacBook-Pro.local> Co-authored-by: obchain <167975049+obchain@users.noreply.github.com> Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Summary
openhuman.composio_list_available_triggers,composio_list_triggers,composio_enable_trigger,composio_disable_trigger).requiredConfigKeys(e.g. Slackchannel) render as disabled rows with a "Needs configuration" hint; richer config UI is intentionally deferred.Why
The backend used to wire every default trigger automatically inside
composioAuthorizeController, which burned tokens on triggers users didn't want. Backend PR #671 removed that and replaced it with explicit enable/disable endpoints. This PR ports the OpenHuman desktop app to that contract.Files
src/openhuman/composio/client.rs—list_available_triggers,list_active_triggers,enable_trigger,disable_triggerHTTP wrappers.src/openhuman/composio/ops.rs— matchingRpcOutcome-returning ops.src/openhuman/composio/schemas.rs— 4 new RPC controllers registered alongside the existing composio surface.src/openhuman/composio/types.rs— wire types matching the backend shapes (AvailableTrigger,ActiveTrigger, etc.).app/src/lib/composio/composioApi.ts+types.ts— TS wrappers + types.app/src/components/composio/TriggerToggles.tsx— new component used by the modal.app/src/components/composio/ComposioConnectModal.tsx— renders<TriggerToggles>when the connection isACTIVE.Tests
composioApi.test.tscovering envelope unwrap + RPC param shapes.TriggerToggles.test.tsxcovering loading / empty / error states, signature matching for static + GitHub triggers, therequiredConfigKeys"needs config" hint, optimistic flip on enable, optimistic removal on disable, and error surfacing.client_tests.rs) covering input validation + HTTP envelope round-trips against an in-process axum mock.ops_tests.rs) covering each new op via the mock backend, plus a 404-surfacing case.composio-triggers-flow.spec.tsdriveslist_available_triggers→list_triggers→enable_trigger→list_triggers→disable_triggeragainst the running core sidecar + shared mock backend, then asserts the modal renders the Triggers section for an ACTIVE Gmail connection.scripts/mock-api-core.mjs) gained/agent-integrations/composio/{connections, triggers, triggers/available}routes that mutatecomposioActiveTriggersbehavior in place so subsequent reads observe each enable/disable.diff-cover≥ 83% on changed lines locally (only the handler bodies inschemas.rsremain uncovered — those need a tokio runtime + full config which the current per-domain test scaffold doesn't provide; their delegated ops are at 100%).Test plan
pnpm typecheckpnpm lint(0 errors, 31 pre-existing warnings)pnpm format:checkpnpm --filter openhuman-app test src/components/composio src/lib/composio— 27 passcargo test -p openhuman --lib openhuman::composio::— 286 passcargo check --manifest-path Cargo.tomldiff-cover lcov-app.info lcov-core.info --compare-branch=main --fail-under=80— 83%bash app/scripts/e2e-run-spec.sh test/e2e/specs/composio-triggers-flow.spec.ts composio-triggers) — needs built.app; not run locally.Related
Summary by CodeRabbit
Release Notes
New Features
Tests