Skip to content

feat(composio): client-side trigger enable/disable toggles - #1110

Merged
senamakel merged 2 commits into
tinyhumansai:mainfrom
senamakel:feat/trigger-ui
May 2, 2026
Merged

senamakel merged 2 commits into
tinyhumansai:mainfrom
senamakel:feat/trigger-ui

Conversation

@senamakel

@senamakel senamakel commented May 2, 2026 •

Copy link
Copy Markdown
Member

Summary

  • Mirror backend tinyhumansai/backend#671's new trigger management endpoints in the core RPC layer (openhuman.composio_list_available_triggers, composio_list_triggers, composio_enable_trigger, composio_disable_trigger).
  • Surface them as per-trigger toggle rows inside the existing Composio connection modal, so triggers are now opt-in from the client instead of being fire-and-forget provisioned server-side on every authorize.
  • Triggers with requiredConfigKeys (e.g. Slack channel) 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_trigger HTTP wrappers.
  • src/openhuman/composio/ops.rs — matching RpcOutcome-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 is ACTIVE.

Tests

  • 7 Vitest cases in composioApi.test.ts covering envelope unwrap + RPC param shapes.
  • 15 RTL tests in TriggerToggles.test.tsx covering loading / empty / error states, signature matching for static + GitHub triggers, the requiredConfigKeys "needs config" hint, optimistic flip on enable, optimistic removal on disable, and error surfacing.
  • 7 Rust client tests (client_tests.rs) covering input validation + HTTP envelope round-trips against an in-process axum mock.
  • 6 Rust ops tests (ops_tests.rs) covering each new op via the mock backend, plus a 404-surfacing case.
  • Schema tests confirming all 4 controllers are registered and their input shapes are correct.
  • WDIO spec composio-triggers-flow.spec.ts drives list_available_triggers → list_triggers → enable_trigger → list_triggers → disable_trigger against the running core sidecar + shared mock backend, then asserts the modal renders the Triggers section for an ACTIVE Gmail connection.
  • Shared mock (scripts/mock-api-core.mjs) gained /agent-integrations/composio/{connections, triggers, triggers/available} routes that mutate composioActiveTriggers behavior in place so subsequent reads observe each enable/disable.

