f7dfa94e5d fix(observability): Wave 4 classifier — socket transport + custom-provider config-rejection (~366 events, 13 IDs)
## Summary

- Extends `expected_error_kind` matcher ladder with 11 new substring arms across two existing buckets — no new variants, no new emit sites, no behavior change beyond reclassifying known wire shapes that were leaking past Wave 1-3 anchors.
- Closes 13 Sentry IDs (~366 events) — **all deterministic user-environment / user-config errors with no remediation path** (offline, DNS fail, captive portal, bad API key, out of credits, wrong model id, missing OAuth scope, region block).
- Pure classifier — no UI, retry, or fallback logic touched. The reliable-provider stack still falls back to OpenHuman's hosted tier for the Lane O bodies; users keep their chat. Lane N socket failures already gated through `report_error_or_expected` at `ws_loop.rs:191`, so the threshold-escalation event simply demotes to a `warn` breadcrumb instead of paging.
- Two micro-commits — Lane O (`is_provider_config_rejection_message` PHRASES const) and Lane N (`is_network_unreachable_message` substring arms) — each independently revertible.

## Problem

After the Wave 1-3 sweep landed, fresh Sentry triage surfaced **13 unresolved IDs** whose bodies match the *spirit* of an existing classifier bucket but use wire-shape variants the current substring anchors miss:

### Lane O — custom-provider config rejection (~250 events)

