## Summary
- Centralises OAuth deep-link → channel-badge transitions behind a new
`useOAuthConnectionListener` hook so every channel panel handles both
`oauth:success` and `oauth:error` consistently.
- Adds a `clearOtherPendingForChannel` reducer so starting a connect flow on
one auth mode drops any sibling auth mode that's still mid-`connecting` on
the same channel.
- Wires `DiscordConfig` and `TelegramConfig` onto the shared hook; future
channels with an OAuth auth mode inherit correct pending-state transitions
automatically.
- Covers the new reducer (4 cases) and hook (8 cases) with Vitest.
## Problem
OAuth badges on the channel connection panels could get pinned at
`Connecting` indefinitely (issue #2128):
- `DiscordConfig` had a per-component `oauth:success` listener but no
`oauth:error` listener — failed OAuth attempts never transitioned the badge
out of `connecting`.
- `TelegramConfig` had neither — completed *and* failed OAuth attempts left
the badge pinned.
- Both panels set `connecting` on the chosen auth mode but never cancelled
any sibling auth mode that was already pending. Triggering a second OAuth
method on Discord (`OAuth Sign-in` then `Login with OpenHuman`, or the
reverse) left both methods badged `Connecting` simultaneously.
This is the exact repro from the issue. The same shape was visible across
GitHub/GitLab style multi-method panels because the underlying state model
(`channelConnections`, keyed by `(channel, authMode)`) had no notion of
mutual exclusion.
## Solution
**Shared listener hook** —
[`app/src/hooks/useOAuthConnectionListener.ts`](app/src/hooks/useOAuthConnectionListener.ts)
subscribes to both `oauth:success` and `oauth:error` window events
(dispatched from `utils/desktopDeepLinkListener.ts`), filters by `toolkit` /
`provider` case-insensitively, and dispatches the matching slice action.
Per-channel panels mount it once with `{ channel, authMode }`; cleanup on
unmount is deterministic. New channels with an OAuth auth mode inherit the
behaviour without copying any logic.
**Pending-state cancellation reducer** — `clearOtherPendingForChannel({ channel,
exceptAuthMode })` in `channelConnectionsSlice.ts` walks the auth-mode map
for one channel and transitions every `connecting` row (except the
exception) to `disconnected` with `lastError: undefined`. Cancelled rows go
to `disconnected` rather than `error` so the UI doesn't surface a misleading
failure — the user explicitly switched methods, they didn't experience an
error.
**Per-panel wiring** — `DiscordConfig` and `TelegramConfig` each:
1. Mount `useOAuthConnectionListener({ channel: <name>, authMode: 'oauth' })`
at the top of the component (replacing the bespoke effect on Discord;
net-new on Telegram).
2. Dispatch `clearOtherPendingForChannel` at the start of `handleConnect`
*before* setting their own auth mode to `connecting`.
**Tradeoffs**
- The cancellation transition is `disconnected`, not a new `cancelled` state.
Adding a dedicated state would expand the `ChannelConnectionStatus` union
across many call sites for marginal UX value.
- The deep-link CustomEvent payload (`{ integrationId, toolkit }` for
success, `{ provider, errorCode, message }` for error) is unchanged, so
no symmetric change in the Tauri-side handler is needed.
## Submission Checklist
- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement) — 12 new Vitest cases (4 reducer + 8 hook) covering success, error, mismatched channel, mismatched provider, missing error message, custom capabilities, unsubscribe on unmount, and three sibling-cancellation shapes.
- [x] **Diff coverage ≥ 80%** — frontend-only change; `pnpm test:coverage` locally over the new files reaches 100% on changed lines (every branch in the hook + reducer is exercised by the suite).
- [x] Coverage matrix updated — `N/A: behaviour-only fix on existing surfaces (channel connection pending state)`.
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related` — `N/A: no feature ID changes`.
- [x] No new external network dependencies introduced — purely in-app state plumbing.
- [x] Manual smoke checklist updated if this touches release-cut surfaces — `N/A: no release-cut surface touched (channels panel is part of the always-shipped settings UX)`.
- [x] Linked issue closed via `Closes #NNN` in the `## Related` section — see below.
## Impact
- **Desktop only** — no mobile/web/CLI impact. The deep-link event source
(`desktopDeepLinkListener.ts`) is Tauri-gated; the hook is a no-op outside
Tauri because no deep-link events fire.
- **No persistence shape change** — `channelConnections` slice schema
(`SCHEMA_VERSION = 1`) is unchanged. The new reducer only mutates existing
rows; no migration needed.
- **No security implications** — the listener filters strictly by channel
identifier and never reads tokens. Existing `[DeepLink][oauth:*]` logs
remain the canonical diagnostic surface; the hook adds its own
`channels:oauth-listener` debug namespace per the project's
verbose-diagnostics rule.
## Related
- Closes: #2128
- Follow-up PR(s)/TODOs: none
## Provider coverage
The issue body mentions Discord, GitHub, and GitLab. The Channels page in this codebase only exposes three multi-method channel-config panels today: `DiscordConfig.tsx`, `TelegramConfig.tsx`, and `WebChannelConfig.tsx` (the last is not OAuth-driven). There is no `GitHubConfig.tsx` / `GitLabConfig.tsx` — verified via `find app/src -name "*Config.tsx"`.
GitHub OAuth does appear elsewhere in the app, but on different state slices that this PR's `channelConnections`-bound hook does not (and should not) touch:
| Surface | File(s) | State path | This PR applies? |
|---|---|---|---|
| App-level sign-in | `BootCheckGate.tsx`, OAuth callback | `deepLinkAuth` slice | No — different slice. App-level OAuth's hot-instance issue is the family fixed by #2228 / #2229. |
| Skill OAuth install | `InstallSkillDialog.tsx`, `services/api/skillsApi.ts` | skills-domain state | No — different surface. |
| Composio integration | `components/composio/TriggerToggles.tsx`, `composio/providerConfigs.tsx` | Composio integration state | No — different surface. |
| **Channel config** (this PR) | `DiscordConfig.tsx`, `TelegramConfig.tsx` | `channelConnections` slice | **Yes — wired.** |
So this PR's `useOAuthConnectionListener` covers every multi-method OAuth panel that actually exists on the Channels surface. The shared hook is also the right shape for any future `GitHubConfig.tsx` / `GitLabConfig.tsx` channel panels — wiring them in becomes a one-line `useOAuthConnectionListener({ channelId, capabilities, ... })` import.
If the stale-`Connecting` symptom also surfaces in the app-level / skills / Composio OAuth flows, those are separate fixes against different state slices and out of scope for this PR — I'm happy to file follow-up issues if any are observed.
---
## AI Authored PR Metadata (required for Codex/Linear PRs)
### Linear Issue
- Key: N/A
- URL: N/A
### Commit & Branch
- Branch: `fix/2128-oauth-badge-pending-state`
- Commit SHA: `2d93f7c0`
### Validation Run
- [x] `pnpm --filter openhuman-app format:check` — `All matched files use Prettier code style!` on the 6 changed files
- [x] `pnpm typecheck` — clean (`tsc --noEmit`)
- [x] Focused tests: `pnpm --filter openhuman-app exec vitest run --config test/vitest.config.ts src/store/__tests__/channelConnectionsSlice.test.ts src/hooks/__tests__/useOAuthConnectionListener.test.tsx src/components/channels/__tests__/DiscordConfig.test.tsx src/components/channels/__tests__/TelegramConfig.test.tsx` → 4 files, 27 tests pass
- [x] Rust fmt/check (if changed): `N/A: no Rust changes`
- [x] Tauri fmt/check (if changed): `N/A: no Tauri shell changes`
### Validation Blocked
- `command:` `git push` pre-push hook (`app:lint:commands-tokens`)
- `error:` `lint:commands-tokens requires ripgrep` — `rg` not installed on the dev environment
- `impact:` zero — the check greps a directory I did not modify (`src/components/commands/`). Pushed with `--no-verify` per the CLAUDE.md guidance for environment-related hook failures unrelated to the diff. Maintainers can re-run on CI to validate.
### Behavior Changes
- Intended behavior change: OAuth badges on channel panels transition out of `connecting` when the OAuth flow completes *or* fails, and starting a new method cancels the previous method's `connecting` row.
- User-visible effect: the reported bug (multiple methods stuck on `Connecting` simultaneously, Telegram OAuth never clearing) goes away. No new UI elements; only badge state transitions are affected.
### Parity Contract
- Legacy behavior preserved: existing `connected` and `error` transitions are unchanged; `disconnectChannelConnection`, `upsertChannelConnection`, `setChannelConnectionStatus` are all untouched. The Discord `oauth:success` path still produces the same final state (`status: 'connected'`, `capabilities: ['read', 'write']`); the inline effect was just refactored behind the shared hook.
- Guard/fallback/dispatch parity checks: hook only reacts when the event's `toolkit` (success) or `provider` (error) field matches the subscribed channel — siblings on other channels, and mismatched dispatches, are no-ops.
### Duplicate / Superseded PR Handling
- Duplicate PR(s): none found. #2170 cross-references #2128 in passing but its title and body close#2141 (channel selector error-status aggregation, a different surface).
- Canonical PR: this one.
- Resolution: N/A.
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
* **New Features**
* Reusable OAuth connection listener to handle OAuth success/error deep-link flows for Discord and Telegram.
* New action to clear other pending/connecting auth methods for a channel.
* **Bug Fixes**
* Prevents multiple auth methods from remaining "connecting"; switching stops in-flight polling and clears sibling pending modes.
* OAuth errors now record meaningful messages and listeners unsubscribe on unmount.
* **Tests**
* Added tests covering the OAuth listener and pending-clearing reducer behaviors.
<!-- review_stack_entry_start -->
[](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2256?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)
<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
Co-authored-by: sanil-23 <sanil@alphahuman.xyz>
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
- Implement command palette (⌘K) using cmdk and Radix Dialog with a global action registry.
- Add a keyboard shortcut system with a capture-phase hotkey manager and scope stack.
- Integrate seed navigation actions for Home, Chat, Intelligence, Skills, and Settings.
- Improve chat UX with a `useStickToBottom` hook for auto-scroll and fixed hydration errors.
- Refactor command UI to display shortcuts inline and remove the redundant help overlay.
- Fix Vite HMR connection issues within the Tauri/CEF environment.
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
* refactor(tauri): update development scripts and configuration for CEF integration
- Modified `package.json` scripts to consistently export `CEF_PATH` for all `cargo tauri` commands, ensuring a unified CEF binary distribution location.
- Removed the overlay window configuration from `tauri.conf.json` and updated related Rust functions to reflect this change, while retaining helper functions for potential future use.
- Updated documentation in `install.md` to clarify the importance of setting `CEF_PATH` for consistent CEF integration across builds.
- Enhanced the `ensure-tauri-cli.sh` script to set `CEF_PATH` and ensure proper installation of the vendored CEF-aware `tauri-cli`.
These changes streamline the development workflow and improve the reliability of CEF integration in the application.
* refactor(release): enhance macOS signing script for nested frameworks and helper apps
- Introduced a new `codesign_hardened` function to streamline the signing process with consistent options.
- Improved the signing logic for nested frameworks and helper applications, ensuring all binaries are signed correctly.
- Updated output messages for better clarity during the signing process, including detailed listings of bundle contents.
- Disabled the summarizer payload threshold in the configuration to prevent recursive invocations until the issue is resolved.
These changes improve the reliability and maintainability of the macOS signing and notarization workflow.
* feat(orchestrator): add current_time tool and enhance agent capabilities
- Introduced the `current_time` tool to provide the current date and time in UTC and local time zones, facilitating scheduling and reminders.
- Updated `agent.toml` to include new tools: `current_time`, `cron_add`, `cron_list`, `cron_remove`, and `schedule`, enhancing the orchestrator's functionality.
- Expanded documentation in `prompt.md` to guide users on utilizing the new direct tools effectively.
These changes improve the orchestrator's ability to handle time-related queries and scheduling tasks directly, enhancing user experience.
* feat(gmail): implement post-processing for Gmail responses to convert HTML to markdown
- Added a new `post_process` module specifically for Gmail, which modifies action responses to convert HTML content into markdown format, improving usability and reducing context token usage.
- Enhanced the `ComposioProvider` trait with a `post_process_action_result` method to allow providers to handle response modifications.
- Introduced a `post_process` function that checks for a `raw_html` flag in the arguments to determine whether to apply the conversion.
- Implemented tests to validate the HTML detection and conversion logic, ensuring the integrity of the post-processing functionality.
These changes enhance the handling of Gmail responses, making them more suitable for further processing and display in the application.
* refactor(gmail): improve HTML to markdown conversion and tool result budget
- Enhanced the `post_process` module for Gmail to handle large HTML payloads more efficiently, implementing a fallback mechanism for oversized content.
- Updated the `DEFAULT_TOOL_RESULT_BUDGET_BYTES` to `0`, disabling the budget temporarily while reworking the oversized-output path.
- Refined the `extract_markdown_body` function to better manage HTML content, ensuring cleaner markdown output and improved performance.
- Added utility functions for stripping HTML noise and handling large email bodies, enhancing the overall robustness of the email processing logic.
These changes optimize the handling of Gmail responses, improving usability and performance in processing large HTML content.
* feat(thread): implement thread title generation from user and assistant messages
- Added a new `generateTitleIfNeeded` function in the `threadApi` to create a thread title based on the first user message and the assistant's reply.
- Introduced a new `GenerateConversationThreadTitleRequest` struct to handle requests for title generation.
- Updated the `ChatRuntimeProvider` to dispatch the title generation action after processing inference responses.
- Enhanced the `threadSlice` with a new async thunk for generating thread titles, ensuring proper error handling and thread loading.
- Added tests for the new title generation functionality to validate the integration with the threads RPC.
These changes improve the user experience by automatically generating relevant thread titles, enhancing the organization of conversations.
* feat(conversations): add thread title update functionality
- Implemented `update_thread_title` method in `ConversationStore` to allow updating the title of existing conversation threads.
- Added a corresponding public function `update_thread_title` for external access.
- Enhanced tests to verify that thread titles are correctly updated and persisted in the store.
These changes improve the management of conversation threads by enabling dynamic title updates, enhancing user experience and organization.
* feat(docs): add comprehensive agent and subagent tool flow documentation
- Introduced a new document detailing the runtime flow of the agent harness, including execution paths for main agents and tools.
- Explained the differences between typed and fork subagents, and provided guidance for debugging harness and delegation issues.
- Included a file map outlining key components and their roles within the Rust implementation.
- Added a flow diagram to visually represent the interaction between agents, tools, and subagents.
These changes enhance the understanding of the agent architecture and improve the documentation for developers working with the system.
* feat(subagent): implement extraction tool and handoff cache for oversized results
- Introduced `extract_from_result` tool to allow targeted queries against oversized tool outputs, improving efficiency by directly interacting with the extraction model.
- Added `ResultHandoffCache` to manage oversized payloads, enabling progressive disclosure and reducing context length issues in sub-agent history.
- Implemented hygiene helpers for cleaning tool outputs before caching, ensuring only relevant data is stored.
- Enhanced the sub-agent runner with new modules for tool preparation and execution, streamlining the overall agent workflow.
These changes enhance the sub-agent's ability to handle large tool results effectively, improving performance and user experience.
* feat(conversations): enhance message bubble rendering and timeline entry formatting
- Introduced new utility functions for parsing and rendering agent messages, including `splitAgentMessageIntoBubbles` and `parseMarkdownTable`, to improve the display of messages in conversation threads.
- Implemented `BubbleMarkdown` and `TableCellMarkdown` components for better formatting of user and agent messages, ensuring consistent styling and interaction.
- Enhanced the `formatTimelineEntry` function to provide clearer titles and details for tool timeline entries, improving the user experience during interactions with subagents.
- Updated the `ChatRuntimeProvider` to utilize the new formatting functions, ensuring that tool timeline entries are displayed with relevant context and detail.
These changes improve the overall presentation and usability of conversation messages and tool interactions, enhancing user engagement and clarity.
* feat(subagent): enforce restrictions on sub-agent spawning tools
- Introduced a filter to prevent sub-agents from invoking their own spawning tools, specifically `spawn_subagent` and `delegate_*`, to avoid recursion issues and ensure proper delegation by the top-level orchestrator.
- Updated the `is_subagent_spawn_tool` function to identify these tools and integrated checks in both `run_typed_mode` and `run_fork_mode` to maintain the integrity of the sub-agent execution environment.
- Enhanced logging to track the removal of restricted tools from the sub-agent's tool surface, improving observability and debugging capabilities.
These changes strengthen the sub-agent architecture by enforcing strict boundaries on tool invocation, enhancing stability and performance.
* feat(conversations): add ToolTimelineBlock component and enhance timeline entry formatting
- Introduced the `ToolTimelineBlock` component to display tool timeline entries with improved formatting and user interaction, including auto-expansion for running entries.
- Enhanced the `formatTimelineEntry` function to include user-friendly titles for specific tool actions, such as 'Viewing your Integrations'.
- Updated the rendering logic in the `Conversations` component to filter and display visible messages more effectively, improving user experience during conversations.
These changes enhance the clarity and usability of tool interactions within conversation threads, providing users with better context and engagement.
* refactor(conversations): streamline component imports and enhance formatting consistency
- Consolidated import statements in `Conversations.tsx` and `ChatRuntimeProvider.tsx` for improved readability.
- Refactored the `ToolTimelineBlock` component to simplify its props structure.
- Enhanced formatting consistency in the rendering logic of agent message bubbles and timeline entries, ensuring cleaner code and better maintainability.
- Updated test cases for `splitAgentMessageIntoBubbles` and `formatTimelineEntry` to reflect formatting changes and ensure accuracy.
These changes improve code clarity and maintainability while enhancing the overall user experience in conversation threads.
* fix: satisfy pre-push lint on fix/tauri
* fix(chat): refresh usage counters after responses
* refactor(ChatRuntimeProvider): remove redundant import of requestUsageRefresh
- Eliminated the duplicate import statement for `requestUsageRefresh` in `ChatRuntimeProvider.tsx`, streamlining the code for better readability and maintainability.
* fix(chat): satisfy pre-push checks
* refactor(conversations): improve error handling and remove unused title generation logic
- Removed the unused `generateThreadTitleIfNeeded` function call from the `Conversations` component, simplifying the message dispatch logic.
- Enhanced error handling during message dispatch by using `unwrap()` to catch and log errors, providing clearer feedback on send failures.
- Updated the `ChatRuntimeProvider` to ensure proper error logging when generating thread titles, improving observability of issues related to title generation.
These changes streamline the conversation handling process and improve the robustness of error management in the chat system.
* refactor(conversations): improve formatting of title redaction and streamline test assertions
- Reformatted the `redact_title_for_log` function for better readability by adjusting the formatting of the output string.
- Simplified the assertion in the timezone test to enhance clarity and maintainability.
These changes contribute to cleaner code and improved test structure in the conversations module.
* refactor(tokenjuice): sort fact parts for improved formatting in inline summary
- Modified the `format_inline` function to sort the fact parts before generating the summary string. This change enhances the readability and consistency of the output by ensuring that facts are presented in a stable order.
These changes contribute to better formatted summaries in the token juice module.
* fix: address remaining CodeRabbit review comments
* refactor: streamline error handling and improve code readability
- Updated error handling in prompt loading to use `std::io::Error::other` for better clarity.
- Simplified conditional checks using `is_none_or` and `is_some_and` for improved readability.
- Refactored string trimming logic to utilize array syntax for better clarity.
- Enhanced default implementations for several structs to reduce boilerplate code.
These changes contribute to cleaner code and improved maintainability across various modules.
* refactor: improve code formatting and structure
- Adjusted formatting in `post_process.rs` for better readability by aligning the conditional block.
- Combined derive attributes in `tools.rs` for the `ComputerControlConfig` struct to reduce redundancy.
- Streamlined entry retrieval in `compatible.rs` by condensing multiple lines into a single line for clarity.
These changes enhance code readability and maintainability across the affected modules.