Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/src/components/intelligence/MemorySources.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* "Connected sources" panel with one section, one Sync button, one
* stats block per identity. Sync only appears when:
* 1. the connection is currently ACTIVE/CONNECTED, AND
* 2. the toolkit is in the syncable allow-list (today: gmail).
* 2. the toolkit is in the syncable allow-list passed by the parent.
*/
import { useCallback, useEffect, useMemo, useState } from 'react';

Expand Down
14 changes: 10 additions & 4 deletions app/src/components/intelligence/MemoryWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,17 @@ interface MemoryWorkspaceProps {
* adding chunks to the memory tree.
*
* Source of truth: providers under
* `src/openhuman/composio/providers/<toolkit>/` that call
* `ingest_page_into_memory_tree`. Today that's gmail. Add a slug here
* when a new provider lands a memory-tree ingest path.
* `src/openhuman/memory_sync/composio/providers/<toolkit>/` that
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.

Nitpick (doc accuracy). This says the source-of-truth providers "persist items via store_skill_sync into the memory tree." That's true for clickup/github/linear/notion (they go through persist_single_itemstore_skill_sync in memory_sync/composio/providers/sync_state.rs:306), but gmail and slack ingest via ingest_page_into_memory_tree (gmail/ingest.rs, slack/ingest.rs), not store_skill_sync. Consider generalizing, e.g. "…that ingest items into the memory tree (via store_skill_sync or a provider-specific ingest path)."

* persist items via `store_skill_sync` into the memory tree.
*/
const SYNCABLE_TOOLKITS: ReadonlySet<string> = new Set(['gmail']);
const SYNCABLE_TOOLKITS: ReadonlySet<string> = new Set([
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 (non-blocking) — static allowlist will drift from the Rust source of truth.

This SYNCABLE_TOOLKITS set is a hand-maintained mirror of the backend has_native_provider list in src/openhuman/memory_sync/composio/providers/mod.rs:110. The two have already drifted before — clickup (#2288), linear (#2400), and github became native providers in Rust while the UI stayed Gmail-only, which is the very bug this PR fixes. Nothing stops it recurring, and no test ties this TS set to the Rust capability matrix.

The core already exposes the authoritative data: composio.list_capabilities (openhuman.composio_list_capabilities) returns ComposioCapability[] with a per-toolkit memory_ingest flag derived from has_native_provider. Deriving the set from capabilities.filter(c => c.memory_ingest) would keep the Rust core as the single source of truth (consistent with the repo's "core is authoritative; frontend presents only" rule).

Not a blocker — the static list is correct today and the PR description flags this as a known tradeoff. If kept static, at least add a one-line comment pointing at has_native_provider as the thing to keep in sync.

'clickup',
'github',
'gmail',
'linear',
'notion',
'slack',
]);

export function MemoryWorkspace({ onToast }: MemoryWorkspaceProps) {
const { t } = useT();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,24 +235,23 @@ describe('MemoryWorkspace (graph view)', () => {
});
});

it('hides toolkits without a memory-tree ingest provider entirely', async () => {
it('shows sync rows for provider-backed toolkits and hides non-syncable ones', async () => {
listConnections.mockResolvedValue({
connections: [
{ id: 'conn-gmail', toolkit: 'gmail', status: 'ACTIVE', accountEmail: 'a@x' },
{ id: 'conn-slack', toolkit: 'slack', status: 'ACTIVE', workspace: 'acme' },
{ id: 'conn-notion', toolkit: 'notion', status: 'ACTIVE' },
{ id: 'conn-discord', toolkit: 'discord', status: 'ACTIVE' },
],
});
renderWithProviders(<MemoryWorkspace />);
// Gmail row exists with a working Sync button.
// Provider-backed toolkits should render actionable Sync rows
expect(await screen.findByTestId('memory-source-sync-gmail')).toBeInTheDocument();
// Non-syncable toolkits are filtered out completely — neither
// the row nor the Sync button render. Cleaner than a "no sync
// yet" placeholder for an action the user can't take.
expect(screen.queryByTestId('memory-source-row-slack')).toBeNull();
expect(screen.queryByTestId('memory-source-row-notion')).toBeNull();
expect(screen.queryByTestId('memory-source-sync-slack')).toBeNull();
expect(screen.queryByTestId('memory-source-sync-notion')).toBeNull();
expect(screen.getByTestId('memory-source-sync-slack')).toBeInTheDocument();
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.

Nitpick (test completeness). This test exercises 3 of the 6 newly-syncable toolkits (gmail/slack/notion) plus the hidden case (discord), but not clickup/github/linear. The filter is toolkit-agnostic (syncableToolkits.has(conn.toolkit)), so risk is low — but asserting all six would lock the PR's stated intent ("show all native sources") against future allowlist edits.

for (const tk of ['clickup', 'github', 'gmail', 'linear', 'notion', 'slack']) {
  expect(screen.getByTestId(`memory-source-sync-${tk}`)).toBeInTheDocument();
}

(with the corresponding connections added to the listConnections mock).

expect(screen.getByTestId('memory-source-sync-notion')).toBeInTheDocument();
// Non-syncable toolkits stay hidden.
expect(screen.queryByTestId('memory-source-row-discord')).toBeNull();
expect(screen.queryByTestId('memory-source-sync-discord')).toBeNull();
});

it('toggling to Contacts mode re-fetches the graph with mode=contacts', async () => {
Expand Down
4 changes: 2 additions & 2 deletions src/openhuman/memory_sync/composio/providers/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,8 @@ pub fn capability_matrix() -> Vec<ComposioCapability> {
///
/// This is consulted by the meta-tool layer alongside any registered
/// provider's [`ComposioProvider::curated_tools`]. It lets toolkits
/// without a full native provider (e.g. `github`, which has no sync
/// logic yet) still benefit from curated whitelisting.
/// without a full native provider still benefit from curated
/// whitelisting.
///
/// Lookup key is the lowercased prefix returned by
/// [`toolkit_from_slug`] applied to the action slug — e.g.
Expand Down
Loading