diff-cover ≥ 83% on changed lines locally (only the handler bodies in schemas.rs remain 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 typecheck
  • pnpm lint (0 errors, 31 pre-existing warnings)
  • pnpm format:check
  • pnpm --filter openhuman-app test src/components/composio src/lib/composio — 27 pass
  • cargo test -p openhuman --lib openhuman::composio:: — 286 pass
  • cargo check --manifest-path Cargo.toml
  • diff-cover lcov-app.info lcov-core.info --compare-branch=main --fail-under=80 — 83%
  • WDIO spec on macOS (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

    • Users can now manage triggers for Composio connections—view available triggers and enable/disable them directly within the connection modal
    • Support for multiple trigger types including static triggers and GitHub repository-based triggers with configurable settings
  • Tests

    • Added comprehensive unit, integration, and end-to-end test coverage for trigger management functionality

senamakel added 2 commits May 2, 2026 14:36
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).
@senamakel
senamakel requested a review from a team May 2, 2026 22:00
@coderabbitai

coderabbitai Bot commented May 2, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

This 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.

Changes

Trigger Management Feature

Layer / File(s) Summary
Type Definitions
app/src/lib/composio/types.ts, src/openhuman/composio/types.rs
New TypeScript and Rust types for available triggers (ComposioAvailableTrigger), active triggers (ComposioActiveTrigger), and enable/disable responses, including scope (static | github_repo), optional config/repo details, and activation IDs.
Backend Client & Core Ops
src/openhuman/composio/client.rs, src/openhuman/composio/ops.rs
Composio HTTP client adds four trigger methods: list_available_triggers, list_active_triggers, enable_trigger, disable_trigger. Ops layer wraps each method into RPC handlers that return formatted outcomes and error messages.
RPC Schema Registration
src/openhuman/composio/schemas.rs
Registers four new trigger-management RPC endpoints with parameter structs, ControllerSchema definitions (required/optional fields, descriptions), and handler dispatch functions that parse inputs and call the corresponding ops functions.
Frontend API Wrappers
app/src/lib/composio/composioApi.ts
Wraps the four trigger RPC methods (listAvailableTriggers, listTriggers, enableTrigger, disableTrigger) to call openhuman.composio_* RPC endpoints and unwrap CLI JSON responses.
UI Component
app/src/components/composio/TriggerToggles.tsx
New React component that loads available and active triggers per connection, renders a scrollable list with enable/disable toggles, computes trigger signatures to match active state, handles toggle actions (enable/disable via API), and displays loading/error/empty states and per-trigger config requirements.
Modal Integration
app/src/components/composio/ComposioConnectModal.tsx
Imports and renders TriggerToggles in the 'connected' view when an active Composio connection is present, wiring toolkitSlug, toolkitName, and connectionId.
Mock Backend Support
scripts/mock-api-core.mjs
Adds five new /agent-integrations/composio/… route handlers (GET/POST/DELETE for connections, available triggers, active triggers) that seed/mutate mockBehavior state and return JSON responses matching the backend schema.
Backend Tests
src/openhuman/composio/client_tests.rs, src/openhuman/composio/ops_tests.rs, src/openhuman/composio/schemas_tests.rs
Client tests validate query parameters, input validation, and HTTP method usage; ops tests verify RPC dispatch and error formatting; schema tests confirm endpoint registration, input/output shapes, and controller presence.
Frontend Tests
app/src/lib/composio/composioApi.test.ts, app/src/components/composio/TriggerToggles.test.tsx
API tests mock RPC calls and verify correct endpoint/params mapping; component tests mock API responses, verify signature logic, test UI state (loading/error/empty), toggle interactions (enable/disable), config requirements, and error rendering.
E2E Test
app/test/e2e/specs/composio-triggers-flow.spec.ts
End-to-end spec that signs in, seeds trigger state via RPC, verifies listing/enabling/disabling triggers via core RPC, and asserts the Triggers section renders in the modal UI for an active connection.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Possibly related PRs

  • PR #501: Main PR extends the foundational Composio integration added in PR #501 by wiring a new TriggerToggles UI component and trigger-management RPCs/types/ops into the existing ComposioConnectModal, directly modifying the same Composio integration modules.
  • PR #509: Both PRs add Composio trigger/sync functionality and modify the same Rust Composio surface (client, ops, schemas, types) to add trigger management and provider-trigger capabilities, touching shared domain-level modules.
  • PR #567: Adds extensive tests and test helpers across many Composio modules (client, ops, schemas, types) that validate and exercise the new trigger-management APIs and types introduced in this PR.

🐰 Triggers now toggle with glee,
Gmail and Slack dance in harmony,
Modal connects, signatures compute,
Each webhook gets the Composio salute! 🎯

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.18% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title accurately describes the main change: adding client-side UI toggles for enabling/disabling Composio triggers, which is the primary feature across both frontend and backend implementations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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 @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (2)
src/openhuman/composio/types.rs (1)

152-228: ⚡ Quick win

Add 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 win

Assert 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

📥 Commits

Reviewing files that changed from the base of the PR and between ce40cc7 and dadba11.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (15)
  • app/src/components/composio/ComposioConnectModal.tsx
  • app/src/components/composio/TriggerToggles.test.tsx
  • app/src/components/composio/TriggerToggles.tsx
  • app/src/lib/composio/composioApi.test.ts
  • app/src/lib/composio/composioApi.ts
  • app/src/lib/composio/types.ts
  • app/test/e2e/specs/composio-triggers-flow.spec.ts
  • scripts/mock-api-core.mjs
  • src/openhuman/composio/client.rs
  • src/openhuman/composio/client_tests.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/ops_tests.rs
  • src/openhuman/composio/schemas.rs
  • src/openhuman/composio/schemas_tests.rs
  • src/openhuman/composio/types.rs

Comment on lines +149 to +153
try {
const el = await $('button=Manage');
if (el && (await el.isExisting())) {
await el.click();
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ 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).

Comment thread scripts/mock-api-core.mjs
Comment on lines +1152 to +1154
const slug = parsedBody?.slug ?? "UNKNOWN";
const connectionId = parsedBody?.connectionId ?? "";
const triggerId = `ti-${Date.now()}`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment thread scripts/mock-api-core.mjs
Comment on lines +1178 to +1182
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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +266 to +268
self.raw_delete::<ComposioDisableTriggerResponse>(&format!(
"/agent-integrations/composio/triggers/{trigger_id}"
))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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>.

Comment on lines +367 to +370
Ok(RpcOutcome::new(
resp,
vec![format!("composio: disabled trigger {trigger_id}")],
))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
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.

Comment on lines +448 to +462
"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,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

@senamakel
senamakel merged commit 195c9c5 into tinyhumansai:main May 2, 2026
17 of 19 checks passed
jwalin-shah added a commit to jwalin-shah/openhuman that referenced this pull request May 5, 2026
* 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>
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 2026
senamakel added a commit to nocstah/openhuman that referenced this pull request Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant