Refine billing, settings, rewards, and usage UI#547
Refine billing, settings, rewards, and usage UI#547senamakel merged 25 commits intotinyhumansai:mainfrom
Conversation
- Added `react-icons` dependency to the project for enhanced icon usage. - Introduced new skill icons in the `toolkitMeta.tsx` component, replacing SVG icons with `react-icons` for improved maintainability and consistency. - Created a new `skillCategories.ts` file to define skill categories and their order, streamlining the management of skill categories across the application. - Refactored the `SkillCategoryFilter` component to utilize the new skill categories structure, enhancing clarity and reducing redundancy in the codebase. - Updated the `Skills` page to leverage the new icon rendering method and skill categories, improving the overall user experience. - Added tests to ensure the correct functionality of the new skill category and icon implementations.
- Updated `.env.example` and `app/.env.example` to reflect new backend URL and added optional environment selector for staging. - Enhanced `SettingsHome` component by separating account and billing sections for improved clarity. - Introduced a new billing section in the settings menu to streamline user navigation. - Updated `useSettingsNavigation` hook to accommodate changes in settings structure. - Improved `BillingPanel` to handle session token checks and ensure accurate billing state retrieval. - Refactored `Rewards` page to enhance user experience with clearer progress indicators and improved layout. - Added tests to validate changes in the rewards and settings components.
…tion - Introduced a new staging API base URL constant for better environment management. - Added app environment variable constants to streamline environment detection. - Refactored effective API URL resolution to accommodate environment-specific defaults. - Implemented functions to resolve app environment from process environment variables. - Added tests to validate the correct behavior of staging and production API URL handling.
- Introduced `RewardsCommunityTab` and `RewardsReferralsTab` components to enhance the rewards management interface. - The `RewardsCommunityTab` displays user progress, Discord role statuses, and connection options, improving user engagement with rewards. - The `RewardsReferralsTab` allows users to manage their referral program, track progress, and access coupon rewards in a streamlined layout. - Updated the `Rewards` page to integrate these new tabs, enhancing overall user experience and navigation.
…components - Added `ReferralRewardsSection` to manage referral statistics and code application, enhancing user engagement with the referral program. - Created `RewardsRedeemTab` to streamline the process of applying reward codes, improving the overall rewards management experience. - Updated `Rewards` page to include the new redeem tab, allowing users to easily switch between referral and redeem functionalities. - Refactored `RewardsCommunityTab` to adjust the referral selection handler, ensuring consistent navigation across the rewards interface. - Removed redundant UI elements in `RewardsCouponSection` for a cleaner layout. - Enhanced tests to cover new functionalities and ensure robust performance across the rewards system.
…lter and Rewards pages - Added a new `PillTabBar` component to enhance tab navigation with customizable styles and item rendering. - Refactored `SkillCategoryFilter` to utilize `PillTabBar`, improving code clarity and reducing redundancy in button rendering. - Updated the `Rewards` page to replace the existing tab navigation with `PillTabBar`, streamlining the user interface for switching between referral, rewards, and redeem tabs. - Enhanced the `ReferralRewardsSection` layout for better user experience and consistency across the rewards management interface. - Improved tests to cover the new `PillTabBar` functionality and ensure robust performance across the updated components.
- Changed the input placeholder from "Promo code" to "Coupon code" for clarity. - Updated button text from "Apply code" to "Redeem Code" to better reflect the action being performed. - Adjusted loading state text from "Applying…" to "Redeeming..." for consistency in user feedback.
- Updated the `ComposioConnectModal` to simplify the connection message, removing unnecessary wording for clarity. - Introduced a `TOOLKIT_ALIASES` mapping in `toolkitMeta.tsx` to standardize toolkit slugs, improving consistency across the application. - Refactored the `composioToolkitMeta` function to utilize the new slug mapping, enhancing toolkit metadata retrieval. - Improved the `useComposioIntegrations` hook to leverage the canonicalized toolkit slugs for better integration handling. - Adjusted the `Skills` page to normalize toolkit slugs during rendering, ensuring a consistent user experience. - Updated tests to reflect changes in messaging and toolkit handling, ensuring robust functionality across the application.
… features - Introduced `IntelligenceDreamsTab`, which displays generated dreams based on daily life events, enhancing user engagement with a visually appealing layout. - Added `IntelligenceMemoryTab` to manage actionable items, featuring search and filter capabilities for improved user interaction with memory data. - Created `IntelligenceSubconsciousTab` to handle subconscious tasks and logs, providing users with insights and management options for their subconscious activities. - Refactored the `RewardsCommunityTab` to remove unused Discord role status logic, streamlining the component for better performance. - Implemented a new `PageBackButton` component for consistent navigation across settings pages. - Enhanced the `BillingPanel` and its subcomponents to improve user experience in managing billing and subscription details, including transaction history and payment methods. - Added new billing-related tabs for better organization and access to billing features, including `BillingOverviewTab`, `BillingPaymentsTab`, and `BillingPlansTab`. - Updated tests to ensure functionality across new and refactored components, maintaining robust application performance.
…ed user experience - Changed the default selected tab in `BillingPanel` from 'overview' to 'plans' to prioritize subscription management. - Removed the `BillingOverviewTab` from the `BillingPanel`, streamlining the billing interface. - Enhanced the `BillingPlansTab` header to clarify the purpose, changing "Explore tiers" to "Choose a Subscription Plan". - Updated the description in `BillingPlansTab` for better clarity on payment options. - Improved the layout of the `SubscriptionPlans` component to better highlight crypto payment options and their availability. - Cleaned up unused code and comments for better maintainability.
…ents - Removed unused `teamUsage` state and related API call from `BillingPanel` to simplify data management. - Adjusted layout in `BillingPlansTab` for improved visual hierarchy and user experience. - Cleaned up code by eliminating unnecessary comments and enhancing maintainability.
…onsiveness - Updated the layout of the `SubscriptionPlans` component to ensure better responsiveness and visual consistency. - Adjusted class names to include minimum height and width properties for better alignment across different screen sizes. - Enhanced the layout of the pricing display section for improved clarity and user experience.
…or improved clarity and user experience - Adjusted pricing for BASIC and PRO plans to reflect new monthly and annual rates. - Enhanced feature descriptions for subscription plans to better communicate value. - Removed redundant UI elements in the BillingPaymentsTab and PayAsYouGoCard for a cleaner layout. - Added loading and confirmation messages in SubscriptionPlans to improve user feedback during payment processes. - Updated class names for better responsiveness and visual consistency across billing components.
…ing components - Updated error message in BillingPanel to specify adding a payment card on Stripe for clarity. - Changed header text in AutoRechargeSection to "Enable Auto-Recharge" for better user understanding. - Modified button text in AutoRechargeSection to "Add card on Stripe" for consistency. - Enhanced styling in PayAsYouGoCard for improved visual appeal and user interaction. - Removed redundant UI elements in PayAsYouGoCard for a cleaner layout.
…tency across billing components - Eliminated `setArError` prop from BillingPanel, AutoRechargeSection, and BillingPaymentsTab to streamline error handling. - Enhanced layout and styling in BillingHistoryTab and SubscriptionPlans for better visual consistency and user experience. - Removed the BillingOverviewTab component to simplify the billing interface and improve maintainability.
…onPlans display - Revised feature descriptions in the PLANS array for clarity and conciseness. - Increased the number of displayed features in the SubscriptionPlans component to provide users with more information at a glance.
… functionality - Revised feature descriptions in the PLANS array to reflect increased usage limits. - Renamed header in BillingHistoryTab to "Transaction History" and updated description for clarity. - Adjusted transaction amount formatting in BillingHistoryTab to display five decimal places. - Removed redundant UI elements in BillingPaymentsTab to streamline the layout. - Enhanced PayAsYouGoCard with improved credit balance display and top-up options, including validation for custom amounts.
- Introduced `shouldShowBudgetCompletedMessage` to the `UsageState` interface to indicate when the budget completion message should be displayed. - Updated the `useUsageState` hook to calculate the budget completion message based on user credits and budget status. - Enhanced tests for `useUsageState` to verify the correct behavior of the budget completion message under various scenarios. - Modified the `Conversations` component to display the budget completion message appropriately based on the new logic.
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughAdds env-aware backend selection and staging defaults, centralizes app-root/workspace resolution with a staging suffix, introduces a reusable PillTabBar and extracts multiple tab components (Intelligence, Rewards, Billing), centralizes skill/category icons, and updates billing/credits normalization and related UI/tests. Changes
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Page as Frontend Page
participant Pill as PillTabBar<br/>(component)
participant Tab as Tab Component
participant API as Backend/API
User->>Page: open page
Page->>Pill: render PillTabBar(items)
Pill->>User: show tab pills
User->>Pill: click pill
Pill->>Page: onChange(selected)
Page->>Tab: mount selected Tab component
Tab->>API: fetch data (via BACKEND_URL)
API-->>Tab: return data
Tab->>User: render tab content
sequenceDiagram
participant Frontend as Frontend App
participant Config as config.ts
participant Env as Environment
participant Backend as Backend API
Frontend->>Config: read VITE_OPENHUMAN_APP_ENV & VITE_BACKEND_URL
Config->>Env: retrieve vars
Env-->>Config: return values
Config->>Config: compute APP_ENV (trim/lowercase)
Config->>Config: choose DEFAULT_BACKEND_URL (staging vs production)
Config-->>Frontend: provide BACKEND_URL
Frontend->>Backend: perform requests to BACKEND_URL
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 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: 12
🧹 Nitpick comments (20)
src/openhuman/channels/controllers/definitions.rs (1)
434-452: Consider adding deserialize assertions to complete the wire-contract test.This currently validates serialization only. Adding
serde_json::from_valuechecks would guard inbound compatibility too.Proposed test addition
fn auth_mode_serializes_to_expected_wire_values() { assert_eq!( serde_json::to_value(ChannelAuthMode::ApiKey).expect("serialize"), serde_json::Value::String("api_key".to_string()) ); @@ assert_eq!( serde_json::to_value(ChannelAuthMode::ManagedDm).expect("serialize"), serde_json::Value::String("managed_dm".to_string()) ); } + + #[test] + fn auth_mode_deserializes_from_expected_wire_values() { + assert_eq!( + serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String("api_key".to_string())) + .expect("deserialize"), + ChannelAuthMode::ApiKey + ); + assert_eq!( + serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String("bot_token".to_string())) + .expect("deserialize"), + ChannelAuthMode::BotToken + ); + assert_eq!( + serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String("oauth".to_string())) + .expect("deserialize"), + ChannelAuthMode::OAuth + ); + assert_eq!( + serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String("managed_dm".to_string())) + .expect("deserialize"), + ChannelAuthMode::ManagedDm + ); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/channels/controllers/definitions.rs` around lines 434 - 452, The test auth_mode_serializes_to_expected_wire_values only checks serde_json::to_value; add reciprocal serde_json::from_value assertions to ensure deserialization maps the wire strings back to the enum variants. For each checked value ("api_key", "bot_token", "oauth", "managed_dm") call serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String(...)) and assert_eq! the result to ChannelAuthMode::ApiKey, ChannelAuthMode::BotToken, ChannelAuthMode::OAuth, and ChannelAuthMode::ManagedDm respectively so both directions are covered.src/openhuman/channels/providers/web.rs (1)
91-97: Harden the budget-error matcher to reduce false negatives.The current matcher is strict and can miss common variants (for example,
top-uporbudget has been exceeded). Consider broadening the phrase set so budget exhaustion is consistently mapped to the friendly response.Suggested refactor
fn is_inference_budget_exceeded_error(message: &str) -> bool { let normalized = message.trim().to_ascii_lowercase(); - normalized.contains("budget exceeded") - && (normalized.contains("add credits to continue") - || normalized.contains("add credits") - || normalized.contains("top up")) + let budget_phrase = normalized.contains("budget exceeded") + || normalized.contains("budget has been exceeded") + || normalized.contains("insufficient credits"); + let action_phrase = normalized.contains("add credits") + || normalized.contains("credits to continue") + || normalized.contains("top up") + || normalized.contains("top-up"); + budget_phrase && action_phrase }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/channels/providers/web.rs` around lines 91 - 97, The current is_inference_budget_exceeded_error matcher is too strict and misses variants like "top-up", "budget has been exceeded", or "out of credits"; replace the simple substring checks with a broader, case-insensitive pattern (or multiple patterns) that normalizes whitespace/hyphens and matches common variants such as r"budget.*exceed", r"top[- ]?up", r"add.*credits", r"out of credits", and r"no remaining credits"; update is_inference_budget_exceeded_error to trim and to_ascii_lowercase the message, collapse repeated whitespace/hyphens, and test against these regexes (prefer compiling them once as static/lazy to avoid reallocation) so budget exhaustion messages are reliably detected.src/openhuman/encryption/core.rs (1)
107-108: Implementation is correct; consider doc wording sync.The path resolution change is solid. Optional follow-up: update the nearby docs to mention env-aware root names (e.g., staging suffix) instead of only
~/.openhuman.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/encryption/core.rs` around lines 107 - 108, Update the docs and any nearby comments to reflect the env-aware root directory behavior introduced by the path resolution change: replace or augment references to "~/.openhuman" with a note that default_root_openhuman_dir() (in src/openhuman/encryption/core.rs calling crate::openhuman::config::default_root_openhuman_dir()) may return environment-specific names (e.g., staging suffixes) and describe how to override via env if applicable; ensure any README, comments, or docstrings near default_root_openhuman_dir and its callers mention the env-aware naming convention.src/openhuman/agent/harness/definition_loader.rs (1)
126-128: Log root-dir resolution failures before returningNone.Line 126 currently drops resolver errors silently via
.ok(). Adding a debug log here will make startup path issues traceable without changing behavior.♻️ Suggested change
- crate::openhuman::config::default_root_openhuman_dir() - .ok() - .map(|dir| dir.join("agents")) + match crate::openhuman::config::default_root_openhuman_dir() { + Ok(dir) => Some(dir.join("agents")), + Err(err) => { + tracing::debug!( + error = %err, + "[agent_defs] unable to resolve default root directory for user agents" + ); + None + } + }As per coding guidelines: "Add substantial, development-oriented logs while implementing features or fixes so issues are easy to trace end-to-end."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/openhuman/agent/harness/definition_loader.rs` around lines 126 - 128, The call to crate::openhuman::config::default_root_openhuman_dir() currently silences errors via .ok() — change this so any Err is logged before returning None: call default_root_openhuman_dir(), match or map_err to capture the error, emit a debug (or tracing::debug) log containing the error and context (e.g., "resolving root openhuman dir failed"), then proceed to return None or map to dir.join("agents") on Ok; reference the default_root_openhuman_dir() invocation and the subsequent .join("agents") mapping when making the change..env.example (1)
10-16: Optional: reorder keys to satisfydotenv-linterkey-order checks.Current ordering triggers
UnorderedKeywarnings forBACKEND_URLandVITE_BACKEND_URL.🔧 Suggested reorder
-OPENHUMAN_APP_ENV=staging BACKEND_URL=https://staging-api.tinyhumans.ai +OPENHUMAN_APP_ENV=staging - VITE_OPENHUMAN_APP_ENV=staging VITE_BACKEND_URL=https://staging-api.tinyhumans.ai +VITE_OPENHUMAN_APP_ENV=staging🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In @.env.example around lines 10 - 16, The .env.example triggers dotenv-linter UnorderedKey warnings for BACKEND_URL and VITE_BACKEND_URL; fix this by reordering the keys so they follow the linter's expected ordering (alphabetical/grouped by prefix). Specifically, move BACKEND_URL to sit alphabetically with other non-VITE keys (after OPENHUMAN_APP_ENV) and place VITE_BACKEND_URL so it is ordered correctly among VITE_* entries (after VITE_OPENHUMAN_APP_ENV) to eliminate the UnorderedKey warnings for BACKEND_URL and VITE_BACKEND_URL.app/src/components/intelligence/IntelligenceSubconsciousTab.tsx (1)
57-67: Add[subconscious-ui]debug checkpoints around these new side effects.This tab now owns task creation, manual runs, escalation decisions, toggles, removals, and navigation branches, but none of those transitions emit namespaced debug logs. That will make failures in these flows much harder to trace from app logs.
🪵 Example logging points
const handleAddTask = async (e: FormEvent<HTMLFormElement>) => { e.preventDefault(); const title = newTaskTitle.trim(); if (!title) return; try { + console.debug('[subconscious-ui] add task start', { titleLen: title.length }); await addSubconsciousTask(title); setNewTaskTitle(''); + console.debug('[subconscious-ui] add task success'); } catch { + console.debug('[subconscious-ui] add task failed'); // handled by hook } };console.debug('[subconscious-ui] trigger tick click'); console.debug('[subconscious-ui] escalation action', { escalationId: esc.id, action: 'approve' }); console.debug('[subconscious-ui] task toggle', { taskId: task.id, enabled: !task.enabled });As per coding guidelines, "Add substantial, development-oriented logs while implementing features or fixes so issues are easy to trace end-to-end; log critical checkpoints including entry/exit points, branch decisions, external calls, retries/timeouts, state transitions, and error handling paths."
Also applies to: 125-287
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/intelligence/IntelligenceSubconsciousTab.tsx` around lines 57 - 67, Add namespaced debug checkpoints "[subconscious-ui]" around all side-effect handlers in this component: e.g., inside handleAddTask log entry with the title before calling addSubconsciousTask, log success after setNewTaskTitle('') and log failure in the catch; do the same pattern for other side-effect functions in this file (e.g., handleRunTick / any "run tick" handler, escalation handlers referenced by escalationId or esc.id, task toggle handler like handleToggleTask that calls task toggle APIs, handleRemoveTask, and any navigation branch functions) — each should emit console.debug('[subconscious-ui] <action>', { relevantIds/state/branch }) at entry, before/after external calls (addSubconsciousTask, manual run, escalation approve/deny), and on error to aid tracing.app/src/lib/composio/hooks.ts (1)
3-3: Decouple composio hook logic from UI component modules.
app/src/lib/composio/hooks.tsnow depends onapp/src/components/composio/toolkitMeta.tsx. Please move slug canonicalization into a non-UI shared module underapp/src/lib/composio/and import it from both places.♻️ Minimal direction for the import boundary
-import { canonicalizeComposioToolkitSlug } from '../../components/composio/toolkitMeta'; +import { canonicalizeComposioToolkitSlug } from './toolkitSlug';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/lib/composio/hooks.ts` at line 3, The composio hook is importing canonicalizeComposioToolkitSlug from a UI component module; extract the slug canonicalization logic into a new non-UI shared module (e.g., create a plain JS/TS file exporting canonicalizeComposioToolkitSlug under the composio lib), move the implementation from toolkitMeta (remove it from the UI component), and update both hooks.ts (which currently imports canonicalizeComposioToolkitSlug) and the toolkitMeta component to import the function from the new shared module so the hook no longer depends on UI code.app/src/pages/__tests__/Rewards.test.tsx (1)
73-73: Avoid asserting duplicate loading-text count.
toHaveLength(2)couples this test to rendering structure instead of behavior. A single loading-state presence assertion is more stable.✅ More behavior-focused assertion
- expect(screen.getAllByText('Loading rewards…')).toHaveLength(2); + expect(screen.getByText('Loading rewards…')).toBeInTheDocument();As per coding guidelines, "Prefer testing behavior over implementation details in Vitest unit tests".
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/pages/__tests__/Rewards.test.tsx` at line 73, The test currently asserts an exact duplicate loading-text count using screen.getAllByText('Loading rewards…').toHaveLength(2'), which couples the test to implementation details; replace that assertion with a behavior-focused presence check such as expect(screen.getByText('Loading rewards…')).toBeInTheDocument() (or expect(screen.queryByText('Loading rewards…')).not.toBeNull()) so the test only verifies the loading state exists rather than how many times it’s rendered.app/src/components/settings/panels/billing/BillingHistoryTab.tsx (2)
53-55: Five decimal places for USD display seems excessive.
toFixed(5)shows amounts like$0.00123which is unusual for user-facing currency. ConsidertoFixed(2)for standard USD display, or if sub-cent precision is intentional (e.g., for micro-transactions), add a comment explaining why.💰 Proposed fix for standard currency display
<div className={`font-semibold ${isEarn ? 'text-sage-600' : 'text-stone-950'}`}> - {isEarn ? '+' : '-'}${Math.abs(transaction.amountUsd).toFixed(5)} + {isEarn ? '+' : '-'}${Math.abs(transaction.amountUsd).toFixed(2)} </div>🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/billing/BillingHistoryTab.tsx` around lines 53 - 55, The USD amount in BillingHistoryTab is formatted with toFixed(5), which is excessive for user-facing currency; update the display logic that renders {isEarn ? '+' : '-'}${Math.abs(transaction.amountUsd).toFixed(5)} to use toFixed(2) for standard USD formatting (or, if sub-cent precision is intentionally required for micro-transactions, retain the higher precision but add a clear comment near the transaction.amountUsd formatting explaining the rationale and expected units/precision so future readers understand why 5 decimals are used).
44-50: Hardcoded'en-US'locale for date formatting.This is acceptable for now, but consider using
undefined(user's default locale) or a shared locale constant if internationalization is planned.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/billing/BillingHistoryTab.tsx` around lines 44 - 50, The date formatting in BillingHistoryTab.tsx hardcodes the 'en-US' locale for new Date(transaction.createdAt).toLocaleDateString; update this to use the user's default locale (pass undefined instead of 'en-US') or a shared locale constant (e.g., LOCALE or getUserLocale()) so the display respects i18n; adjust the expression around transaction.createdAt in the BillingHistoryTab component accordingly.app/src/services/api/creditsApi.ts (2)
153-160:asNumberandnormalizeUsdare identical—consolidate into one helper.Both functions perform the same logic: check for finite number, parse string if non-empty, return fallback. Consider removing
asNumberand usingnormalizeUsdthroughout, or vice versa.♻️ Proposed consolidation
-function asNumber(value: unknown): number { - if (typeof value === 'number' && Number.isFinite(value)) return value; - if (typeof value === 'string' && value.trim() !== '') { - const parsed = Number(value); - if (Number.isFinite(parsed)) return parsed; - } - return 0; -} +// Use normalizeUsd(value, 0) instead of asNumber(value)Also applies to: 199-206
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/services/api/creditsApi.ts` around lines 153 - 160, The two helpers asNumber and normalizeUsd contain identical logic; consolidate them into one function (pick a single canonical name, e.g., normalizeUsd) and remove the duplicate (delete asNumber or normalizeUsd accordingly), then replace all usages of the removed name with the chosen helper (update callers that reference asNumber and callers that reference normalizeUsd, including the code around the current function and the other instance at lines referenced in the review). Ensure the chosen helper is exported/visible where needed and keep the exact behavior (finite number check, non-empty string parse, fallback 0).
208-231: Consider adding debug logging when normalization falls back to defaults.The lenient parsing silently returns
0when expected fields are missing. Per coding guidelines, adding debug logs for fallback cases would help trace data issues when the backend response shape changes unexpectedly.🔍 Example debug logging
function normalizeCreditBalance(payload: unknown): CreditBalance { const raw = (payload && typeof payload === 'object' ? payload : {}) as Record<string, unknown>; const nested = asRecord(raw.data) ?? asRecord(raw.balance) ?? null; const source = nested ?? raw; + if (import.meta.env.DEV) { + const hasPromoField = 'promotionBalanceUsd' in source || 'promotion_balance_usd' in source; + const hasTeamField = 'teamTopupUsd' in source || 'team_topup_usd' in source; + if (!hasPromoField || !hasTeamField) { + console.debug('[creditsApi] normalizeCreditBalance fallback', { hasPromoField, hasTeamField, keys: Object.keys(source) }); + } + } + return { promotionBalanceUsd: normalizeUsd(As per coding guidelines: "Add substantial, development-oriented logs while implementing features or fixes so issues are easy to trace end-to-end."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/services/api/creditsApi.ts` around lines 208 - 231, The normalizeCreditBalance function silently falls back to zeros when expected fields are missing; update normalizeCreditBalance to detect when none of the known balance keys are present on source (for promotionBalanceUsd and teamTopupUsd) and emit a development debug log with the raw source object and which keys were missing before returning normalized values, using the project's debug/logger utility (e.g., logger.debug) and keep calls around normalizeUsd so you can trace when fallback-to-default occurs; reference normalizeCreditBalance, normalizeUsd and asRecord to locate where to add the checks/logging.app/src/pages/Settings.tsx (2)
276-276: Consider adding a comment explaining whyBillingPanelis not wrapped.Unlike other leaf panels,
BillingPanelrenders withoutWrappedSettingsPage. This is intentional (it uses its own widermax-w-2xllayout), but a brief comment would help future maintainers understand the exception.+ {/* BillingPanel uses its own wider layout (max-w-2xl) with internal tabs */} <Route path="billing" element={<BillingPanel />} />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/pages/Settings.tsx` at line 276, Add a short inline comment in Settings.tsx next to the Route for BillingPanel explaining why it is not wrapped by WrappedSettingsPage (e.g., "BillingPanel intentionally not wrapped because it uses its own wider max-w-2xl layout"); update the Route line (<Route path="billing" element={<BillingPanel />} />) to include that comment so future maintainers know this is an intentional exception rather than an oversight.
217-225:wrapSettingsPageis recreated on every render—consider memoizing or extracting.Defining this helper inside the component body means a new function instance is created on each render. While React Router likely handles this gracefully, extracting it outside the component or wrapping with
useCallbackwould be slightly more idiomatic.♻️ Option: Extract outside component
+const wrapSettingsPage = (element: React.ReactNode) => ( + <WrappedSettingsPage> + {element} + <div className="border-t border-stone-100 px-4 py-3 text-center text-[11px] text-stone-400"> + Beta build - v{APP_VERSION} + </div> + </WrappedSettingsPage> +); + const Settings = () => { - const wrapSettingsPage = (element: React.ReactNode) => ( - <WrappedSettingsPage> - {element} - <div className="border-t border-stone-100 px-4 py-3 text-center text-[11px] text-stone-400"> - Beta build - v{APP_VERSION} - </div> - </WrappedSettingsPage> - ); - return (🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/pages/Settings.tsx` around lines 217 - 225, The helper wrapSettingsPage defined inside the Settings component is recreated on every render; move it outside the Settings function or memoize it with React.useCallback to avoid allocating a new function each render. Locate the wrapSettingsPage arrow function (which returns <WrappedSettingsPage> with APP_VERSION) and either: 1) extract it as a top-level function that accepts element: React.ReactNode, or 2) wrap its definition with useCallback inside Settings (e.g., const wrapSettingsPage = useCallback((element) => ..., [APP_VERSION])) so the function identity is stable. Ensure the JSX still renders the Beta build v{APP_VERSION} and update any references to wrapSettingsPage accordingly.app/src/components/composio/toolkitMeta.tsx (1)
44-48: Inconsistent alias direction forgooglesheets.Other aliases map underscore variants to non-underscore canonical forms (
google_calendar→googlecalendar), butgooglesheetsmaps togoogle_sheets(opposite direction). This works because CATALOG has both entries, but the inconsistency may cause confusion.Consider aligning the direction:
♻️ Option A: Make google_sheets the canonical form consistently
const TOOLKIT_ALIASES: Record<string, string> = { google_calendar: 'googlecalendar', google_drive: 'googledrive', - googlesheets: 'google_sheets', + // google_sheets is canonical; no alias needed if backend always sends google_sheets };♻️ Option B: Align with other aliases (non-underscore canonical)
const TOOLKIT_ALIASES: Record<string, string> = { google_calendar: 'googlecalendar', google_drive: 'googledrive', - googlesheets: 'google_sheets', + google_sheets: 'googlesheets', };Then update
KNOWN_COMPOSIO_TOOLKITSto use'googlesheets'instead of'google_sheets'.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/composio/toolkitMeta.tsx` around lines 44 - 48, TOOLKIT_ALIASES is inconsistent: change the googlesheets entry so the canonical form matches the other aliases (map "google_sheets" -> "googlesheets" or vice versa). Recommended: update TOOLKIT_ALIASES to map "google_sheets" to "googlesheets" (so all mappings go from underscored to non-underscored canonical names) and then update KNOWN_COMPOSIO_TOOLKITS to use "googlesheets" instead of "google_sheets" wherever it appears.app/src/components/PillTabBar.tsx (1)
27-45: Consider adding ARIA tab semantics for improved accessibility.The component uses
aria-pressedwhich is appropriate for toggle buttons, but since this functions as a tablist, usingrole="tablist"on the container androle="tab"on buttons would provide clearer semantics for screen readers.♿ Proposed accessibility enhancement
return ( - <div className={containerClassName}> + <div className={containerClassName} role="tablist"> {items.map(item => { const active = selected === item.value; return ( <button key={item.value} type="button" - aria-pressed={active} + role="tab" + aria-selected={active} onClick={() => onChange(item.value)}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/PillTabBar.tsx` around lines 27 - 45, This component is presenting a tab list but uses aria-pressed; update PillTabBar to expose proper ARIA tab semantics: add role="tablist" to the container (containerClassName), set each button to role="tab" (in the items.map render), replace aria-pressed with aria-selected={active} and manage keyboard focus via tabIndex={active ? 0 : -1}; also ensure each tab has a stable id (e.g., derived from item.value) and, if panels exist, include aria-controls linking to the panel id so screen readers can associate tabs with their panels; keep onChange(selected) behavior intact and leave renderItem/selected/activeClassName/inactiveClassName usage as-is.app/src/components/settings/panels/billing/PayAsYouGoCard.tsx (2)
5-11: Remove unusedonBalanceRefreshprop from interface.The
onBalanceRefreshprop is defined inPayAsYouGoCardPropsbut never destructured or used in the component (line 18). The parentBillingPaymentsTabstill passes this prop, creating a misleading API contract.♻️ Proposed fix
interface PayAsYouGoCardProps { creditBalance: CreditBalance | null; isLoadingCredits: boolean; isToppingUp: boolean; onTopUp: (amountUsd: number) => void; - onBalanceRefresh: () => void; }Also update the parent in
BillingPaymentsTab.tsxto stop passing this prop.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/billing/PayAsYouGoCard.tsx` around lines 5 - 11, Remove the unused onBalanceRefresh prop from the PayAsYouGoCardProps interface and from the PayAsYouGoCard component’s props list: locate the interface PayAsYouGoCardProps and delete the onBalanceRefresh declaration, then update the PayAsYouGoCard component signature so it no longer expects or destructures onBalanceRefresh; finally, update the parent BillingPaymentsTab to stop passing onBalanceRefresh into PayAsYouGoCard so the prop contract is consistent.
29-39: Derive validity inline instead of usinguseEffectto sync state.Using
useEffectto setcustomTopUpAmountValidbased oncustomTopUpAmountcreates an extra render cycle. This can be computed inline or viauseMemo. Based on learnings, this codebase discourages synchronoussetStatecalls insideuseEffectbodies.♻️ Proposed fix
- const [customTopUpAmount, setCustomTopUpAmount] = useState(''); - const [customTopUpAmountValid, setCustomTopUpAmountValid] = useState(false); + const [customTopUpAmount, setCustomTopUpAmount] = useState(''); + const customTopUpAmountValid = Number(customTopUpAmount) > 0; const handleCustomTopUp = () => { if (!customTopUpAmountValid || isToppingUp) return; onTopUp(Number(customTopUpAmount)); }; - - useEffect(() => { - setCustomTopUpAmountValid(Number(customTopUpAmount) > 0); - }, [customTopUpAmount]);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/billing/PayAsYouGoCard.tsx` around lines 29 - 39, customTopUpAmountValid is derived from customTopUpAmount via a useEffect causing an extra render; remove the customTopUpAmountValid state and the useEffect, compute validity inline (or with useMemo) from customTopUpAmount (e.g., const customTopUpAmountValid = Number(customTopUpAmount) > 0) and update handleCustomTopUp to reference that derived value; delete setCustomTopUpAmountValid and the useEffect block to avoid synchronous state updates.app/src/components/settings/panels/billing/SubscriptionPlans.tsx (1)
202-202: Consider removing commented-out code.The tagline rendering is commented out. If this is intentionally disabled, consider removing the dead code entirely to reduce noise.
♻️ Proposed fix
- {/* {plan.tagline && <p className="mt-1 text-sm text-stone-500">{plan.tagline}</p>} */}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/settings/panels/billing/SubscriptionPlans.tsx` at line 202, The commented-out JSX line rendering plan.tagline inside the SubscriptionPlans component is dead code and should be removed to reduce noise; locate the commented snippet referencing plan.tagline (the line with {/* {plan.tagline && <p className="mt-1 text-sm text-stone-500">{plan.tagline}</p>} */}) in SubscriptionPlans.tsx and delete it, or if the tagline may be needed later, replace the comment with a clear TODO explaining why it’s disabled and reference the plan.tagline conditional rendering for future restoration.app/src/components/rewards/RewardsCommunityTab.tsx (1)
68-80: Remove unusedonSelectRedeemandplanprops.The props
onSelectRedeemandplanare defined inRewardsCommunityTabPropsbut never destructured or used in the component body. This creates a misleading API contract.♻️ Proposed fix
interface RewardsCommunityTabProps { error: string | null; isLoading: boolean; - onSelectRedeem: () => void; - plan: string; snapshot: RewardsSnapshot | null; } export default function RewardsCommunityTab({ error, isLoading, snapshot, }: RewardsCommunityTabProps) {Also update the parent
Rewards.tsxto stop passing these unused props.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/components/rewards/RewardsCommunityTab.tsx` around lines 68 - 80, Remove the unused props from the component API: delete onSelectRedeem and plan from the RewardsCommunityTabProps interface and stop expecting/destructuring them in the RewardsCommunityTab function; then update the parent component (Rewards.tsx) to stop passing onSelectRedeem and plan into <RewardsCommunityTab /> so the prop contract and usage stay consistent (refer to RewardsCommunityTabProps, RewardsCommunityTab, onSelectRedeem, plan, and the parent Rewards.tsx component to locate the changes).
🤖 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/components/intelligence/IntelligenceDreamsTab.tsx`:
- Around line 21-24: The paragraph in the IntelligenceDreamsTab component
contains typos and grammar issues; update the text node (inside the <p> with
className "text-stone-400 text-sm mb-1") to read something like: "Twice every
day, OpenHuman will generate a dream (or a summary) based on everything that has
happened in your life today. These dreams are then indexed and can be used to
influence OpenHuman's behavior." Replace the existing string in
IntelligenceDreamsTab accordingly.
In `@app/src/components/intelligence/IntelligenceMemoryTab.tsx`:
- Around line 37-59: In IntelligenceMemoryTab.tsx the search input
(searchFilter/setSearchFilter) and source select (sourceFilter/setSourceFilter)
lack accessible names for screen readers; add programmatic labels by either
wrapping each control with a <label htmlFor="..."> and setting matching id
attributes (e.g., id="actionable-search" and id="actionable-source") or adding
explicit aria-label or aria-labelledby attributes that describe their purpose
(e.g., "Search actionable items" and "Filter by source"), ensuring the select
options remain unchanged and the onChange handlers (setSearchFilter,
setSourceFilter) continue to work.
In `@app/src/components/intelligence/IntelligenceSubconsciousTab.tsx`:
- Around line 179-184: The onClick currently calls dismissEscalation(esc.id)
before navigate('/skills'), which removes the escalation immediately and loses
the reminder if the user doesn't actually fix it; remove the immediate
dismissEscalation call from the onClick handler and instead navigate to the
Skills page carrying the escalation id (e.g., via route state or a query param)
so the Skills component (or the fix action there) can call
dismissEscalation(esc.id) only after the user resolves the issue; update related
handlers in the Skills page to read the passed esc id and perform the dismissal
when the fix completes.
- Around line 236-267: The toggle and remove buttons lack accessible names and
state; update the toggle button (where toggleSubconsciousTask is called) to
include an accessible name and state (e.g., set aria-pressed based on
task.enabled and a dynamic aria-label like "Enable {task.title}" / "Disable
{task.title}" so screen readers know the action and current state), and update
the remove button (where removeSubconsciousTask is called) to include a
descriptive aria-label such as "Remove {task.title}" (or add visually-hidden
text tied to the button) so the icon-only control is announced; keep task.title
and task.enabled to build the dynamic labels.
In `@app/src/components/rewards/ReferralRewardsSection.tsx`:
- Around line 263-270: The input currently uppercases every keystroke
(setApplyCode(e.target.value.toUpperCase())), which mangles pasted URLs; instead
stop mutating input onChange (use setApplyCode(e.target.value)) so applyCode
preserves the raw value, and move normalization into handleApply: trim
applyCode, detect URLs (e.g. /^https?:\/\//i.test(trimmed>) and if it’s a URL
keep it verbatim, otherwise uppercase the code, then call
referralApi.claimReferral or claimReferral(value) with that normalized value;
update references to setApplyCode, applyCode, and handleApply accordingly.
In `@app/src/components/settings/panels/billing/BillingPaymentsTab.tsx`:
- Around line 71-77: The PayAsYouGoCard is passed onBalanceRefresh but the
component doesn't destructure or call it; update the PayAsYouGoCard component to
accept onBalanceRefresh in its props (add it to the props interface /
destructuring) and invoke it from the appropriate UI action (for example, wire
it to the refresh button's onClick or the balance refresh handler inside
PayAsYouGoCard), or alternatively remove the onBalanceRefresh prop from the
parent (BillingPaymentsTab) if no refresh UI is needed; reference PayAsYouGoCard
and the onBalanceRefresh prop when making the change.
In `@app/src/components/skills/skillIcons.tsx`:
- Around line 37-45: The span that renders the labeled icon badge (the element
using aria-label and className generated by iconClasses and rendering <Icon
aria-hidden="true" />) should include role="img" so assistive technologies treat
it as an image; update the span to add role="img" (keeping aria-label and
preserving Icon's aria-hidden) to provide correct semantics for the labeled icon
badge.
In `@src/api/config.rs`:
- Line 23: Update the doc comment for the API base URL fallback to reflect the
new env-aware behavior: state that the function now falls back to
default_api_base_url_for_env(app_env_from_env().as_deref()) (which selects a
DEFAULT_API_BASE_URL based on the current APP_ENV) rather than unconditionally
using DEFAULT_API_BASE_URL; mention the involved symbols
default_api_base_url_for_env and app_env_from_env so readers know the fallback
is environment-aware.
In `@src/openhuman/channels/providers/web.rs`:
- Around line 355-361: The current log::warn call in the web channel is emitting
the raw err_message which can leak sensitive upstream/provider payloads; replace
the raw err_message with a sanitized classification or redacted summary instead.
Locate the log::warn invocation (the lines with client_id, thread_id, request_id
and err_message) and change it to log a fixed classification string or the
result of a sanitizer helper (e.g., sanitize_error(err_message) or
map_err_to_category(err_message)) that strips tokens/PII and only returns a
non-sensitive category or short summary; ensure no full JWTs, API keys, or PII
are included in the logged value.
In `@src/openhuman/config/ops.rs`:
- Around line 42-44: The fallback to PathBuf::from(".openhuman") is not
environment-aware; change the unwrap_or_else closure used after
crate::openhuman::config::default_root_openhuman_dir() so it reads an env var
(e.g., OPENHUMAN_ENV) and builds a suffix (empty for production or "-<env>" for
staging/other) and returns PathBuf::from(format!(".openhuman{}", suffix)).
Replace both occurrences (the two places currently using .unwrap_or_else(|_|
PathBuf::from(".openhuman")).join("workspace")) so the fallback becomes
environment-scoped before calling .join("workspace").
In `@src/openhuman/config/schema/load.rs`:
- Around line 1274-1282: The test
default_root_dir_name_uses_staging_suffix_for_staging_env currently checks
is_staging_app_env(Some("staging")) against a literal instead of exercising
default_root_dir_name(); update the test to call the function
default_root_dir_name() (and optionally also call it for a non-staging env) and
assert that it returns ".openhuman-staging" when
crate::api::config::is_staging_app_env(Some("staging")) is true, and
".openhuman" when the env is non-staging, so the mapping implemented by
default_root_dir_name() is validated directly.
In `@src/openhuman/config/schema/types.rs`:
- Around line 201-206: The fallback for openhuman_dir in Config::default()
always uses ".openhuman" and thus loses any staging suffix; update the
unwrap_or_else closure so it constructs the fallback path then preserves/appends
the staging suffix used elsewhere (e.g., call the existing
crate::openhuman::config helper such as staging_suffix() or is_staging() to
determine the suffix) instead of hardcoding ".openhuman". Concretely, inside the
closure that builds home.join(".openhuman"), conditionally append the staging
suffix obtained from crate::openhuman::config (or call a provided
staging_suffix() function) so openhuman_dir honors the same staging/prod naming
as default_root_openhuman_dir().
---
Nitpick comments:
In @.env.example:
- Around line 10-16: The .env.example triggers dotenv-linter UnorderedKey
warnings for BACKEND_URL and VITE_BACKEND_URL; fix this by reordering the keys
so they follow the linter's expected ordering (alphabetical/grouped by prefix).
Specifically, move BACKEND_URL to sit alphabetically with other non-VITE keys
(after OPENHUMAN_APP_ENV) and place VITE_BACKEND_URL so it is ordered correctly
among VITE_* entries (after VITE_OPENHUMAN_APP_ENV) to eliminate the
UnorderedKey warnings for BACKEND_URL and VITE_BACKEND_URL.
In `@app/src/components/composio/toolkitMeta.tsx`:
- Around line 44-48: TOOLKIT_ALIASES is inconsistent: change the googlesheets
entry so the canonical form matches the other aliases (map "google_sheets" ->
"googlesheets" or vice versa). Recommended: update TOOLKIT_ALIASES to map
"google_sheets" to "googlesheets" (so all mappings go from underscored to
non-underscored canonical names) and then update KNOWN_COMPOSIO_TOOLKITS to use
"googlesheets" instead of "google_sheets" wherever it appears.
In `@app/src/components/intelligence/IntelligenceSubconsciousTab.tsx`:
- Around line 57-67: Add namespaced debug checkpoints "[subconscious-ui]" around
all side-effect handlers in this component: e.g., inside handleAddTask log entry
with the title before calling addSubconsciousTask, log success after
setNewTaskTitle('') and log failure in the catch; do the same pattern for other
side-effect functions in this file (e.g., handleRunTick / any "run tick"
handler, escalation handlers referenced by escalationId or esc.id, task toggle
handler like handleToggleTask that calls task toggle APIs, handleRemoveTask, and
any navigation branch functions) — each should emit
console.debug('[subconscious-ui] <action>', { relevantIds/state/branch }) at
entry, before/after external calls (addSubconsciousTask, manual run, escalation
approve/deny), and on error to aid tracing.
In `@app/src/components/PillTabBar.tsx`:
- Around line 27-45: This component is presenting a tab list but uses
aria-pressed; update PillTabBar to expose proper ARIA tab semantics: add
role="tablist" to the container (containerClassName), set each button to
role="tab" (in the items.map render), replace aria-pressed with
aria-selected={active} and manage keyboard focus via tabIndex={active ? 0 : -1};
also ensure each tab has a stable id (e.g., derived from item.value) and, if
panels exist, include aria-controls linking to the panel id so screen readers
can associate tabs with their panels; keep onChange(selected) behavior intact
and leave renderItem/selected/activeClassName/inactiveClassName usage as-is.
In `@app/src/components/rewards/RewardsCommunityTab.tsx`:
- Around line 68-80: Remove the unused props from the component API: delete
onSelectRedeem and plan from the RewardsCommunityTabProps interface and stop
expecting/destructuring them in the RewardsCommunityTab function; then update
the parent component (Rewards.tsx) to stop passing onSelectRedeem and plan into
<RewardsCommunityTab /> so the prop contract and usage stay consistent (refer to
RewardsCommunityTabProps, RewardsCommunityTab, onSelectRedeem, plan, and the
parent Rewards.tsx component to locate the changes).
In `@app/src/components/settings/panels/billing/BillingHistoryTab.tsx`:
- Around line 53-55: The USD amount in BillingHistoryTab is formatted with
toFixed(5), which is excessive for user-facing currency; update the display
logic that renders {isEarn ? '+' :
'-'}${Math.abs(transaction.amountUsd).toFixed(5)} to use toFixed(2) for standard
USD formatting (or, if sub-cent precision is intentionally required for
micro-transactions, retain the higher precision but add a clear comment near the
transaction.amountUsd formatting explaining the rationale and expected
units/precision so future readers understand why 5 decimals are used).
- Around line 44-50: The date formatting in BillingHistoryTab.tsx hardcodes the
'en-US' locale for new Date(transaction.createdAt).toLocaleDateString; update
this to use the user's default locale (pass undefined instead of 'en-US') or a
shared locale constant (e.g., LOCALE or getUserLocale()) so the display respects
i18n; adjust the expression around transaction.createdAt in the
BillingHistoryTab component accordingly.
In `@app/src/components/settings/panels/billing/PayAsYouGoCard.tsx`:
- Around line 5-11: Remove the unused onBalanceRefresh prop from the
PayAsYouGoCardProps interface and from the PayAsYouGoCard component’s props
list: locate the interface PayAsYouGoCardProps and delete the onBalanceRefresh
declaration, then update the PayAsYouGoCard component signature so it no longer
expects or destructures onBalanceRefresh; finally, update the parent
BillingPaymentsTab to stop passing onBalanceRefresh into PayAsYouGoCard so the
prop contract is consistent.
- Around line 29-39: customTopUpAmountValid is derived from customTopUpAmount
via a useEffect causing an extra render; remove the customTopUpAmountValid state
and the useEffect, compute validity inline (or with useMemo) from
customTopUpAmount (e.g., const customTopUpAmountValid =
Number(customTopUpAmount) > 0) and update handleCustomTopUp to reference that
derived value; delete setCustomTopUpAmountValid and the useEffect block to avoid
synchronous state updates.
In `@app/src/components/settings/panels/billing/SubscriptionPlans.tsx`:
- Line 202: The commented-out JSX line rendering plan.tagline inside the
SubscriptionPlans component is dead code and should be removed to reduce noise;
locate the commented snippet referencing plan.tagline (the line with {/*
{plan.tagline && <p className="mt-1 text-sm text-stone-500">{plan.tagline}</p>}
*/}) in SubscriptionPlans.tsx and delete it, or if the tagline may be needed
later, replace the comment with a clear TODO explaining why it’s disabled and
reference the plan.tagline conditional rendering for future restoration.
In `@app/src/lib/composio/hooks.ts`:
- Line 3: The composio hook is importing canonicalizeComposioToolkitSlug from a
UI component module; extract the slug canonicalization logic into a new non-UI
shared module (e.g., create a plain JS/TS file exporting
canonicalizeComposioToolkitSlug under the composio lib), move the implementation
from toolkitMeta (remove it from the UI component), and update both hooks.ts
(which currently imports canonicalizeComposioToolkitSlug) and the toolkitMeta
component to import the function from the new shared module so the hook no
longer depends on UI code.
In `@app/src/pages/__tests__/Rewards.test.tsx`:
- Line 73: The test currently asserts an exact duplicate loading-text count
using screen.getAllByText('Loading rewards…').toHaveLength(2'), which couples
the test to implementation details; replace that assertion with a
behavior-focused presence check such as expect(screen.getByText('Loading
rewards…')).toBeInTheDocument() (or expect(screen.queryByText('Loading
rewards…')).not.toBeNull()) so the test only verifies the loading state exists
rather than how many times it’s rendered.
In `@app/src/pages/Settings.tsx`:
- Line 276: Add a short inline comment in Settings.tsx next to the Route for
BillingPanel explaining why it is not wrapped by WrappedSettingsPage (e.g.,
"BillingPanel intentionally not wrapped because it uses its own wider max-w-2xl
layout"); update the Route line (<Route path="billing" element={<BillingPanel
/>} />) to include that comment so future maintainers know this is an
intentional exception rather than an oversight.
- Around line 217-225: The helper wrapSettingsPage defined inside the Settings
component is recreated on every render; move it outside the Settings function or
memoize it with React.useCallback to avoid allocating a new function each
render. Locate the wrapSettingsPage arrow function (which returns
<WrappedSettingsPage> with APP_VERSION) and either: 1) extract it as a top-level
function that accepts element: React.ReactNode, or 2) wrap its definition with
useCallback inside Settings (e.g., const wrapSettingsPage =
useCallback((element) => ..., [APP_VERSION])) so the function identity is
stable. Ensure the JSX still renders the Beta build v{APP_VERSION} and update
any references to wrapSettingsPage accordingly.
In `@app/src/services/api/creditsApi.ts`:
- Around line 153-160: The two helpers asNumber and normalizeUsd contain
identical logic; consolidate them into one function (pick a single canonical
name, e.g., normalizeUsd) and remove the duplicate (delete asNumber or
normalizeUsd accordingly), then replace all usages of the removed name with the
chosen helper (update callers that reference asNumber and callers that reference
normalizeUsd, including the code around the current function and the other
instance at lines referenced in the review). Ensure the chosen helper is
exported/visible where needed and keep the exact behavior (finite number check,
non-empty string parse, fallback 0).
- Around line 208-231: The normalizeCreditBalance function silently falls back
to zeros when expected fields are missing; update normalizeCreditBalance to
detect when none of the known balance keys are present on source (for
promotionBalanceUsd and teamTopupUsd) and emit a development debug log with the
raw source object and which keys were missing before returning normalized
values, using the project's debug/logger utility (e.g., logger.debug) and keep
calls around normalizeUsd so you can trace when fallback-to-default occurs;
reference normalizeCreditBalance, normalizeUsd and asRecord to locate where to
add the checks/logging.
In `@src/openhuman/agent/harness/definition_loader.rs`:
- Around line 126-128: The call to
crate::openhuman::config::default_root_openhuman_dir() currently silences errors
via .ok() — change this so any Err is logged before returning None: call
default_root_openhuman_dir(), match or map_err to capture the error, emit a
debug (or tracing::debug) log containing the error and context (e.g., "resolving
root openhuman dir failed"), then proceed to return None or map to
dir.join("agents") on Ok; reference the default_root_openhuman_dir() invocation
and the subsequent .join("agents") mapping when making the change.
In `@src/openhuman/channels/controllers/definitions.rs`:
- Around line 434-452: The test auth_mode_serializes_to_expected_wire_values
only checks serde_json::to_value; add reciprocal serde_json::from_value
assertions to ensure deserialization maps the wire strings back to the enum
variants. For each checked value ("api_key", "bot_token", "oauth", "managed_dm")
call serde_json::from_value::<ChannelAuthMode>(serde_json::Value::String(...))
and assert_eq! the result to ChannelAuthMode::ApiKey, ChannelAuthMode::BotToken,
ChannelAuthMode::OAuth, and ChannelAuthMode::ManagedDm respectively so both
directions are covered.
In `@src/openhuman/channels/providers/web.rs`:
- Around line 91-97: The current is_inference_budget_exceeded_error matcher is
too strict and misses variants like "top-up", "budget has been exceeded", or
"out of credits"; replace the simple substring checks with a broader,
case-insensitive pattern (or multiple patterns) that normalizes
whitespace/hyphens and matches common variants such as r"budget.*exceed",
r"top[- ]?up", r"add.*credits", r"out of credits", and r"no remaining credits";
update is_inference_budget_exceeded_error to trim and to_ascii_lowercase the
message, collapse repeated whitespace/hyphens, and test against these regexes
(prefer compiling them once as static/lazy to avoid reallocation) so budget
exhaustion messages are reliably detected.
In `@src/openhuman/encryption/core.rs`:
- Around line 107-108: Update the docs and any nearby comments to reflect the
env-aware root directory behavior introduced by the path resolution change:
replace or augment references to "~/.openhuman" with a note that
default_root_openhuman_dir() (in src/openhuman/encryption/core.rs calling
crate::openhuman::config::default_root_openhuman_dir()) may return
environment-specific names (e.g., staging suffixes) and describe how to override
via env if applicable; ensure any README, comments, or docstrings near
default_root_openhuman_dir and its callers mention the env-aware naming
convention.
🪄 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: 4aaa561f-7ada-4ad2-a7ff-ae253c1767ff
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (52)
.env.exampleapp/.env.exampleapp/package.jsonapp/src/components/PillTabBar.tsxapp/src/components/composio/ComposioConnectModal.tsxapp/src/components/composio/toolkitMeta.tsxapp/src/components/intelligence/IntelligenceDreamsTab.tsxapp/src/components/intelligence/IntelligenceMemoryTab.tsxapp/src/components/intelligence/IntelligenceSubconsciousTab.tsxapp/src/components/rewards/ReferralRewardsSection.tsxapp/src/components/rewards/RewardsCommunityTab.tsxapp/src/components/rewards/RewardsCouponSection.tsxapp/src/components/rewards/RewardsRedeemTab.tsxapp/src/components/rewards/RewardsReferralsTab.tsxapp/src/components/settings/SettingsHome.tsxapp/src/components/settings/components/PageBackButton.tsxapp/src/components/settings/hooks/useSettingsNavigation.tsapp/src/components/settings/panels/BillingPanel.tsxapp/src/components/settings/panels/billing/AutoRechargeSection.tsxapp/src/components/settings/panels/billing/BillingHistoryTab.tsxapp/src/components/settings/panels/billing/BillingPaymentsTab.tsxapp/src/components/settings/panels/billing/BillingPlansTab.tsxapp/src/components/settings/panels/billing/PayAsYouGoCard.tsxapp/src/components/settings/panels/billing/SubscriptionPlans.tsxapp/src/components/settings/panels/billingHelpers.tsapp/src/components/skills/SkillCategoryFilter.tsxapp/src/components/skills/skillCategories.tsapp/src/components/skills/skillIcons.tsxapp/src/hooks/useUsageState.test.tsapp/src/hooks/useUsageState.tsapp/src/lib/composio/hooks.tsapp/src/pages/Conversations.tsxapp/src/pages/Intelligence.tsxapp/src/pages/Rewards.tsxapp/src/pages/Settings.tsxapp/src/pages/Skills.tsxapp/src/pages/__tests__/Rewards.test.tsxapp/src/pages/__tests__/Skills.third-party-notion-debug-tools.test.tsxapp/src/services/api/__tests__/billingApi.test.tsapp/src/services/api/creditsApi.tsapp/src/utils/config.tsapp/src/vite-env.d.tssrc/api/config.rssrc/openhuman/agent/harness/definition_loader.rssrc/openhuman/channels/controllers/definitions.rssrc/openhuman/channels/providers/web.rssrc/openhuman/config/ops.rssrc/openhuman/config/schema/load.rssrc/openhuman/config/schema/types.rssrc/openhuman/encryption/core.rssrc/openhuman/memory/global.rssrc/openhuman/memory/store/client.rs
…nd consistency - Renamed placeholder text and button labels in the RewardsCouponSection test to reflect updated terminology. - Adjusted test assertions to ensure they align with the new button and placeholder names. - Updated pricing details in billingHelpers tests to reflect new monthly and annual rates for BASIC and PRO plans. - Enhanced the ContextGatheringStep labels for better clarity regarding email and LinkedIn processing stages.
- Reformatted the coupon code input changes in the RewardsCouponSection test for improved readability and consistency. - Ensured that the test cases maintain a uniform style for better maintainability.
… components - Added ARIA roles and attributes to the PillTabBar for better accessibility. - Refactored the canonicalization logic for toolkit slugs into a new file for improved organization. - Updated the IntelligenceDreamsTab and IntelligenceMemoryTab components for better readability and accessibility. - Enhanced error handling and logging in the IntelligenceSubconsciousTab for improved debugging. - Streamlined the ReferralRewardsSection and RewardsCommunityTab components by normalizing referral code input handling. - Removed unused props and improved layout consistency in BillingPanel and related components. - Updated the Skills page to handle subconscious escalation dismissals more effectively.
- Added steps to configure the staging app environment in the release workflow. - Set environment variables for staging, including OPENHUMAN_APP_ENV and VITE_OPENHUMAN_APP_ENV. - Updated build and deployment steps to conditionally use staging settings based on the build target. - Ensured proper handling of workspace paths for staging deployments.
- Introduced OPENHUMAN_APP_ENV variable to specify the app environment as 'staging'. - Updated .env.example to include new environment configuration for clarity.
…ting - Simplified the navigation logic in the IntelligenceSubconsciousTab for better readability. - Improved text formatting in the IntelligenceDreamsTab for enhanced clarity and consistency. - Refactored import statements in toolkitMeta.tsx for better organization.
Summary
Problem
Solution
Submission Checklist
app/src/hooks/useUsageState.test.tsandapp/src/services/api/__tests__/billingApi.test.tsImpact
Related
Summary by CodeRabbit
New Features
Improvements