Skip to content

feat(composio): inject connected identities into agent prompts#774

Merged
senamakel merged 4 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/691-connected-identities-composio
Apr 22, 2026
Merged

feat(composio): inject connected identities into agent prompts#774
senamakel merged 4 commits into
tinyhumansai:mainfrom
M3gA-Mind:feat/691-connected-identities-composio

Conversation

@M3gA-Mind
Copy link
Copy Markdown
Contributor

@M3gA-Mind M3gA-Mind commented Apr 22, 2026

Summary

  • implement issue Build user identity from the skills and other places #691 (Composio-only) with a standardized identity_set callback in the provider lifecycle and ProviderUserProfile.profile_url support
  • persist connected identity fragments as durable FacetType::Skill profile facets using deterministic keys (skill:{toolkit}:{identifier}:{field}), and add loaders/renderers for merged identity context
  • inject a unified Connected Identities section into welcome, orchestrator, and integrations agent system prompts so inference can reference cross-platform identity
  • include related tests and memory notes updates; branch also includes existing vendor bump commit 0eb58a37

Linked issue

Closes #691

Test plan

  • cargo test --manifest-path Cargo.toml composio::providers::profile -- --nocapture
  • cargo test --manifest-path Cargo.toml prompt::tests -- --nocapture
  • cargo fmt --manifest-path Cargo.toml
  • cargo check --manifest-path Cargo.toml
  • yarn typecheck
  • yarn lint
  • yarn format:check
  • yarn build
  • yarn tauri dev (smoke start)

Made with Cursor

Summary by CodeRabbit

  • New Features

    • Prompts now include a “Connected Identities” section showing linked provider accounts; provider profile URLs are surfaced when available.
    • Can list and remove connected identity entries; connection-deleted events are emitted.
  • Refactor

    • Unified identity persistence path and migrated stored identity keys to a new skill-oriented schema.
  • Tests

    • Updated tests and fixtures for the new prompt context field.
  • Chores

    • Updated vendored Tauri CEF and installer DRY_RUN handling.

Standardize provider identity persistence with a shared identity_set hook and skill-scoped facet keys so connected account identity survives restarts and is merged into inference context. Add connected identity rendering in welcome, orchestrator, and integrations_agent prompts so agents can reference cross-platform user identity during conversations.

Closes tinyhumansai#691

Made-with: Cursor
@M3gA-Mind M3gA-Mind requested a review from a team April 22, 2026 09:18
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 22, 2026

📝 Walkthrough

Walkthrough

Adds Composio identity persistence and rendering: new identity_set hook, profile_url on provider profiles, facet schema changed to skill:{toolkit}:{identifier}:{field}, APIs to load/render/delete connected identities, and injection of a rendered "Connected Identities" markdown block into agent system prompts.

Changes

