- Add Sentry debug symbol upload step to the CI pipeline for production builds.
- Implement a helper script for manual symbol uploads with OS and architecture detection.
- Configure automatic Sentry release creation and commit association on main branch pushes.
- Refine Sentry CLI parameters to correctly handle shallow clones and debug ID indexing.
- Initialize CHANGELOG.md to track project changes and infrastructure updates.
- Update workflow permissions to allow Sentry to read action metadata for commit mapping.
Closes#627
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
## Summary
- Introduce an `EnvLookup` seam in `src/openhuman/config/schema/load.rs`.
- Route env-overlay reads through `EnvLookup`/`ProcessEnv` while preserving existing precedence and runtime behavior.
- Add focused parallel-safe unit coverage for config env overlay behavior without mutating process env.
## Problem
- `Config::apply_env_overrides` mixed environment access, override logic, and global side effects.
- That made precedence and parsing behavior harder to unit test in isolation and forced reliance on process-env mutation.
## Solution
- Add `pub(crate) trait EnvLookup` with `get`, `contains`, and `get_any`, plus a production `ProcessEnv` implementation.
- Delegate overlay logic through `apply_env_overlay_with(&ProcessEnv)` and keep side effects in the small wrapper.
- Add focused tests covering precedence, parsing, defaults, and legacy-migration interactions with no global env mutation.
## Submission Checklist
- [x] **Unit tests** — Targeted Rust config tests added and broader related module suites passed
- [ ] **E2E / integration** — Not applicable; this refactor preserves existing behavior and improves unit-level testability
- [x] **N/A** — E2E is not applicable because no user-visible or cross-process flow changed
- [ ] **Doc comments** — Not applicable; internal refactor in existing module only
- [ ] **Inline comments** — Not applicable; logic remains local and test coverage documents behavior
## Impact
- No intended runtime behavior change; refactor keeps config precedence intact.
- Improves maintainability and makes future config/workspace resolution seams easier to test safely.
## Related
- Issue(s):
- Follow-up PR(s)/TODOs: thread `EnvLookup` through runtime config directory resolution helpers
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
* feat(webview_accounts): native OS notifications from embedded webviews (#714)
Forward CEF notification intercept payloads to tauri-plugin-notification,
prefixing the title with the provider label so the source of each toast is
obvious at a glance. Honour `silent` (skip toast, still record route),
`icon` (passed through to the native builder), and `tag` (used as the
dedup key, with a monotonic timestamp fallback for untagged payloads).
Record a NotificationRoute keyed by `{provider}:{account_id}:{tag_or_uuid}`
so a future click hook (UNUserNotificationCenter / notify-rust on_response)
can route the OS click back to the source account. Entries are cleared on
webview_account_close / _purge to bound map growth.
Expose webview_notification_permission_state / _request commands mapping
tauri::plugin::PermissionState onto the web API triple. Non-cef stubs
return "default" so the frontend can call the same invoke names on both
runtimes. Wire notification:allow-* capabilities so the plugin can be
invoked from the webview.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(accounts): wire notification permission + click bridge (#714)
Round-trip the OS notification permission once per session on first
account open via the new invoke pair. Attach a dormant notification:click
listener that dispatches setActiveAccount and brings the main window to
front when a platform click hook starts emitting the event — contract
matches the Rust NotificationRoute shape so the emit side is a one-liner.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* chore: sync Cargo.lock to 0.52.26 after version bump
Lockfile picked up the pending 0.52.26 version bump from Cargo.toml
while building the notification feature. No dependency graph change.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat(notifications): add notification bypass for embedded webview apps (#679)
- Add NotificationBypassPrefs (global DND, per-account mute, bypass-when-focused)
to WebviewAccountsState with thread-safe AtomicBool window focus tracking
- Evaluate all three bypass conditions inside forward_native_notification before
showing OS toast; each suppression path logs at debug with [notify-bypass] prefix
- Add four new Tauri commands: webview_notification_set_dnd,
webview_notification_mute_account, webview_notification_get_bypass_prefs,
webview_set_focused_account
- Wire window focus tracking in setup hook via on_window_event Focused handler
- Frontend: add setAccountMuted, setGlobalDnd, getBypassPrefs, setFocusedAccount
helpers in webviewAccountService; sync focused account on open + click
- Add NotificationsPanel settings page with Global DND toggle
- Register NotificationsPanel at /settings/notifications
Closes#679
* feat(notifications): integrate notifications feature into app
- Added Notifications page and routing to AppRoutes.
- Introduced NotificationRoutingPanel in Settings for managing notification settings.
- Updated SettingsHome to include navigation for notification routing.
- Integrated notifications reducer into the store for state management.
- Enhanced Rust backend to support notification handling from embedded webviews.
This commit lays the groundwork for a comprehensive notification system within the application.
* refactor(notifications): clean up code formatting and structure
- Simplified JSX structure in NotificationCard for better readability.
- Consolidated fetchNotifications call in NotificationCenter for cleaner syntax.
- Improved formatting in NotificationRoutingPanel and notificationsSlice for consistency.
- Enhanced Rust code readability by streamlining function signatures and logic.
These changes enhance code maintainability and readability across the notifications feature.
* refactor(webview_accounts): simplify webview_notification_set_dnd function signature
- Removed unnecessary line breaks in the webview_notification_set_dnd function for improved readability.
* feat(notifications): implement provider-level notification settings management
- Added `getNotificationSettings` and `setNotificationSettings` functions to manage notification settings for providers.
- Enhanced `NotificationRoutingPanel` to display and update settings for Gmail, Slack, Discord, and WhatsApp.
- Introduced new RPC endpoints for retrieving and updating notification settings.
- Updated database schema to store notification settings persistently.
This commit establishes a robust system for managing notification preferences, improving user control over notifications.
* refactor(notifications): improve code formatting and readability
- Enhanced formatting in NotificationRoutingPanel for better clarity.
- Streamlined function signatures in notificationService and Rust backend.
- Improved readability of assertions in tests by adjusting line breaks.
These changes contribute to a more maintainable and comprehensible codebase for the notifications feature.
* chore(vendor): bump tauri-cef to fix Slack notification permission banner
Updates the tauri-cef submodule to 55db2d6 which adds a
navigator.permissions.query shim in the CEF render process. Slack checks
this API (not just Notification.permission) to decide whether to show its
"needs your permission" banner — the shim returns "granted" for
notifications queries so the banner no longer appears.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(vendor): bump tauri-cef for cargo fmt fixes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* chore(vendor): bump tauri-cef — native V8 permissions.query shim
Switches from context.eval() to a proper PermissionsQueryV8Handler so
the navigator.permissions.query fix actually runs in on_context_created.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(notifications): patch navigator.permissions.query in ua_spoof.js
The V8 set_value_bykey approach in cef-helper's on_context_created does
not stick on CEF platform objects (Chromium's V8 binding layer silently
ignores property writes on native wrappers like Permissions). The init
script path via frame.execute_java_script runs in the fully-initialised
JS context where navigator.permissions IS writable, matching how
ua_spoof.js already overrides navigator.userAgent successfully.
Slack checks navigator.permissions.query({ name: 'notifications' })
before showing its "needs permission" banner — patching it here to
return "granted" removes the banner.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(notifications): use Object.defineProperty to shim navigator.permissions
Two-layer fix for the Slack "needs permission to enable notifications" banner:
1. cef-helper (submodule update to 99a2686): context.eval() in
on_context_created installs Object.defineProperty(navigator, 'permissions',
...) before any page JS runs.
2. ua_spoof.js: same Object.defineProperty pattern as belt-and-suspenders
for frames that reload or trigger permission checks after on_load_end.
Simple property assignment on Blink platform objects is silently ignored;
Object.defineProperty on the navigator wrapper itself (the same mechanism
already used for navigator.userAgent) works correctly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(notifications): address CodeRabbit review issues on PR #727
- Move raw_title out of log::info! (PII risk) — log title_chars at info,
raw_title at debug only
- Fix permissionChecked set before async invoke in ensureNotificationPermission
so transient failures allow retry on next account open
* fix(cef): enable webview-data-url feature for CEF placeholder URL
The CEF backend uses a data: URL as the initial webview location so CDP
can attach before the real provider URL loads. Tauri's add_child rejects
data: URLs unless the webview-data-url feature is enabled.
* fix(notifications): address remaining CodeRabbit issues on PR #727
- cdp/emulation: bump Chrome UA 124→136 to pass Slack browser check
- cdp/session: inject Page.addScriptToEvaluateOnNewDocument to stub
Notification.permission as "granted" and silence provider banners
- notifications/mod.rs + core/all.rs: wire notifications domain into
the controller registry (fixes unknown-method in json_rpc_e2e tests)
- notifications/schemas: add skipped bool output to ingest schema
- notifications/store: add tracing::warn on datetime parse failure
- notificationService: union return type for ingestNotification
- webviewAccountService: narrow union before accessing result.id
- NotificationCenter: drive loading/error from fetch effect; track
allProviders separately so filter pills don't collapse on selection
- NotificationRoutingPanel: rollback optimistic update on save failure
- useSettingsNavigation: add notifications/notification-routing routes
- scripts/install.sh: remove silent dry-run exit 0 on asset failure
- scripts/setup-dev-codesign.sh: remove unconditional -legacy flag
- docs/SUMMARY.md: remove worktree path, fix macOS capitalisation,
remove self-referential deletion note
* chore: apply prettier + cargo fmt + fix useEffect dep warning
Auto-apply formatting changes flagged by the pre-push hook:
- prettier reformatted NotificationCenter.tsx and notificationService.ts
- cargo fmt reformatted all.rs, openhuman/mod.rs, notifications/schemas.rs
- NotificationRoutingPanel: move providers array to module scope so
useEffect dependency array is satisfied without exhaustive-deps warning
* feat(notifications): enhance notification management and permissions
- Added new commands for managing notification preferences, including setting global Do Not Disturb (DND), muting specific accounts, and retrieving current bypass preferences.
- Implemented a notification permission state handler to ensure consistent behavior across different environments.
- Updated the JavaScript shim for notification permissions to handle both Notification and PushManager states, ensuring compatibility with various providers.
- Refactored the WebviewAccountsState to include a new structure for managing notification bypass preferences, improving the overall notification handling logic.
* update agents
* fix(notifications): complete schema + navigation metadata for ingest/settings routes
- app/src/components/settings/hooks/useSettingsNavigation.ts: resolve the
new `/settings/notifications` and `/settings/notification-routing` URLs
to their SettingsRoute values and feed them into breadcrumbs so the new
panels don't silently fall through to `'home'`. Addresses CodeRabbit on
useSettingsNavigation.ts:34.
- src/openhuman/notifications/schemas.rs: add the optional `reason` output
on `notification.ingest` (populated alongside `skipped=true` by the
runtime) and the normalized `settings` output on `notification.settings_set`
so schema-driven clients see the full response shape. Addresses
CodeRabbit on schemas.rs:103 and schemas.rs:217.
- src/core/all.rs: add a `notification` namespace_description so CLI help
covers the new controllers, plus a test assertion. Addresses CodeRabbit
on src/core/all.rs:149.
* fix(notifications): trace DB entry, surface empty update matches, warn on bad scored_at
- Add `tracing::trace!` checkpoints around the `with_connection` DB open
and schema migration so notification-delivery issues are reconstructible
from logs.
- `update_triage` and `mark_read` now inspect `Connection::execute`'s
affected-row count: log a `warn!` when the update matched zero rows
(row deleted between ingest and scoring / client passed a stale id),
`debug!` on the normal path.
- `scored_at` parsing no longer silently drops malformed values — log a
`warn!` with the raw value and parse error before treating the row as
unscored, matching the existing behavior for `received_at`.
Addresses CodeRabbit on store.rs (lines 72, 172, 294).
* fix(webview): respect silent notifications, multi-host CDP fallback, shim idempotency
- webview_accounts/mod.rs: honor the Web Notification `silent` flag.
Previously we only logged it and still called `builder.show()`, so
pages that marked a notification silent still produced an OS toast.
Mirror event still fires so the in-app center updates; only the OS
toast is suppressed. Also picks up a prior cargo-fmt rewrap.
- cdp/target.rs: `browser_ws_url()` now continues the host loop when
`resp.json()` fails instead of early-returning via `?`. A malformed
response from the first host (CDP_HOST) no longer prevents the
`localhost` fallback from being tried.
- webview_accounts/ua_spoof.js: guard the Notification wrapper behind
`window.__OH_NOTIF_SHIM` so repeated evaluations of the script
(Page.addScriptToEvaluateOnNewDocument + frame-level re-injections)
don't stack wrappers onto the same page globals or re-proxy
`Function.prototype.toString`.
Addresses CodeRabbit on webview_accounts/mod.rs:377, cdp/target.rs:33,
and ua_spoof.js:176.
* update agents
* update
---------
Co-authored-by: oxoxDev <nikhil@tinyhumans.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
* chore(vendor): bump tauri-cef to feat/cef tip (073047817)
* feat(composio): inject connected identities into agent prompts
Standardize provider identity persistence with a shared identity_set hook and skill-scoped facet keys so connected account identity survives restarts and is merged into inference context. Add connected identity rendering in welcome, orchestrator, and integrations_agent prompts so agents can reference cross-platform user identity during conversations.
Closes#691
Made-with: Cursor
* fix(composio): resolve CodeRabbit identity prompt and cleanup findings
Make connected-identity prompt injection deterministic via PromptContext, sanitize provider identity fields before prompt rendering, and clear persisted identity facets when Composio connections are removed to avoid stale context.
Made-with: Cursor
* fix(ci): satisfy format checks and dry-run installer smoke
Apply rustfmt to touched Rust files and make install.sh dry-run exit successfully when no compatible release asset exists, so smoke validation remains informative without failing on missing artifacts.
Made-with: Cursor
* feat(routing,local-ai): pluggable local inference + openhuman:// deep-link
Salvaged from recovery commit cfb1fd9f (Apr 19) — originally auto-backed up
by claude-mem recovery tool, not yet PR'd.
## Local AI routing becomes pluggable (factory.rs, health.rs)
Previously the IntelligentRoutingProvider hard-wired Ollama as the only local
inference backend. This commit lets operators point at any OpenAI-compatible
local server (llama.cpp / llama-server, LM Studio, MLX's mlx_lm.server /
mlx-omni-server, vLLM — anything exposing /v1/chat/completions + /v1/models).
Config surface:
- `OPENHUMAN_LOCAL_INFERENCE_URL` env var — full /v1 base URL. When set,
health is probed via GET {base}/models (OpenAI-compat) instead of Ollama's
/api/tags.
- `LocalAiConfig.provider` accepts "llamacpp" or "llama-server" as aliases
that default to http://127.0.0.1:8080/v1.
- Default (no env, no override) stays Ollama — zero config change for
existing users.
Motivation: Ollama's embedded llama.cpp cannot yet load Gemma 4 E2B and
other recent models; MLX is significantly faster on Apple Silicon.
Operators should be able to pick the backend; end users see nothing.
## Deep-link scheme (Info.plist)
Adds CFBundleURLTypes entry registering `openhuman://` for the macOS bundle
so browser-issued deep links (e.g. openhuman://auth?token=...) launch the
installed app. See .claude/rules/14-deep-link-platform-guide.md for the
flow — requires a .app bundle (not `tauri dev`).
## OpenSSL 3.x PKCS12 fix (setup-dev-codesign.sh)
Adds `-legacy` to the `openssl pkcs12 -export` call. Required on OpenSSL 3.x
because the modern SHA-256 MAC / AES-256-CBC defaults are not yet supported
by macOS `security` tool, which silently fails to import the cert.
Notes:
- Dropped the CLAUDE.md.new noise and the mic-description copy downgrade
from the original recovery commit; kept only the load-bearing changes.
- Cargo.lock files reset to upstream/main to avoid dependency drift — the
new code does not introduce new deps.
Co-Authored-By: WOZCODE <contact@withwoz.com>
* fix(routing,codesign): address CodeRabbit review on PR #750
- setup-dev-codesign.sh: probe for openssl `-legacy` support before
using it so older OpenSSL/LibreSSL installs don't silently fail;
drop the stderr suppression on pkcs12 so import errors surface.
- routing/factory.rs: trim provider before alias matching, lower the
local-inference diagnostic to `debug` and drop raw URLs from the
log fields, and honor `OPENHUMAN_OLLAMA_BASE_URL` via the existing
`ollama_base_url()` helper in the default Ollama branch.
- local_ai/mod.rs: re-export `ollama_base_url` for the routing factory.
* style(app/src-tauri): apply rustfmt to merged main
`cargo fmt --check` in the merge workspace flagged two hunks inherited
from upstream main:
- app/src-tauri/src/lib.rs: move `mod notification_settings;` between
the cfg(cef) imessage_scanner and slack_scanner declarations so the
ordering matches rustfmt's reordering output.
- app/src-tauri/src/webview_accounts/mod.rs: wrap the long
`try_state::<NotificationSettingsState>()` binding.
No behavior change.
* format
---------
Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Co-authored-by: WOZCODE <contact@withwoz.com>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
* fix(agent): orchestrator never sends users to external dashboards for connect
Agent was improvising guidance like 'open your Composio dashboard at
app.composio.dev' when users asked to connect a service, which is broken
UX for non-technical users. Add an explicit rule: route connect requests
to the in-app Settings → Connections path, never paste external URLs
or explain OAuth/Composio internals.
* plan: imessage live-tick harness
Plan doc for extracting run_single_tick from ScannerRegistry's
AppHandle-coupled loop, enabling unit tests with fake deps against
real chat.db and an ignored live-sidecar integration test.
Scaffolding only — code changes follow in subsequent commits.
Co-Authored-By: WOZCODE <contact@withwoz.com>
* feat(imessage): testable tick harness + fix scanner never ingested
Extract pure `run_single_tick` + `TickDeps` trait from `run_scanner`. Prod
path wraps in `HttpDeps`; tests use `FakeDeps` with real chat.db so the
scanner body runs without a Tauri AppHandle. Template for five more
Apple-native sources per the roadmap.
While running it end-to-end, two pre-existing bugs in the shipped scanner
surfaced — both had to be fixed for a tick to ever succeed:
1. `ScannerRegistry::ensure_scanner` called `tokio::spawn` from the Tauri
`setup` hook, which runs before a Tokio reactor is active. Scanner
task never spawned; main thread panicked with "no reactor running".
Fix: `tauri::async_runtime::spawn`, which uses Tauri's own runtime.
2. `fetch_imessage_gate` used JSON pointer `/result/config/...` but the
JSON-RPC `RpcOutcome` envelope nests one level deeper:
`/result/result/config/...`. Gate always resolved to None, so every
tick silently skipped even with iMessage connected. Fix: correct
the pointer.
Verification: after both fixes, iMessage connected via
`openhuman.channels_connect` with `allowed_contacts=["*"]`, scanner
ingested 252 chat-day groups into `memory_docs` namespace
`imessage_default` — first successful ingest in the project's history.
Tests: 10 unit (9 existing + `skips_when_gate_disconnected`), 4 ignored
live-chat.db (2 existing + `run_single_tick_ingests_groups_from_real_chatdb`
+ `run_single_tick_keeps_cursor_on_group_failure`). All green.
Infra: `scripts/worktree-bootstrap.sh` — one-shot to init submodules,
symlink `.env`, build+stage sidecar, ensure tauri-cli. Worktrees didn't
inherit any of this, costing ~30 min of manual setup per branch.
Docs: `docs/superpowers/learnings/2026-04-21-imessage-harness-session.md`
captures what broke, why, and the debuggability ladder that would have
caught both bugs at merge time (launch-smoke CI, minimum).
Co-Authored-By: WOZCODE <contact@withwoz.com>
* chore(bootstrap): yarn install so husky hooks don't block push
Pre-push hook needs prettier; worktree had no node_modules. Friction
discovered pushing this branch.
Co-Authored-By: WOZCODE <contact@withwoz.com>
---------
Co-authored-by: Jwalin Shah <jshah1331@gmail.com>
Co-authored-by: WOZCODE <contact@withwoz.com>
Staged openhuman-core sidecar was compiled without these optional
features, so the shipped binary embedded the feature-gated stubs and
failed at runtime with "requires 'whatsapp-web' feature". Wire both
features into the cargo build invocation so the real WA-web and Matrix
listeners make it into the binary.
Co-authored-by: Jwalin Shah <jshah1331@gmail.com>