3da80d852e feat(skills,node): integrate SKILL.md + managed Node runtime (#681) (#723)
* build(skills): add serde_yaml for SKILL.md frontmatter (#681)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(skills): parse SKILL.md frontmatter and add multi-path discovery (#681)

Extends the skills module with agentskills.io-style discovery:

* Parses YAML frontmatter in SKILL.md (name, description, version,
  author, tags, allowed-tools, license) via serde_yaml, with a
  catch-all extras map for forward-compatible keys.
* Adds SkillScope (User / Project / Legacy) and discover_skills(),
  which scans ~/.openhuman/skills, ~/.agents/skills, <ws>/.openhuman/
  skills, <ws>/.agents/skills and the legacy <ws>/skills directory.
* Gates project-scope loading behind an explicit
  <ws>/.openhuman/trust marker (is_workspace_trusted helper).
* Resolves name collisions with project > user > legacy precedence
  and surfaces shadowing as per-skill warnings.
* Inventories bundled resources under scripts/, references/, assets/.
* Keeps the existing load_skills(workspace_dir) API as a
  backwards-compatible wrapper for existing callers.
* Preserves legacy skill.json fallback (marked legacy = true).
* Lenient validation: missing / mismatched / oversized name or
  description produce warnings instead of errors.
* Adds 12 unit tests covering frontmatter parsing, trust gating,
  collision shadowing, lenient fallbacks, legacy JSON and resource
  inventory. One existing prompt test updated to use
  ..Default::default() for the expanded struct.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills): align frontmatter with agentskills.io spec (#681)

Per the agentskills.io SKILL.md spec, only name, description, license,
compatibility, metadata, and allowed-tools are valid top-level keys.
Our SkillFrontmatter had version, author, and tags as top-level fields,
which drifted from the spec and would silently swallow mis-shaped data
for skills authored against the canonical schema.

Demote version/author/tags into the metadata map. Non-spec top-level
keys still parse via #[serde(flatten)] into extra, and when present
there we emit a migration warning and still populate the derived Skill
fields so existing skills keep working. Add compatibility as an
optional string alongside license.

New tests cover the spec-compliant metadata shape, the deprecated
top-level fallback path, and verify that the full spec frontmatter
parses without leaking into extras.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* build(runtime): add tar+xz2+zip for node runtime extraction (#681)

Node.js distributions ship as .tar.xz on Unix and .zip on Windows. The
upcoming Node runtime bootstrap extracts these in pure Rust; xz2 is
built with the `static` feature so liblzma is bundled and not a system
dependency. zip is pinned to default-features = false + deflate to
avoid pulling in bzip2/zstd/aes which we don't need for Node archives.

Deps only — no behavior change.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(config): add NodeConfig with env overrides (#681)

Introduce managed Node.js runtime configuration so skills that require
`node`/`npm` (agentskills.io packages with build steps) can be wired in
behind a single config switch. Fields:

- `node.enabled` — master switch (default true)
- `node.version` — pinned release (default `v22.11.0` LTS)
- `node.cache_dir` — absolute path for managed distributions; empty = use
  workspace default
- `node.prefer_system` — reuse matching system `node` when found (default
  true); set false for reproducible CI / airgapped deploys

Env overrides land in load.rs alongside the existing LOCAL_AI_TIER block:
- OPENHUMAN_NODE_ENABLED
- OPENHUMAN_NODE_VERSION
- OPENHUMAN_NODE_CACHE_DIR
- OPENHUMAN_NODE_PREFER_SYSTEM

No resolver / downloader yet — subsequent commits in the #681 series
consume this config to bootstrap the runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(node_runtime): detect compatible system node on PATH (#681)

Introduce the `openhuman::node_runtime` module and its first piece: a
synchronous resolver that walks `PATH`, probes `node --version`, and
returns a `SystemNode` when the host toolchain major-version matches the
configured target.

Why resolve first: a successful probe lets the bootstrap skip a
~60 MB download per managed install. Matching is intentionally loose on
the patch level (Node LTS lines are ABI-stable and skills pin their own
deps via package-lock.json); operators needing strict pinning can set
`node.prefer_system = false`.

Tracing is verbose by design — resolver decisions gate the download
path, so operators need a clear breadcrumb trail at `debug`/`info`.

Includes unit tests for `parse_node_version` covering `v` prefix,
whitespace, major-only, and malformed inputs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(node_runtime): SHASUMS256-verified distribution downloader (#681)

Add the second piece of the managed Node.js runtime: streaming download
of OS/arch-specific prebuilt archives from nodejs.org, gated by a
SHA-256 match against the release's signed `SHASUMS256.txt`.

Key contracts:

- `NodeDistribution::for_host(version)` picks the right archive for the
  current OS/arch tuple. Supported matrix:
  * darwin-{arm64,x64}.tar.xz
  * linux-{arm64,x64,armv7l}.tar.xz
  * win-{arm64,x64}.zip
  Unsupported hosts surface a clear error so the caller can flip
  `node.enabled = false` or point at a pre-installed toolchain.

- `fetch_shasums(client, version)` parses `SHASUMS256.txt` into a
  filename -> hex digest map. Tolerant of trailing / signature blocks.

- `download_distribution(...)` streams chunks into the target path,
  computes SHA-256 on the fly, and wipes the partial file if the
  digest does not match. Integrity check is mandatory — skills will
  execute untrusted code inside the resolved runtime.

Verbose tracing at `info` and `debug` follows the repo debug-logging
rule; operators can grep `[node_runtime::downloader]` to trace every
GET, chunk boundary (via total-bytes log), and hash decision.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(node_runtime): archive extraction + atomic install (#681)

Extract downloaded Node.js distributions and move them into the final
cache path without leaving the reader observing a half-populated
directory.

- `extract_distribution(archive, extract_root, is_zip)` — wraps the
  synchronous `tar`/`xz2`/`zip` crates in `spawn_blocking` and returns
  the single top-level folder produced by the archive (`node-vX.Y.Z-
  <os>-<arch>/`). `set_preserve_permissions(true)` + `set_overwrite(
  true)` on the tar side keeps the `node` binary's `+x` bit. The zip
  path restores Unix mode bits via `unix_mode()` so cross-OS builds
  still land correct permissions.
- `atomic_install(staged, final_dest)` — renames a staged directory
  into place via a single `rename(2)`, moving any pre-existing install
  to a `.old-<pid>` sibling first and cleaning it up on success. This
  gives concurrent readers a "before" or "after" view, never a
  partial one.
- Unsafe zip paths (`enclosed_name()` rejection) are logged and
  skipped rather than traversed — defence against malicious archives,
  even though the digest check in the downloader already rules out
  tampered official releases.

No new tests here: the logic leans on upstream tar/zip crates, and
meaningful end-to-end coverage requires a real archive on disk — that
comes in the integration-test commit later in this series.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(node_runtime): bootstrap mutex + cache + bin path helper (#681)

Stitch resolver, downloader, and extractor into a single idempotent
entry point callers use at startup (or lazily before the first
`node_exec`/`npm_exec` call).

`NodeBootstrap::resolve()` contract:
1. Return cached `ResolvedNode` if a previous call already succeeded.
2. Bail when `node.enabled = false`.
3. If `node.prefer_system`, probe the host PATH. Matching major
   version wins — return a `System`-sourced `ResolvedNode`.
4. Otherwise compute the install path
   (`{cache_dir}/node-v{version}-{os}-{arch}/`). If it already contains
   valid bins, reuse it. This makes the bootstrap ~free across
   restarts once a managed install lands.
5. Else fetch `SHASUMS256.txt`, locate the expected digest for our
   archive, stream the download, extract into a `.stage-<pid>` scratch
   dir, `atomic_install` the top-level folder into the final path, and
   remove scratch + archive to reclaim disk.

Concurrency is handled by a `tokio::sync::Mutex<Option<ResolvedNode>>`
— parallel callers queue behind the first one; the winner memoises,
the losers pick up the cached result. No race can produce two
concurrent downloads of the same archive.

Platform-specific bin layout lives in `managed_bin_dir` / `build_
resolved`:
- Unix: `<install>/bin/{node,npm}`
- Windows: `<install>/{node.exe,npm.cmd}`

This closes the resolver/downloader/cache layer promised in the #681
checkpoint. Tools (`node_exec`, `npm_exec`) and shell-PATH injection
layer on top in the next batch of commits.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat(tools): node_exec runs JS via managed node runtime (#681)

Executes `inline_code` (via `node -e`) or a workspace-relative
`script_path` through the NodeBootstrap-resolved `node` binary. POSIX
single-quote quoting keeps user input inert; `env_clear` + allow-list
mirrors the shell tool so secrets never leak. 300s default timeout
(capped at 1800s), 1MB stdout/stderr caps. PATH is prepended with the
resolved bin dir so child processes see managed `node`/`npm`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(tools): npm_exec runs npm via managed node runtime (#681)

Thin npm CLI wrapper paired with node_exec. Subcommands go through
`is_sane_subcommand` (alphanumerics + `._-:` only) and a deny-list
(publish/adduser/login/token/…) blocks registry-mutation and auth
flows. `cwd` is resolved under the workspace — absolute paths and
`..` components are rejected. 600s default timeout (1800s ceiling),
1MB stdout/stderr caps. PATH prepended with managed bin dir so
npm's own node/corepack lookups hit the managed toolchain.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(tools): shell prepends managed node bin to PATH when cached (#681)

Adds a non-blocking `NodeBootstrap::try_cached()` primitive that peeks the
memoised `ResolvedNode` without holding the async lock or triggering a
download. ShellTool gains an optional `node_bootstrap` field wired through
a new `with_node_bootstrap` constructor; when set, each shell invocation
consults `try_cached()` and, on a hit, prepends the managed bin dir to
the child PATH using the platform separator. Unrelated shell commands
stay byte-identical — no download is ever forced from the shell path.

Consequence: once `node_exec`/`npm_exec` have resolved the toolchain
once, skills that shell out to `node`/`npm`/`npx`/`corepack` transparently
pick up the managed install without any per-command coordination.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(tools): register node_exec + npm_exec behind node.enabled (#681)

Wires the skills-oriented Node.js toolchain into the tool registry:

* Constructs one session-scoped `NodeBootstrap` in `all_tools_with_runtime`
  when `root_config.node.enabled` is true — all three consumers (ShellTool,
  NodeExecTool, NpmExecTool) share the same `Arc<NodeBootstrap>` so the
  download/extract/install pipeline runs at most once per session and the
  memoised `ResolvedNode` is reused across every shelling-out call.
* Swaps ShellTool to `with_node_bootstrap(...)` when the bootstrap exists
  so shell PATH injection fires automatically once any node/npm tool has
  resolved the runtime.
* Registers `node_exec` and `npm_exec` only when the flag is on — flipping
  `node.enabled = false` cleanly removes both tools and falls back to the
  legacy ShellTool construction.
* Adds two regression tests (`all_tools_registers_node_exec_when_node_enabled`
  and `all_tools_excludes_node_exec_when_node_disabled`) so future refactors
  can't silently drop the wiring.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* feat(agents): grant node_exec + npm_exec to code_executor + tool_maker (#681)

code_executor is the sandboxed developer sub-agent — it writes, runs, and
debugs code — and tool_maker is the narrow self-healing agent that polyfills
missing commands. Both now see the managed Node.js tools so skills and
polyfills can call into the resolved runtime directly rather than relying on
whatever `node`/`npm` happens to be on the host PATH.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* style(core): apply cargo fmt auto-fixes (#681)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* chore(cargo): sync Cargo.lock to OpenHuman v0.52.26 after rebase (#681)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(node_runtime): strip leading v/V defensively in resolve_from_system (#681)

CodeRabbit R1 flagged that build_resolved receives a raw version string from
SystemNode. detect_system_node already trims the prefix, but trim defensively
at the resolve_from_system boundary too so any future code path constructing
SystemNode with an un-normalised version won't emit "vvX.Y.Z".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(node_runtime): propagate flush errors and clean up partial archives (#681)

CodeRabbit R2 flagged that download_distribution silently swallowed flush
errors via `.ok()` and left partial files on disk when any chunk/write/flush
step errored. Wrap the streaming loop in an async block returning Result<()>,
propagate flush errors, and on any failure delete the partial archive so a
retry starts clean and callers never see a half-written file.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(node_runtime): restore previous install on staged rename failure (#681)

CodeRabbit R3 flagged that atomic_install left the user with no Node runtime
on disk if the staged->final rename failed after a backup had been taken.
Wrap the rename in `if let Err`, restore the backup if present, log restore
failures separately (warning), and always return the original error so the
caller sees the true failure cause.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(node_runtime): enforce real 5s timeout on node --version probe (#681)

CodeRabbit R4 flagged that probe_node_version used Command::output() with no
real timeout — a broken shim or FUSE-backed binary could hang the bootstrap
forever. Add wait-timeout crate dep and rewrite the probe to spawn with
piped stdio, wait_timeout(5s), kill on timeout, and read stdout/stderr from
the piped handles after exit. Logs a warning when a probe times out so the
install can be diagnosed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tools): reject script_path escapes in node_exec (#681)

CodeRabbit R5 flagged that node_exec joined user-supplied script_path onto
the workspace without rejecting `..` segments or absolute paths, letting a
prompt-injected `../../../etc/passwd` read arbitrary files via node. Add a
resolve_script_path helper mirroring npm_exec::resolve_cwd — reject empty,
absolute, parent-dir, and Windows-prefix components. Extra positional args
stay opaque (shell-quoted, not path-checked) and get an explanatory comment
at the call site. Unit tests cover each rejection case plus the relative-
subdir happy path.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(skills): drop dead SKILL.md branch in legacy manifest loader (#681)

CodeRabbit N1 flagged dead code: load_from_legacy_manifest only runs when
load_skill_dir already determined SKILL.md is absent, so the fallback that
re-checked for SKILL.md and its helper read_skill_md_description could
never execute. Simplify to a direct description/location decision and
delete the dead helper.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor(config): extract parse_env_bool helper with warn on unknown values (#681)

CodeRabbit N2 flagged two near-identical boolean env-override match blocks
for OPENHUMAN_NODE_ENABLED and OPENHUMAN_NODE_PREFER_SYSTEM. Extract a
module-level parse_env_bool helper that accepts 1/true/yes/on and
0/false/no/off (case-insensitive) and emits a tracing::warn when the value
is unrecognised so silent mis-spellings don't invisibly leave the config
unchanged.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(node_runtime): detect corrupted cache missing npm (#723)

`probe_managed_install` now verifies the npm launcher exists before
reusing an extracted install. A download interrupted after `node` was
extracted but before `npm` would otherwise be cached forever and
`npm_exec` could never self-heal — now the corrupted cache forces a
fresh download via the normal resolve path.

Addresses CodeRabbit review feedback on PR #723.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(node_runtime): skip non-executable PATH shims when probing node (#723)

`which_node` previously returned the first matching filename on `PATH`
regardless of its execute bit. A non-executable `node` placeholder
earlier in `PATH` (e.g. an unprivileged shim left by a failed install)
would mask a valid later install and force the managed runtime download.

Now checks `file && (mode & 0o111 != 0)` on Unix to mirror shell `which`
behaviour. Windows remains unchanged — `.exe` suffix already encodes
executability for the loader.

Addresses CodeRabbit review feedback on PR #723.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills): deterministic entry order in scan_root (#723)

`read_dir` order is unspecified by the OS. When two sibling skill
directories declare the same logical `frontmatter.name` (which can
differ from the folder name), cross-scope/same-scope deduplication
downstream would pick a non-deterministic winner across runs.

Sort entries by on-disk directory name for a stable, reproducible
order so collision resolution is deterministic.

Addresses CodeRabbit review feedback on PR #723.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills): surface YAML parse errors as skill warnings (#723)

`parse_skill_md` previously swallowed the `serde_yaml` error via `log::warn!`
and fell back to an empty `SkillFrontmatter`, then the catalog reported a
generic "could not parse — exposing directory as placeholder". Skill
authors had no way to see the real cause without scraping logs.

Return parse-level diagnostics as a third tuple element. `load_from_skill_md`
merges them into the skill's user-visible `warnings`, so the catalog now
surfaces the actual YAML error (e.g. "frontmatter parse error: mapping
values are not allowed here at line 3 column 12").

Addresses CodeRabbit review feedback on PR #723.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(skills): skip symlinks when walking skill resources (#723)

`walk_files` used `is_dir()` / `is_file()` which transparently follow
symlinks. Two failure modes:

1. **Unbounded recursion** — a skill resource symlink that points back
   at an ancestor (e.g. `resources/self -> resources/`) would cause
   infinite traversal, eventually blowing the stack.
2. **Silent out-of-tree leakage** — a symlink pointing at `/`, `/etc`,
   or another skill's directory would enumerate its contents into the
   current skill's resource listing.

Switch to `entry.file_type()` and skip symlinks before descending.
Directories and regular files behave as before.

Addresses CodeRabbit review feedback on PR #723.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(node_runtime): default managed cache to user-owned OS cache (#723)

Default `cache_root()` to `dirs::cache_dir()/openhuman/node-runtime/` so a
repo cannot ship a checked-in `./node-runtime/` and have the bootstrap
reuse it as a trusted managed install. Explicit `config.cache_dir` still
wins; workspace-local falls back only when `dirs::cache_dir()` is
unavailable, and we emit a warning on that fallback.

As a second line of defence, `probe_managed_install()` now canonicalises
both the install dir and the cache root and refuses any install that
escapes the cache tree.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(node_runtime): require npm when accepting system node (#723)

On distros that package `node` and `npm` separately (Debian/Ubuntu,
Alpine `nodejs-current`, some NixOS setups) the host `node` can be
present without `npm`. `npm_exec` then fails every call because the
resolved `SystemNode` has no usable npm launcher.

Before returning `Some(SystemNode)`, locate `npm` on `PATH` and probe
`npm --version` through the same `wait_timeout` path as the node probe.
Either missing gate falls back to the managed download flow.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(skills): surface user-scope skills via load_skills (#723)

Existing production callers (`agent::harness::session::builder`,
`channels::runtime::startup`) reach the skill catalog via
`load_skills(workspace_dir)`. Previously this shim passed `None` for
the home directory, so skills installed under `~/.openhuman/skills/`
and `~/.agents/skills/` were silently dropped even after the
multi-scope discovery landed in `discover_skills`. Delegate to
`discover_skills_inner` with `dirs::home_dir()` so user-scope skills
reach the runtime; project-scope still wins on name collision.

Tests stay hermetic via a `load_skills_ws` helper that preserves the
old workspace-only semantics. A new `load_skills_surfaces_user_scope`
test drives a tempdir-as-home through `discover_skills` and asserts
the user-scope skill is returned with `SkillScope::User`.

Addresses CodeRabbit review comment 3116332244.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(skills): skip symlinked skill dirs during scan (#723)

`scan_root` previously accepted any entry where `path.is_dir()`
returned true. `is_dir()` dereferences symlinks, so a link from
`<skills-root>/foo -> /some/external/tree` would load as a
legitimate skill even though `walk_files` already rejects
symlinks deeper in the resource walker. Attacker-authored
symlinks in a skills root would therefore have one remaining
escape hatch.

Switch to `entry.file_type()` (non-dereferencing) and reject
both symlinked and non-directory entries at the top level.
Treat a failed `file_type()` probe as "not safe to traverse"
and skip.

A new `symlinked_skill_dirs_are_skipped` test (Unix-only, since
symlinks are the platform guarantee being tested) creates an
external skill tempdir, links it into the workspace skills root,
and asserts `load_skills_ws` returns empty.

Addresses CodeRabbit review comment 3116332252.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(skills): reject symlinked resource roots (#723)

`inventory_resources` gated each resource sub-root (`scripts/`,
`references/`, `assets/`) on `root.is_dir()`. `is_dir()` follows
symlinks, so a `scripts -> /etc` link inside a skill directory
would pass the check and `walk_files` would inventory the
external tree. Deeper symlinks inside the walk were already
rejected by `walk_files`, but the root-level check was the
missing layer.

Use `std::fs::symlink_metadata` for a non-dereferencing probe
and reject roots whose own `file_type().is_symlink()` returns
true. Treat any `symlink_metadata` error as a non-existent root
and skip.

A new `symlinked_resource_roots_are_rejected` test (Unix-only)
links a skill's `assets/` sub-root to an external tempdir with a
file inside and asserts `inventory_resources` returns empty.

Addresses CodeRabbit review comment 3116332260.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-21 10:59:21 -07:00
2026-04-21 14:57:32 +00:00
2026-04-21 14:14:15 +00:00
2026-02-20 13:03:15 +04:00
2026-02-20 13:03:15 +04:00

OpenHuman

The age of super intelligence is here. OpenHuman is your Personal AI super intelligence. Private, Simple and extremely powerful.

DiscordRedditX/TwitterDocs

Early Beta Platforms: desktop only Latest Release

The Tet

"The Tet. What a brilliant machine" — Morgan Freeman as he reminisces about alien superintelligence in the movie Oblivion

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

# For MacOS/Linux
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 that is designed to integrate with you in your daily life. Here's what makes OpenHuman special:

  • Simple, UI-first — A clean desktop experience and short onboarding paths so you can go from install to a working agent in a few clicks, without a config-first setup. You don't need a terminal to run OpenHuman.

  • One subscription, many providers — You only need one account to get access to many agentic APIs (AI Models, Search, Webhooks/Tunnels and other 3rd party APIs etc..), simplifying the experience to get a powerful agent going.

  • Rich Skills — Plug into Gmail, Slack, Notion, and the rest of your stack via rich, feature-backed skills. Connections are typically one click through setup wizards instead of wiring APIs by hand. Workflow data is kept on device, encrypted locally, and treated as yours: encryption and sensitive context stay on your machine. Webhooks give instant feedback into the agent when external systems or skills emit events, so the loop stays tight without constant polling.

  • Local knowledge base — Built from your data and your activity. How you work across tools, sessions, and connected services—so the agent gets rich, workflow-aware context, not a one-off chat transcript. Everything is stored on your machine and compounding over time without becoming a cloud dossier. Channels, skills and ongoing conversations feed the same loop so day-to-day context does not reset every session.

  • Local AI model — The Rust core exposes local AI paths (and the desktop bundle can ship local/bundled runners where applicable) for the workloads above—vision snippets, speech helpers, summarization, tooling—so sensitive steps can stay off the cloud when you choose.

  • Deep desktop integrations — OpenHuman is a native desktop assistant, not a web-only chat: memory-aware keyboard autocomplete, voice (STT listening and TTS replies), screen intelligence that understands what is on screen and feeds your local context, plus windowing and OS-level permissions—so the agent meets you on the machine, not trapped in a browser tab.

Architecture: docs/ARCHITECTURE.md. Contributor orientation: CONTRIBUTING.md. Running from source: docs/install.md.

OpenHuman vs other agents

High-level comparison (products evolve—verify against each vendor). OpenHuman is built to minimize vendor sprawl, keep workflow knowledge on-device, and ship deep desktop features—not only chat.

Claude Code/Cowork OpenClaw Hermes Agent OpenHuman
Open-source: Is the codebase open to review? 🚫 Proprietary client MIT License MIT License GNU License
Simple: Is it simple to get started? Simple Desktop App + CLI ⚠️ Terminal first and often complex ⚠️ Terminal first and often complex Simple, Clean UI/UX. Get started within minutes
Cost: How expensive is to run? ⚠️ Subscription + add-on tool/API costs ⚠️ Tied to models & hosting you choose ⚠️ Tied to models & hosting you choose Cost optimized with the option to run many things locally for free
Memory & Knowledge Base (KB): Does the agent know you and your world? Built-in memory; mostly chat/session scoped ⚠️ Has a local memory but often needs plugins for richer behavior Self-learning / task loops (typical) 🚀 Local KB + Self-learning from your activity & data (GMail, Notion etc... via skills) & prompts
API spagetti: How complex is it to hook mulitple features together? 🚫 Claude bill + often extra keys for MCP/tools 🚫 BYOK / multi-vendor common 🚫 Multiple providers common One account get access to many bundled platform APIs
Extensibility: Can you add rich features into it? MCP (different model than sandboxed skills) Plugin Architecture (SKILL.md) Plugin Architecture (SKILL.md) 🚀 Rich Skills with ability to have realtime updates, local DB & more
Desktop integrations: Can it integrate into your desktop completely? ⚠️ Desktop app & access to folders ⚠️ Often lighter native surface ⚠️ Often lighter native surface STT, TTS, screen intelligence, memory-aware autocomplete and a whole lot more

Contributors Hall of Fame

Show some love and end up in the hall of fame

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%