Cohort / File(s) Summary
Memory & Docs
\.claude/memory.md
Documented Composio identity persistence, new facet schema, profile_url, identity_set flow, and prompt injection points.
Provider Types
src/openhuman/composio/providers/types.rs
Added pub profile_url: Option<String> to ProviderUserProfile.
Provider Implementations
src/openhuman/composio/providers/gmail/provider.rs, src/openhuman/composio/providers/notion/provider.rs
Extract profile_url from provider responses and include it in constructed ProviderUserProfile.
Provider Trait & Hooks
src/openhuman/composio/providers/traits.rs
Added fn identity_set(&self, profile: &ProviderUserProfile) -> usize to ComposioProvider with default impl; callers use identity_set on connection/profile events.
Facet Persistence & APIs
src/openhuman/composio/providers/profile.rs
Switched persisted facet schema to FacetType::Skill with keys skill:{toolkit}:{identifier}:{field}; normalize toolkit/identifier; persist profile_url; added ConnectedIdentity, load_connected_identities(), render_connected_identities_section(), and delete_connected_identity_facets().
Composio Ops & Events
src/openhuman/composio/ops.rs, src/core/event_bus/events.rs
composio_get_user_profile now calls provider.identity_set(&profile); composio_delete_connection resolves toolkit and deletes related identity facets and emits DomainEvent::ComposioConnectionDeleted.
Agent Prompt Context & Rendering
src/openhuman/agent/prompts/types.rs, src/openhuman/agent/prompts/connected_identities.rs, src/openhuman/agent/prompts/mod.rs
Added connected_identities_md: String to PromptContext; added render_connected_identities() and re-exported render_connected_identities.
Agent Prompt Injection Sites
src/openhuman/agent/agents/.../prompt.rs (welcome, orchestrator, integrations_agent, and many agent tests)
Injected conditional connected-identities markdown block into system prompts; updated numerous tests and prompt-context constructors to include connected_identities_md.
Prompt Context Producers
src/openhuman/agent/harness/..., src/openhuman/context/debug_dump.rs
Populated connected_identities_md via render_connected_identities() where system prompts are built (session turn, subagent runner, debug rendering).
Tests & Minor
src/openhuman/composio/providers/mod.rs, various agent tests
Updated tests to assert default profile_url is None and to initialize new PromptContext field.
Vendored Submodule
app/src-tauri/vendor/tauri-cef
Updated submodule commit reference.
Scripts
scripts/install.sh
DRY_RUN-aware early success path when no compatible asset is found (logs warning and exits 0 if DRY_RUN=true).

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent (prompt builder)
    participant Prompts as Prompts module
    participant Composio as Composio providers/profile
    participant Storage as Facet storage
    Agent->>Prompts: build system prompt (requests connected_identities_md)
    Prompts->>Composio: render_connected_identities()
    Composio->>Storage: load_connected_identities() (query FacetType::Skill)
    Storage-->>Composio: facets (skill:{toolkit}:{identifier}:{field})
    Composio->>Prompts: render_connected_identities_section(markdown)
    Prompts-->>Agent: connected_identities_md (insert into prompt)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • senamakel

Poem

🐰
I stitched your profiles in tidy rows,
URLs and names where moonlight goes,
Facets keyed by toolkit, id, and art,
Prompts call them softly — each a part,
A hop, a render — connected accounts restart. ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(composio): inject connected identities into agent prompts' clearly and specifically describes the primary change—adding connected identities injection into agent prompts for Composio providers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
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.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (1)
src/openhuman/agent/agents/welcome/prompt.rs (1)

30-34: Prefer passing identities through PromptContext.

build(ctx) is otherwise driven by PromptContext; this new global read makes the rendered prompt depend on ambient process state, which makes it harder to unit-test and can surprise future multi-session callers. Threading preloaded identities through the context would keep the builder deterministic.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/agent/agents/welcome/prompt.rs` around lines 30 - 34, The code
calls render_connected_identities() directly inside the welcome prompt builder,
making build(ctx) depend on global state; instead add a preloaded_identities (or
similar) field to PromptContext, populate it where prompts are constructed, and
use that field inside build(ctx) rather than calling
render_connected_identities(); update the builder to read
ctx.preloaded_identities (trim and push as before), remove the global call to
render_connected_identities(), and update all callers/tests of build and
PromptContext to supply the identities so the builder becomes deterministic.
🤖 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-tauri/vendor/tauri-cef`:
- Line 1: The tauri submodule bump removed the
Builder::<tauri::Cef>::command_line_args::<&str, &str>(args) method used in
app/src-tauri/src/lib.rs and will break compilation; either revert the vendor
submodule to a commit that exposes command_line_args or update the lib.rs call
to the new Builder API provided by the bumped tauri crate (inspect the tauri
crate at commit 073047817269c874199a53e5cb3dd3860789cacb to find the replacement
method name and signature and replace the call to
tauri::Builder::<tauri::Cef>::command_line_args::<&str, &str>(args) with the new
API accordingly).

In `@src/openhuman/agent/prompts/mod.rs`:
- Around line 517-522: Move the operational helper render_connected_identities
out of the prompts mod.rs and into a new sibling module (e.g.,
connected_identities.rs); create a pub fn render_connected_identities() in that
new file that calls
crate::openhuman::composio::providers::profile::load_connected_identities() and
crate::openhuman::composio::providers::profile::render_connected_identities_section(&identities),
then re-export the function from mod.rs (pub use
self::connected_identities::render_connected_identities;) so mod.rs remains
export-focused and the runtime logic lives in the sibling module.

