diff --git a/src/openhuman/memory_sync/composio/providers/descriptions.rs b/src/openhuman/memory_sync/composio/providers/descriptions.rs index 18453f65f..0a0cf52a8 100644 --- a/src/openhuman/memory_sync/composio/providers/descriptions.rs +++ b/src/openhuman/memory_sync/composio/providers/descriptions.rs @@ -11,7 +11,10 @@ pub fn toolkit_description(slug: &str) -> &'static str { "Send, read, draft, reply, forward, and search emails; manage labels and threads" } "notion" => "Create, read, update, and search notion pages and notion databases", - "github" => "Manage repositories, issues, pull requests on GitHub", + "github" => { + "Manage repositories, issues, and pull requests on GitHub; sync \ + assigned issues into Memory Tree" + } "slack" => "Send messages, read channels, manage threads, and post updates in Slack", "discord" => "Send messages, manage channels, and interact with Discord servers", "google_calendar" => "Create, update, and query calendar events; check availability", diff --git a/src/openhuman/memory_sync/composio/providers/github/provider.rs b/src/openhuman/memory_sync/composio/providers/github/provider.rs index 4c8f81683..e69328f1e 100644 --- a/src/openhuman/memory_sync/composio/providers/github/provider.rs +++ b/src/openhuman/memory_sync/composio/providers/github/provider.rs @@ -199,10 +199,6 @@ impl ComposioProvider for GitHubProvider { // Build the base search query. let query = match &state.cursor { Some(cursor) => { - // GitHub's `updated:>` qualifier accepts ISO 8601 dates - // (YYYY-MM-DD or full datetime). Using the full stored cursor - // (e.g. `"2024-05-21T15:30:00Z"`) is accepted by the API and - // more precise than truncating to the day. format!("involves:{login} updated:>{cursor}") } None => format!("involves:{login}"), @@ -236,12 +232,15 @@ impl ComposioProvider for GitHubProvider { "[composio:github] executing {ACTION_SEARCH_ISSUES}" ); - let resp = ctx - .execute(ACTION_SEARCH_ISSUES, Some(args)) - .await - .map_err(|e| { - format!("[composio:github] {ACTION_SEARCH_ISSUES} page={page_num}: {e:#}") - })?; + let resp = match ctx.execute(ACTION_SEARCH_ISSUES, Some(args)).await { + Ok(resp) => resp, + Err(e) => { + let _ = state.save(&memory).await; + return Err(format!( + "[composio:github] {ACTION_SEARCH_ISSUES} page={page_num}: {e:#}" + )); + } + }; state.record_requests(1); if !resp.successful { diff --git a/src/openhuman/memory_sync/composio/providers/mod.rs b/src/openhuman/memory_sync/composio/providers/mod.rs index 60cd08549..e2cb340e2 100644 --- a/src/openhuman/memory_sync/composio/providers/mod.rs +++ b/src/openhuman/memory_sync/composio/providers/mod.rs @@ -100,6 +100,7 @@ fn native_provider_sync_interval(toolkit: &str) -> Option { "notion" => Some(notion::NotionProvider::new().sync_interval_secs()), "slack" => Some(slack::SlackProvider::new().sync_interval_secs()), "clickup" => Some(clickup::ClickUpProvider::new().sync_interval_secs()), + "github" => Some(github::GitHubProvider::new().sync_interval_secs()), "linear" => Some(linear::LinearProvider::new().sync_interval_secs()), _ => None, } @@ -107,7 +108,10 @@ fn native_provider_sync_interval(toolkit: &str) -> Option { } fn has_native_provider(toolkit: &str) -> bool { - matches!(toolkit, "gmail" | "notion" | "slack" | "clickup" | "linear") + matches!( + toolkit, + "gmail" | "notion" | "slack" | "clickup" | "github" | "linear" + ) } /// Static overview of the Composio integrations supported by this core build. @@ -499,6 +503,26 @@ mod tests { assert!(linear.memory_ingest); } + #[test] + fn capability_matrix_includes_github_as_native_memory_provider() { + let matrix = capability_matrix(); + let github = matrix + .iter() + .find(|entry| entry.toolkit == "github") + .expect("github capability row"); + assert!(github.native_provider, "github must be native"); + assert!(github.curated_tools, "github must have a curated catalog"); + assert!( + github.curated_tool_count > 0, + "github catalog must be non-empty" + ); + assert!(github.user_profile); + assert!(github.initial_sync); + assert!(github.periodic_sync); + assert_eq!(github.sync_interval_secs, Some(30 * 60)); + assert!(github.memory_ingest); + } + #[test] fn toolkit_description_known_slugs_are_distinct_and_non_empty() { let known = [