feat(composio): inject connected identities into agent prompts#774
Conversation
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
📝 WalkthroughWalkthroughAdds Composio identity persistence and rendering: new Changes
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)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (1)
src/openhuman/agent/agents/welcome/prompt.rs (1)
30-34: Prefer passing identities throughPromptContext.
build(ctx)is otherwise driven byPromptContext; 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
⛔ Files ignored due to path filters (1)
Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (13)
.claude/memory.mdapp/src-tauri/vendor/tauri-cefsrc/openhuman/agent/agents/integrations_agent/prompt.rssrc/openhuman/agent/agents/orchestrator/prompt.rssrc/openhuman/agent/agents/welcome/prompt.rssrc/openhuman/agent/prompts/mod.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/providers/gmail/provider.rssrc/openhuman/composio/providers/mod.rssrc/openhuman/composio/providers/notion/provider.rssrc/openhuman/composio/providers/profile.rssrc/openhuman/composio/providers/traits.rssrc/openhuman/composio/providers/types.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
There was a problem hiding this comment.
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_agentcall. Indump_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
📒 Files selected for processing (26)
src/core/event_bus/events.rssrc/openhuman/agent/agents/archivist/prompt.rssrc/openhuman/agent/agents/code_executor/prompt.rssrc/openhuman/agent/agents/critic/prompt.rssrc/openhuman/agent/agents/integrations_agent/prompt.rssrc/openhuman/agent/agents/loader.rssrc/openhuman/agent/agents/morning_briefing/prompt.rssrc/openhuman/agent/agents/orchestrator/prompt.rssrc/openhuman/agent/agents/planner/prompt.rssrc/openhuman/agent/agents/researcher/prompt.rssrc/openhuman/agent/agents/summarizer/prompt.rssrc/openhuman/agent/agents/tool_maker/prompt.rssrc/openhuman/agent/agents/tools_agent/prompt.rssrc/openhuman/agent/agents/trigger_reactor/prompt.rssrc/openhuman/agent/agents/trigger_triage/prompt.rssrc/openhuman/agent/agents/welcome/prompt.rssrc/openhuman/agent/harness/session/turn.rssrc/openhuman/agent/harness/subagent_runner/ops.rssrc/openhuman/agent/prompts/connected_identities.rssrc/openhuman/agent/prompts/mod.rssrc/openhuman/agent/prompts/types.rssrc/openhuman/agent/triage/evaluator.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/providers/profile.rssrc/openhuman/context/debug_dump.rssrc/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
| /// A Composio connection was removed. | ||
| ComposioConnectionDeleted { | ||
| toolkit: String, | ||
| connection_id: String, | ||
| }, |
There was a problem hiding this comment.
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.
| 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 |
There was a problem hiding this comment.
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"
+ );
+ }
+ }🤖 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.
There was a problem hiding this comment.
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
📒 Files selected for processing (4)
scripts/install.shsrc/openhuman/agent/harness/session/turn.rssrc/openhuman/composio/ops.rssrc/openhuman/composio/providers/profile.rs
✅ Files skipped from review due to trivial changes (1)
- src/openhuman/agent/harness/session/turn.rs
| 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 |
There was a problem hiding this comment.
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.
| 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.
| //! 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 |
There was a problem hiding this comment.
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`.
| 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 | ||
| } |
There was a problem hiding this comment.
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.
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
…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
Summary
identity_setcallback in the provider lifecycle andProviderUserProfile.profile_urlsupportFacetType::Skillprofile facets using deterministic keys (skill:{toolkit}:{identifier}:{field}), and add loaders/renderers for merged identity contextConnected Identitiessection into welcome, orchestrator, and integrations agent system prompts so inference can reference cross-platform identity0eb58a37Linked issue
Closes #691
Test plan
cargo test --manifest-path Cargo.toml composio::providers::profile -- --nocapturecargo test --manifest-path Cargo.toml prompt::tests -- --nocapturecargo fmt --manifest-path Cargo.tomlcargo check --manifest-path Cargo.tomlyarn typecheckyarn lintyarn format:checkyarn buildyarn tauri dev(smoke start)Made with Cursor
Summary by CodeRabbit
New Features
Refactor
Tests
Chores