In `@src/openhuman/composio/ops.rs`:
- Around line 368-372: When deleting a connection in
composio_delete_connection(), also remove any identity facets for that
connection (keys like "skill:{toolkit}:{identifier}:{field}") before
invalidating caches; after calling client.delete_connection() iterate the
profile store facets for the given toolkit+identifier (similar to how
load_connected_identities() enumerates skill facets) and call the provider
facet-removal API (e.g. provider.identity_delete or provider.remove_facet) for
each matching key, then proceed to invalidate the cache and emit a disconnect
event on the event bus so other modules can react (this prevents
render_connected_identities_section() from seeing stale identities).

In `@src/openhuman/composio/providers/profile.rs`:
- Around line 155-177: The loop that builds the system prompt should sanitize
all external strings before injection: run display_name, email, profile_url,
username (sanitize first, then prepend '@' to the sanitized username), and
identity.identifier through a sanitization helper (e.g., sanitize_prompt_value)
to normalize whitespace, convert newlines/tabs to spaces, replace '|' with '/'
and trim; then use those sanitized values when pushing into fields and when
formatting the final out.push_str call (keep title_case(&identity.source)
as-is). Ensure the sanitize function is defined (name it sanitize_prompt_value)
and called for each place where raw identity fields are used to prevent
newline/markdown/pipe smuggling.
- Around line 115-121: The early-return branches in the function swallow errors
and hide why the prompt section disappears; before each return Vec::new() add a
tracing/debug log that includes context and any error info: when
client_if_ready() returns None log that the memory client isn't ready (include
any state if available), and when profile::profile_facets_by_type(&conn,
&FacetType::Skill) fails log the error returned from that call and the
conn/profile type; reference the functions client_if_ready, profile_conn, and
profile::profile_facets_by_type and include FacetType::Skill in the message so
failures are diagnosable in production.

---

Nitpick comments:
In `@src/openhuman/agent/agents/welcome/prompt.rs`:
- Around line 30-34: The code calls render_connected_identities() directly
inside the welcome prompt builder, making build(ctx) depend on global state;
instead add a preloaded_identities (or similar) field to PromptContext, populate
it where prompts are constructed, and use that field inside build(ctx) rather
than calling render_connected_identities(); update the builder to read
ctx.preloaded_identities (trim and push as before), remove the global call to
render_connected_identities(), and update all callers/tests of build and
PromptContext to supply the identities so the builder becomes deterministic.
🪄 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: fe79647d-39a9-44fc-9110-a3c206352fbc

📥 Commits

Reviewing files that changed from the base of the PR and between 8ab8c59 and b78f2aa.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (13)
  • .claude/memory.md
  • app/src-tauri/vendor/tauri-cef
  • src/openhuman/agent/agents/integrations_agent/prompt.rs
  • src/openhuman/agent/agents/orchestrator/prompt.rs
  • src/openhuman/agent/agents/welcome/prompt.rs
  • src/openhuman/agent/prompts/mod.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/providers/gmail/provider.rs
  • src/openhuman/composio/providers/mod.rs
  • src/openhuman/composio/providers/notion/provider.rs
  • src/openhuman/composio/providers/profile.rs
  • src/openhuman/composio/providers/traits.rs
  • src/openhuman/composio/providers/types.rs

Comment thread app/src-tauri/vendor/tauri-cef
Comment thread src/openhuman/agent/prompts/mod.rs Outdated
Comment thread src/openhuman/composio/ops.rs
Comment thread src/openhuman/composio/providers/profile.rs
Comment thread src/openhuman/composio/providers/profile.rs
Make connected-identity prompt injection deterministic via PromptContext, sanitize provider identity fields before prompt rendering, and clear persisted identity facets when Composio connections are removed to avoid stale context.

Made-with: Cursor
Apply rustfmt to touched Rust files and make install.sh dry-run exit successfully when no compatible release asset exists, so smoke validation remains informative without failing on missing artifacts.

Made-with: Cursor
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/openhuman/context/debug_dump.rs (1)

389-389: Consider reusing connected-identities markdown across toolkit iterations.

Line [389] renders identities in each render_integrations_agent call. In dump_all_agent_prompts, this is repeated per toolkit and can be cached once per dump invocation.