The `ProviderConfigRejection` variant + `is_provider_config_rejection_message` exist precisely for "user pointed OpenHuman at a custom_openai endpoint with a model / temperature / region / credential that provider doesn't accept." But the 7 phrases shipped in Wave 1-3 (#2079 / #2076 / #2202) only cover the DeepSeek / OpenRouter abstract-tier-leak and Moonshot temperature shapes. New surfaces:

| Sentry ID | Events | Real wire body |
|---|---|---|
| `R1` | 44 | `{"error":{"message":"This model is not available in your region.","code":403}}` |
| `R4` | 14 | `{"code":403,"reason":"ModelNotAllowed","message":"模型不允许访问"…}` (Doubao/ChatGLM) |
| `YC` | 16 | `{"error":{"type":"invalid_authentication_error"…}}` |
| `S5` | 14 | `{"error":{"message":"This request requires more credits, or fewer max_tokens…"}}` (OpenRouter 402) |
| `Y0` | 13 | `{'error': '/chat/completions: Invalid model name passed in model=reasoning-v1…'}` |
| `JN` | 14 | `{"error":{"message":"No active credentials for provider: openai"…}}` |
| `KB` | 16 | Same `No active credentials for provider` shape from OpenHuman backend re-emit |
| `JK` | 17 | `litellm.BadRequestError: Github_copilotException - Bad Request…` |
| `J2` / `J5` / `J4` | 62 | `{"error":{"message":"model 'llama3.3' not found","type":"not_found_error"…}}` |

### Lane N — socket WebSocket-connect transport (~116 events)

`is_network_unreachable_message` already catches `connection refused` / `dns error` / `network is unreachable` but two real shapes escape:

| Sentry ID | Events | Real wire body |
|---|---|---|
| `44` | 50 | `[socket] Connection failed: WebSocket connect: IO error: failed to lookup address information: nodename nor servname provided, or not known` |
| `4P` | 66 | `[socket] Connection failed: WebSocket connect: HTTP error: 200 OK` (captive portal / corporate proxy intercepting the WS upgrade handshake) |

Every one of these is deterministic **user-environment** / **user-configuration** state — the maintainers have no remediation. Sentry has no signal to act on. Every event was pure noise.

## Solution

### Lane O — `src/openhuman/inference/provider/config_rejection.rs:55-79`

Append 8 new phrases to the `PHRASES` const (case-insensitive substring match, same precedence as existing 7):

```rust
"not available in your region",        // R1
"modelnotallowed",                     // R4
"invalid_authentication_error",        // YC
"requires more credits",               // S5
"invalid model name passed in model=", // Y0
"no active credentials for provider",  // JN + KB
"litellm.badrequesterror",             // JK
"not_found_error",                     // J2 + J5 + J4
```

Each anchor is intentionally narrow (e.g. `passed in model=` not bare `invalid model name`; `litellm.badrequesterror` not bare `litellm`) so a stray log line elsewhere can't accidentally demote a real provider/backend bug. The HTTP-layer wrapper (`is_provider_config_rejection_http`) still guards on `provider != openhuman_backend::PROVIDER_LABEL`, so a model rejection from **our own** backend (which would be a real regression) still reaches Sentry.

### Lane N — `src/core/observability.rs:299-319`

Three new substring arms appended to `is_network_unreachable_message`:

- `failed to lookup address` + `nodename nor servname` — libc `getaddrinfo()` failure renderings on macOS / BSD / POSIX resolvers when tungstenite wraps as `IO error` without the reqwest `dns error` prefix.
- `http error: 200 ok` — tungstenite-only render. Reqwest renders HTTP 200 as `"HTTP status server error (200)"`, so no collision with the regular HTTP call path. A negative precedence test (`http_200_classifier_does_not_silence_unrelated_log_lines`) pins this against benign `HTTP/1.1 200 OK` / `status: 200 OK` prose so a future broadening cannot silence success traces.

### Design trade-off — classifier vs. root-fix validation layer

The Lane O bugs *do* have a more durable root fix: a pre-save provider validation layer (test the API key, validate the model id against `/v1/models`, surface region-blocks at config-save time). That's a real product initiative requiring UX design, per-provider model-list infrastructure, and meaningful spec work — out of scope for a triage sweep. This PR follows the Wave 1-3 maintainer precedent (classifier-first noise suppression) so the Sentry signal/noise ratio improves immediately; the validation layer remains tracked as a separate follow-up. If/when it lands, these classifier arms become redundant belt-and-suspenders and can be deleted without conflict.

Lane N has no root-fix alternative — offline / firewall / captive-portal / DNS failures are pure user-environment state. Classifier-demote is the correct disposition.

## Submission Checklist

> If a section does not apply to this change, mark the item as `N/A` with a one-line reason. Do not delete items.

- [x] Tests added or updated (happy path + at least one failure / edge case) per [Testing Strategy](../gitbooks/developing/testing-strategy.md#failure-path-requirement)
- [x] **Diff coverage ≥ 80%** — changed lines (Vitest + cargo-llvm-cov merged via `diff-cover`) meet the gate enforced by [`.github/workflows/coverage.yml`](../.github/workflows/coverage.yml). 11 positive tests pinned to real Sentry bodies + 1 negative precedence test cover every new substring arm; only the rustdoc comments and the negative `unrelated_*` test body are non-executable lines.
- [x] N/A: behaviour-only change — Coverage matrix updated — added/removed/renamed feature rows in [`docs/TEST-COVERAGE-MATRIX.md`](../docs/TEST-COVERAGE-MATRIX.md) reflect this change
- [x] All affected feature IDs from the matrix are listed in the PR description under `## Related`
- [x] No new external network dependencies introduced (mock backend used per [Testing Strategy](../gitbooks/developing/testing-strategy.md#mock-policy))
- [x] N/A: classifier-only change with no UI / release-cut surface touched — Manual smoke checklist updated if this touches release-cut surfaces ([`docs/RELEASE-MANUAL-SMOKE.md`](../docs/RELEASE-MANUAL-SMOKE.md))
- [x] Linked issue closed via `Closes #NNN` in the `## Related` section

## Impact

- **Runtime**: no behavior change for users — chat / agent / web-channel / socket paths continue to fall back / retry exactly as before. The only delta is that `report_error_or_expected` now classifies these 13 wire shapes as `ExpectedErrorKind::*` instead of escalating as `tracing::error`, so Sentry stops receiving the events.
- **Performance**: negligible — `is_provider_config_rejection_message` lowercases once and runs 15 substring scans (was 7); `is_network_unreachable_message` runs 11 substring scans (was 8). Both already on the error-path, never on the hot path.
- **Security**: no new attack surface. Substring matchers run on already-rendered error messages — no parsing, no allocation beyond the existing `to_ascii_lowercase()`.
- **Migration**: none.
- **Compatibility**: forward-compatible. New phrases are append-only — no existing matcher behavior changes. If a future maintainer ships the root-fix validation layer, deleting these arms is a clean local edit.

## Related

- Closes OPENHUMAN-TAURI-R1
- Closes OPENHUMAN-TAURI-R4
- Closes OPENHUMAN-TAURI-YC
- Closes OPENHUMAN-TAURI-S5
- Closes OPENHUMAN-TAURI-Y0
- Closes OPENHUMAN-TAURI-JN
- Closes OPENHUMAN-TAURI-KB
- Closes OPENHUMAN-TAURI-JK
- Closes OPENHUMAN-TAURI-J2
- Closes OPENHUMAN-TAURI-J5
- Closes OPENHUMAN-TAURI-J4
- Closes OPENHUMAN-TAURI-44
- Closes OPENHUMAN-TAURI-4P
- Follow-up PR(s)/TODOs: pre-save provider-validation layer (validate API key + model id + region at config-save time) tracked as separate product initiative; would supersede the Lane O arms.

---

## AI Authored PR Metadata (required for Codex/Linear PRs)

> Keep this section for AI-authored PRs. For human-only PRs, mark each field `N/A`.

### Linear Issue
- Key: N/A (Sentry-driven triage; no Linear ticket)
- URL: N/A

### Commit & Branch
- Branch: fix/sentry-wave4-classifier-no
- Commit SHA: e16414ac (tip)

### Agent
- Claude Code (Sonnet 4.6) running locally

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

* **Bug Fixes**
  * Enhanced detection of network connectivity failures and provider configuration rejection scenarios for more accurate error classification.

* **Tests**
  * Added comprehensive test coverage for network-related transport failures and provider configuration rejection cases.

<!-- review_stack_entry_start -->

[![Review Change Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/tinyhumansai/openhuman/pull/2309?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: oxoxDev <nikhil@tinyhumans.ai>
Co-authored-by: Steven Enamakel <enamakel@tinyhumans.ai>
2026-05-20 16:54:07 -07:00
2026-05-20 16:44:35 -07:00
2026-05-20 16:52:19 -07:00
2026-02-20 13:03:15 +04:00
2026-02-20 13:03:15 +04:00

OpenHuman

The Tet

tinyhumansai%2Fopenhuman | Trendshift   OpenHuman - An open source AI harness built with the human in mind | Product Hunt

OpenHuman is your Personal AI super intelligence. Private, Simple and extremely powerful.

DiscordRedditX/TwitterDocsFollow @senamakel (Creator)

🇺🇸 English | 🇨🇳 简体中文 | 🇯🇵 日本語 | 🇰🇷 한국어 | 🇩🇪 Deutsch

Early Beta Latest Release GitHub Stars License 简体中文 日本語 한국어 Deutsch

Early Beta: Under active development. Expect rough edges.

To install or get started, either download from the website over at tinyhumans.ai/openhuman or run

# Download DMG, EXEs over at https://tinyhumans.ai/openhuman or run in from your terminal

# For macOS or Linux x64
curl -fsSL https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.sh | bash

# For Windows
irm https://raw.githubusercontent.com/tinyhumansai/openhuman/main/scripts/install.ps1 | iex

What is OpenHuman?

OpenHuman is an open-source agentic assistant designed to integrate with you in your daily life. Each bullet links to the deeper writeup in the docs.

  • Simple, UI-first & Human A clean desktop experience and short onboarding paths take you from install to a working agent in a few clicks — no config-first setup, no terminal required. The agent has a face: a desktop mascot that speaks, reacts to its surroundings, joins your Google Meets as a real participant, remembers you across weeks, and keeps thinking in the background even when you've stopped typing.

  • 118+ third-party integrations with auto-fetch: plug into Gmail, Notion, GitHub, Slack, Stripe, Calendar, Drive, Linear, Jira and the rest of your stack with one-click OAuth. Every connection is exposed to the agent as a typed tool, and every twenty minutes the core walks each active connection and pulls fresh data into the memory tree. No prompts, no polling loops you have to write, so the agent already has tomorrow's context this morning.

    Managed integrations are backend-proxied through OpenHuman's Composio connector layer. If you want to run Composio directly instead of using the managed backend path, configure direct mode with your own Composio API key; real-time trigger webhooks then need to be hosted and wired by you.

  • Memory Tree + Obsidian Wiki: a local-first knowledge base built from your data and your activity. Everything you connect is canonicalized into ≤3k-token Markdown chunks, scored, and folded into hierarchical summary trees stored in SQLite on your machine. The same chunks land as .md files in an Obsidian-compatible vault you can open, browse and edit, inspired by Karpathy's obsidian-wiki workflow.

  • Batteries included: web search, a web-fetch scraper, a full coder toolset (filesystem, git, lint, test, grep), and native voice (STT in, ElevenLabs TTS out, mascot lip-sync, live Google Meet agent) are wired in by default. Model routing sends each task to the right LLM (reasoning, fast, or vision) under one subscription. No "install a plugin to read files" friction. Optional local AI via Ollama for on-device workloads.

  • Smart token compression (TokenJuice): every tool call, scrape result, email body, and search payload is run through a token compression layer before it touches any LLM Model. HTML is converted to Markdown, long URLs are shortened, and verbose tool output is deduped and summarized via a configurable rule overlay etc... CJK, emoji, and other multi-byte text are preserved grapheme-by-grapheme — never stripped. You get the same information but at a fraction of the tokens. Reducing cost & latency by up to 80%.

  • Messaging channels and privacy & security: inbound/outbound across the channels you already use, with workflow data that stays on device, encrypted locally, treated as yours.

Contributing from source

New contributor? Start with CONTRIBUTING.md for the fork/PR workflow and local validation commands, or use the copy-paste AI-agent prompt in CONTRIBUTING-BEGINNERS.md. The short path is:

  1. Install Git, Node.js 24+, pnpm 10.10.0, Rust 1.93.0 (rustfmt + clippy), CMake, Ninja, ripgrep, and the platform desktop build prerequisites.
  2. Fork and clone the repo, then run git submodule update --init --recursive before pnpm install so the vendored Tauri/CEF sources are present.
  3. Use pnpm dev for web-only UI work, pnpm --filter openhuman-app dev:app for the desktop shell, and focused checks such as pnpm typecheck, pnpm format:check, and cargo check -p openhuman --lib before opening a PR.

Deeper docs: Architecture · Getting Set Up · Cloud Deploy.

Context in minutes, not weeks

OpenHuman is the first agent harness that gets to know you in minutes. Inspired by Karpathy's LLM Knowledgebase. Most agents start cold. Hermes learns by watching you work; OpenClaw waits for plugins to ferry context in. Either way, you spend days or weeks before the agent knows enough about your stack to be genuinely useful.

OpenHuman context-building diagram

OpenHuman summarizes and compresses all your documents, emails & chats; and creates a memory graph that lets your agent remember everything about you.

OpenHuman skips the wait. Connect your accounts, let auto-fetch pull data locally on a 20-minute loop, and then have Memory Trees compress everything into Markdown files stored intelligently in a Karpathy-style Obsidian wiki.

In just one sync pass, the agent has full (compressed) context of your inbox, your calendar, your repos, your docs, your messages. No training period. No "give it a few weeks.". It becomes you, controlled by you.

Already self-host agentmemory across other coding agents? OpenHuman ships an optional Memory backend that proxies to it — set memory.backend = "agentmemory" in config.toml and the same durable store powers OpenHuman alongside Claude Code, Cursor, Codex, and OpenCode. See the agentmemory backend page for setup.

OpenHuman vs Other Agent Harnesses

High-level comparison (products evolve, so verify against each vendor). OpenHuman is built to minimize vendor sprawl, keep workflow knowledge on-device, and give the agent a persistent memory of your data, not only chat.

Claude Cowork OpenClaw Hermes Agent OpenHuman
Open-source 🚫 Proprietary MIT MIT GNU
Simple to start Desktop + CLI ⚠️ Terminal-first ⚠️ Terminal-first Clean UI, minutes
Cost ⚠️ Sub + add-ons ⚠️ BYO models ⚠️ BYO models One sub + TokenJuice
Memory Chat-scoped ⚠️ Plugin-reliant Self-learning 🚀 Memory Tree + Obsidian vault, optional agentmemory backend
Integrations ⚠️ Few connectors ⚠️ BYO ⚠️ BYO 🚀 118+ via OAuth
Auto-fetch 🚫 None 🚫 None 🚫 None 20-min sync into memory
API sprawl 🚫 Extra keys 🚫 BYOK 🚫 Multi-vendor One account
Model routing 🚫 Single model ⚠️ Manual ⚠️ Manual Built-in
Native tools Code-only Code-only Code-only Code + search + scraper + voice

Star us on GitHub

Building toward AGI and artificial consciousness? Star the repo and help others find the path.

Star History Chart

Contributors Hall of Fame

Show some love and end up in the hall of fame. Contributors get free merch and special access to our Discord.

OpenHuman contributors
S
Description
No description provided
Readme GPL-3.0
214 MiB
Languages
Rust 59.1%
TypeScript 37.9%
JavaScript 1.6%
Shell 1.2%
CSS 0.1%