♻️ Refactor sketch
pub async fn dump_all_agent_prompts(...) -> Result<Vec<DumpedPrompt>> {
    let config = load_dump_config(workspace_dir_override, model_override).await?;
+   let connected_identities_md = crate::openhuman::agent::prompts::render_connected_identities();
    ...
-           let dumped = render_integrations_agent(&config, &toolkit)
+           let dumped = render_integrations_agent(&config, &toolkit, &connected_identities_md)
                .await
                ...
}

-async fn render_integrations_agent(config: &Config, toolkit: &str) -> Result<DumpedPrompt> {
+async fn render_integrations_agent(
+    config: &Config,
+    toolkit: &str,
+    connected_identities_md: &str,
+) -> Result<DumpedPrompt> {
    ...
    let ctx = PromptContext {
        ...
-       connected_identities_md: crate::openhuman::agent::prompts::render_connected_identities(),
+       connected_identities_md: connected_identities_md.to_string(),
        ...
    };
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/context/debug_dump.rs` at line 389, Currently
render_connected_identities() is called inside render_integrations_agent for
each toolkit iteration; compute connected_identities_md once in
dump_all_agent_prompts by calling
crate::openhuman::agent::prompts::render_connected_identities() and pass that
precomputed string into render_integrations_agent (or include it in the
struct/args used by dump_all_agent_prompts) so each toolkit reuses the same
connected_identities_md instead of re-rendering it per iteration.
🤖 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/core/event_bus/events.rs`:
- Around line 228-232: The table-driven test all_variants_have_correct_domain()
is missing the new ComposioConnectionDeleted variant, so add a test case that
instantiates DomainEvent::ComposioConnectionDeleted (with appropriate toolkit
and connection_id strings) to the "all variants" list so the
DomainEvent::domain() branch for ComposioConnectionDeleted is exercised; update
the expected domain entry if the test asserts domains explicitly.

In `@src/openhuman/agent/harness/session/turn.rs`:
- Around line 1150-1151: Run rustfmt (cargo fmt) to fix formatting for the
PromptContext assignment around connected_identities_md; reformat the block that
sets connected_identities_md to
crate::openhuman::agent::prompts::render_connected_identities() so it complies
with rustfmt style (you can run cargo fmt or apply the tool's suggested line
breaks/indentation to the PromptContext construction in
src/openhuman/agent/harness/session/turn.rs).

In `@src/openhuman/composio/ops.rs`:
- Around line 125-145: The code currently swallows toolkit resolution failures
by calling resolve_toolkit_for_connection(...).await.ok(), which can skip
calling delete_connected_identity_facets(...) and publish an "unknown" toolkit;
instead ensure identity cleanup runs even if toolkit resolution fails: call
resolve_toolkit_for_connection and branch on its Result/Option so that if
resolution errors you either (a) run a fallback cleanup that does not require a
toolkit identifier or (b) propagate/log the error and still call
delete_connected_identity_facets with a safe default identifier; update the
delete_connection handling around delete_connection(...).await and the
subsequent call to super::providers::profile::delete_connected_identity_facets
and the event published via crate::core::event_bus::publish_global
(ComposioConnectionDeleted) so the cleanup always executes (or the resolution
failure is surfaced) and the published toolkit value is accurate or clearly
marked as fallback.

In `@src/openhuman/composio/providers/profile.rs`:
- Around line 263-280: The code currently collapses all failures to 0; change it
to log errors at debug/trace and only return 0 for genuine "nothing deleted"
cases: when profile::profile_facets_by_type(&conn, &FacetType::Skill) returns
Err, log the error with tracing::debug or log::debug (include error and context)
and return early; inside the loop, when parse_skill_identity_key(&facet.key) is
None, trace the skipped facet with facet.facet_id and facet.key; when running
conn.lock() and calling execute("DELETE FROM user_profile WHERE facet_id = ?1",
params![facet.facet_id]), capture the Result, log failures (with the SQL error
and facet identifiers facet_source/facet_identifier) instead of using
unwrap_or(0) to hide errors, and only increment deleted when execute reports >0;
ensure logs include function-level context (e.g., this function name), facet_id,
facet_source, and facet_identifier for troubleshooting.

---

Nitpick comments:
In `@src/openhuman/context/debug_dump.rs`:
- Line 389: Currently render_connected_identities() is called inside
render_integrations_agent for each toolkit iteration; compute
connected_identities_md once in dump_all_agent_prompts by calling
crate::openhuman::agent::prompts::render_connected_identities() and pass that
precomputed string into render_integrations_agent (or include it in the
struct/args used by dump_all_agent_prompts) so each toolkit reuses the same
connected_identities_md instead of re-rendering it per iteration.
🪄 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: e4bfda55-df87-414b-a1cd-2010be2a5601

📥 Commits

Reviewing files that changed from the base of the PR and between b78f2aa and 24f0565.

📒 Files selected for processing (26)
  • src/core/event_bus/events.rs
  • src/openhuman/agent/agents/archivist/prompt.rs
  • src/openhuman/agent/agents/code_executor/prompt.rs
  • src/openhuman/agent/agents/critic/prompt.rs
  • src/openhuman/agent/agents/integrations_agent/prompt.rs
  • src/openhuman/agent/agents/loader.rs
  • src/openhuman/agent/agents/morning_briefing/prompt.rs
  • src/openhuman/agent/agents/orchestrator/prompt.rs
  • src/openhuman/agent/agents/planner/prompt.rs
  • src/openhuman/agent/agents/researcher/prompt.rs
  • src/openhuman/agent/agents/summarizer/prompt.rs
  • src/openhuman/agent/agents/tool_maker/prompt.rs
  • src/openhuman/agent/agents/tools_agent/prompt.rs
  • src/openhuman/agent/agents/trigger_reactor/prompt.rs
  • src/openhuman/agent/agents/trigger_triage/prompt.rs
  • src/openhuman/agent/agents/welcome/prompt.rs
  • src/openhuman/agent/harness/session/turn.rs
  • src/openhuman/agent/harness/subagent_runner/ops.rs
  • src/openhuman/agent/prompts/connected_identities.rs
  • src/openhuman/agent/prompts/mod.rs
  • src/openhuman/agent/prompts/types.rs
  • src/openhuman/agent/triage/evaluator.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/providers/profile.rs
  • src/openhuman/context/debug_dump.rs
  • src/openhuman/learning/prompt_sections.rs
✅ Files skipped from review due to trivial changes (1)
  • src/openhuman/agent/triage/evaluator.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/agent/agents/orchestrator/prompt.rs
  • src/openhuman/agent/agents/welcome/prompt.rs
  • src/openhuman/agent/prompts/mod.rs

Comment on lines +228 to +232
/// A Composio connection was removed.
ComposioConnectionDeleted {
toolkit: String,
connection_id: String,
},
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

Add this variant to all_variants_have_correct_domain().

DomainEvent::domain() now handles ComposioConnectionDeleted, but the table-driven test below never instantiates it. The test named “all variants” no longer covers the new branch.

Suggested test addition
@@
             (
+                DomainEvent::ComposioConnectionDeleted {
+                    toolkit: "gmail".into(),
+                    connection_id: "conn-1".into(),
+                },
+                "composio",
+            ),
+            (
                 DomainEvent::ComposioActionExecuted {
                     tool: "GMAIL_SEND_EMAIL".into(),
                     success: true,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/core/event_bus/events.rs` around lines 228 - 232, The table-driven test
all_variants_have_correct_domain() is missing the new ComposioConnectionDeleted
variant, so add a test case that instantiates
DomainEvent::ComposioConnectionDeleted (with appropriate toolkit and
connection_id strings) to the "all variants" list so the DomainEvent::domain()
branch for ComposioConnectionDeleted is exercised; update the expected domain
entry if the test asserts domains explicitly.

Comment thread src/openhuman/agent/harness/session/turn.rs Outdated
Comment thread src/openhuman/composio/ops.rs Outdated
Comment on lines +263 to +280
let Ok(facets) = profile::profile_facets_by_type(&conn, &FacetType::Skill) else {
return 0;
};
let mut deleted = 0usize;
for facet in facets {
let Some((facet_source, facet_identifier, _field)) = parse_skill_identity_key(&facet.key)
else {
continue;
};
if facet_source == source && facet_identifier == identifier {
let conn_guard = conn.lock();
if conn_guard
.execute(
"DELETE FROM user_profile WHERE facet_id = ?1",
params![facet.facet_id],
)
.unwrap_or(0)
> 0
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

Log cleanup failures instead of collapsing them to 0.

This path returns 0 both when there is nothing to delete and when profile_facets_by_type(...) or the SQL delete fails. That makes stale disconnected identities hard to diagnose once prompts start rendering old facets again.

Suggested logging hardening
-    let Ok(facets) = profile::profile_facets_by_type(&conn, &FacetType::Skill) else {
-        return 0;
-    };
+    let facets = match profile::profile_facets_by_type(&conn, &FacetType::Skill) {
+        Ok(facets) => facets,
+        Err(error) => {
+            tracing::warn!(
+                source = %source,
+                identifier = %identifier,
+                error = %error,
+                "[composio:profile] delete_connected_identity_facets: profile_facets_by_type failed"
+            );
+            return 0;
+        }
+    };
@@
-            if conn_guard
-                .execute(
-                    "DELETE FROM user_profile WHERE facet_id = ?1",
-                    params![facet.facet_id],
-                )
-                .unwrap_or(0)
-                > 0
-            {
-                deleted += 1;
-            }
+            match conn_guard.execute(
+                "DELETE FROM user_profile WHERE facet_id = ?1",
+                params![facet.facet_id],
+            ) {
+                Ok(rows) if rows > 0 => deleted += 1,
+                Ok(_) => {}
+                Err(error) => {
+                    tracing::warn!(
+                        source = %source,
+                        identifier = %identifier,
+                        facet_id = %facet.facet_id,
+                        error = %error,
+                        "[composio:profile] delete_connected_identity_facets: delete failed"
+                    );
+                }
+            }
As per coding guidelines, `src/**/*.rs`: Rust code must use `log` / `tracing` at `debug` or `trace` level; add substantial development-oriented logs at entry/exit points, branch decisions, external calls, retries, state transitions, and error handling paths.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/providers/profile.rs` around lines 263 - 280, The code
currently collapses all failures to 0; change it to log errors at debug/trace
and only return 0 for genuine "nothing deleted" cases: when
profile::profile_facets_by_type(&conn, &FacetType::Skill) returns Err, log the
error with tracing::debug or log::debug (include error and context) and return
early; inside the loop, when parse_skill_identity_key(&facet.key) is None, trace
the skipped facet with facet.facet_id and facet.key; when running conn.lock()
and calling execute("DELETE FROM user_profile WHERE facet_id = ?1",
params![facet.facet_id]), capture the Result, log failures (with the SQL error
and facet identifiers facet_source/facet_identifier) instead of using
unwrap_or(0) to hide errors, and only increment deleted when execute reports >0;
ensure logs include function-level context (e.g., this function name), facet_id,
facet_source, and facet_identifier for troubleshooting.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@scripts/install.sh`:
- Around line 296-299: Update the dry-run branch that checks DRY_RUN to avoid
implying a specific cause; change the log_warn and echo messages used in that
block (the DRY_RUN conditional currently calling log_warn "No compatible release
asset found for ${OS}/${ARCH}..." and echo "DRY RUN: no compatible artifact
available yet for ${OS}/${ARCH}") to a more generic statement indicating a
resolver failure or that resolution was inconclusive (e.g., "DRY RUN: could not
determine a compatible artifact for ${OS}/${ARCH} — resolution may have failed
or none exists"); keep the early exit behavior intact.

In `@src/openhuman/composio/providers/profile.rs`:
- Around line 251-285: The deletion fails when identifiers differ in
normalization; in delete_connected_identity_facets normalize the incoming source
and identifier using the same normalize_token used by persist_provider_profile
(i.e., call normalize_token on both parameters once before iterating facets) and
then compare those normalized values against the parsed facet_source and
facet_identifier from parse_skill_identity_key; this ensures the equality check
inside the loop (and therefore the DELETE executed via conn.execute) uses the
same canonical form as when facets were stored.
- Around line 6-7: The doc comment incorrectly refers to FacetType::Context
while the implementation uses FacetType::Skill; update the comment in profile.rs
so it describes that each non-`None` field becomes a `FacetType::Skill` facet
(e.g., keyed `skill:{toolkit}:{identifier}:{field}` with confidence 0.95) to
match the behavior implemented in the code paths that construct
`FacetType::Skill`.
🪄 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: 80b19e54-af9e-41cf-804d-b3c613c89aae

📥 Commits

Reviewing files that changed from the base of the PR and between 24f0565 and 99e48db.

📒 Files selected for processing (4)
  • scripts/install.sh
  • src/openhuman/agent/harness/session/turn.rs
  • src/openhuman/composio/ops.rs
  • src/openhuman/composio/providers/profile.rs
✅ Files skipped from review due to trivial changes (1)
  • src/openhuman/agent/harness/session/turn.rs

Comment thread scripts/install.sh
Comment on lines +296 to +299
if [ "${DRY_RUN}" = true ]; then
log_warn "No compatible release asset found for ${OS}/${ARCH} (dry-run mode, skipping download)."
echo "DRY RUN: no compatible artifact available yet for ${OS}/${ARCH}"
exit 0
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

Dry-run warning message is too specific for a generic resolver failure.

At Line 297, the log says “No compatible release asset found,” but this branch also runs for API/network/parsing failures. In dry-run mode, that message can be misleading.

Suggested wording-only adjustment
-      log_warn "No compatible release asset found for ${OS}/${ARCH} (dry-run mode, skipping download)."
-      echo "DRY RUN: no compatible artifact available yet for ${OS}/${ARCH}"
+      log_warn "Could not resolve a release asset for ${OS}/${ARCH}; continuing because --dry-run is enabled."
+      echo "DRY RUN: skipping download/install because release metadata could not be resolved for ${OS}/${ARCH}"
📝 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
if [ "${DRY_RUN}" = true ]; then
log_warn "No compatible release asset found for ${OS}/${ARCH} (dry-run mode, skipping download)."
echo "DRY RUN: no compatible artifact available yet for ${OS}/${ARCH}"
exit 0
if [ "${DRY_RUN}" = true ]; then
log_warn "Could not resolve a release asset for ${OS}/${ARCH}; continuing because --dry-run is enabled."
echo "DRY RUN: skipping download/install because release metadata could not be resolved for ${OS}/${ARCH}"
exit 0
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@scripts/install.sh` around lines 296 - 299, Update the dry-run branch that
checks DRY_RUN to avoid implying a specific cause; change the log_warn and echo
messages used in that block (the DRY_RUN conditional currently calling log_warn
"No compatible release asset found for ${OS}/${ARCH}..." and echo "DRY RUN: no
compatible artifact available yet for ${OS}/${ARCH}") to a more generic
statement indicating a resolver failure or that resolution was inconclusive
(e.g., "DRY RUN: could not determine a compatible artifact for ${OS}/${ARCH} —
resolution may have failed or none exists"); keep the early exit behavior
intact.

Comment on lines 6 to +7
//! Each non-`None` field becomes a [`FacetType::Context`] facet keyed
//! as `composio:{toolkit}:{field}`. Confidence is set to 0.95 because
//! as `skill:{toolkit}:{identifier}:{field}`. Confidence is set to 0.95 because
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

Doc comment references wrong facet type.

Line 6 still refers to FacetType::Context, but the implementation now uses FacetType::Skill.

📝 Proposed fix
-//! Each non-`None` field becomes a [`FacetType::Context`] facet keyed
+//! Each non-`None` field becomes a [`FacetType::Skill`] facet keyed
 //! as `skill:{toolkit}:{identifier}:{field}`. Confidence is set to 0.95 because
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/providers/profile.rs` around lines 6 - 7, The doc
comment incorrectly refers to FacetType::Context while the implementation uses
FacetType::Skill; update the comment in profile.rs so it describes that each
non-`None` field becomes a `FacetType::Skill` facet (e.g., keyed
`skill:{toolkit}:{identifier}:{field}` with confidence 0.95) to match the
behavior implemented in the code paths that construct `FacetType::Skill`.

Comment on lines +251 to +285
pub fn delete_connected_identity_facets(source: &str, identifier: &str) -> usize {
let Some(client) = crate::openhuman::memory::global::client_if_ready() else {
tracing::debug!(
source = %source,
identifier = %identifier,
"[composio:profile] delete_connected_identity_facets: memory client not ready"
);
return 0;
};
let conn = client.profile_conn();
let Ok(facets) = profile::profile_facets_by_type(&conn, &FacetType::Skill) else {
return 0;
};
let mut deleted = 0usize;
for facet in facets {
let Some((facet_source, facet_identifier, _field)) = parse_skill_identity_key(&facet.key)
else {
continue;
};
if facet_source == source && facet_identifier == identifier {
let conn_guard = conn.lock();
if conn_guard
.execute(
"DELETE FROM user_profile WHERE facet_id = ?1",
params![facet.facet_id],
)
.unwrap_or(0)
> 0
{
deleted += 1;
}
}
}
deleted
}
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

Identifier comparison mismatch will cause deletion to fail.

persist_provider_profile normalizes both toolkit and identifier via normalize_token() before storing (lines 44-50), but delete_connected_identity_facets compares the raw parameters directly against the parsed facet key values (line 270). When ops.rs calls this function with a raw connection_id containing uppercase or special characters, the comparison will fail and no facets will be deleted.

🐛 Proposed fix: normalize parameters before comparison
 pub fn delete_connected_identity_facets(source: &str, identifier: &str) -> usize {
+    let source = normalize_token(source);
+    let identifier_normalized = {
+        let norm = normalize_token(identifier);
+        if norm.is_empty() { "default".to_string() } else { norm }
+    };
     let Some(client) = crate::openhuman::memory::global::client_if_ready() else {
         tracing::debug!(
-            source = %source,
-            identifier = %identifier,
+            source = %source,
+            identifier = %identifier_normalized,
             "[composio:profile] delete_connected_identity_facets: memory client not ready"
         );
         return 0;
     };
     let conn = client.profile_conn();
     let Ok(facets) = profile::profile_facets_by_type(&conn, &FacetType::Skill) else {
         return 0;
     };
     let mut deleted = 0usize;
     for facet in facets {
         let Some((facet_source, facet_identifier, _field)) = parse_skill_identity_key(&facet.key)
         else {
             continue;
         };
-        if facet_source == source && facet_identifier == identifier {
+        if facet_source == source && facet_identifier == identifier_normalized {
             let conn_guard = conn.lock();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/openhuman/composio/providers/profile.rs` around lines 251 - 285, The
deletion fails when identifiers differ in normalization; in
delete_connected_identity_facets normalize the incoming source and identifier
using the same normalize_token used by persist_provider_profile (i.e., call
normalize_token on both parameters once before iterating facets) and then
compare those normalized values against the parsed facet_source and
facet_identifier from parse_skill_identity_key; this ensures the equality check
inside the loop (and therefore the DELETE executed via conn.execute) uses the
same canonical form as when facets were stored.

@senamakel senamakel merged commit 1461d78 into tinyhumansai:main Apr 22, 2026
8 checks passed
CodeGhost21 added a commit to CodeGhost21/openhuman that referenced this pull request Apr 22, 2026
Pull latest main into the Windows tool-sync branch to pick up tinyhumansai#774
(connected-identity injection) and other feature work alongside the
per-thread Agent invalidation fix.

# Conflicts:
#	src/core/event_bus/events.rs
#	src/openhuman/composio/ops.rs
AusAgentSmith pushed a commit to AusAgentSmith/openhuman that referenced this pull request May 23, 2026
…umansai#774)

* chore(vendor): bump tauri-cef to feat/cef tip (073047817)

* feat(composio): inject connected identities into agent prompts

Standardize provider identity persistence with a shared identity_set hook and skill-scoped facet keys so connected account identity survives restarts and is merged into inference context. Add connected identity rendering in welcome, orchestrator, and integrations_agent prompts so agents can reference cross-platform user identity during conversations.

Closes tinyhumansai#691

Made-with: Cursor

* fix(composio): resolve CodeRabbit identity prompt and cleanup findings

Make connected-identity prompt injection deterministic via PromptContext, sanitize provider identity fields before prompt rendering, and clear persisted identity facets when Composio connections are removed to avoid stale context.

Made-with: Cursor

* fix(ci): satisfy format checks and dry-run installer smoke

Apply rustfmt to touched Rust files and make install.sh dry-run exit successfully when no compatible release asset exists, so smoke validation remains informative without failing on missing artifacts.

Made-with: Cursor
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.

Build user identity from the skills and other places

2 participants