13 KiB
description, icon
| description | icon |
|---|---|
| How skills are discovered, fetched, installed, initialized, executed and synced. | puzzle-piece |
Skills: How They Work End-to-End
This document explains how OpenHuman skills are discovered, fetched, installed, initialized, executed, and synchronized across the desktop app and Rust core.
It is written for engineers who need to debug, extend, or migrate the skills system.
1) Mental Model
OpenHuman has two skill-related paths:
-
Active runtime path (authoritative for execution):
- QuickJS skills managed by Rust core runtime.
- Accessed through JSON-RPC methods under
openhuman.skills_*. - UI acts as an RPC client and orchestration layer.
-
Legacy metadata path (still present):
- Workspace scanning of
skill.json+SKILL.md. - Used for older prompt/context loading flows, not the primary execution runtime.
- Workspace scanning of
If you are implementing runtime behavior, use the active QuickJS path.
2) Key Directories and Files
Frontend (app)
app/src/lib/skills/skillsApi.ts- Typed RPC wrapper for skills methods (
list_available,install,start,rpc, etc.).
- Typed RPC wrapper for skills methods (
app/src/lib/skills/manager.ts- Orchestrates setup, OAuth completion, tool usage, and sync triggers.
app/src/lib/skills/runtime.ts- Runtime-facing wrapper around skill lifecycle/tool calls.
app/src/lib/skills/hooks.ts- Read hooks for snapshots and available skills.
app/src/lib/skills/sync.ts- Maps snapshots to tool sync payloads.
app/src/lib/skills/skillEvents.ts- Event emitter for local invalidation/re-fetch.
app/src/utils/desktopDeepLinkListener.ts- Handles deep links (including OAuth complete/error) and notifies runtime.
app/src/utils/config.ts- Frontend config values including
VITE_SKILLS_GITHUB_REPO.
- Frontend config values including
Rust core
src/core/jsonrpc.rs- Core server startup and runtime bootstrap (
bootstrap_skill_runtime).
- Core server startup and runtime bootstrap (
src/openhuman/skills/schemas.rs- Controller schemas and handlers for
openhuman.skills_*methods.
- Controller schemas and handlers for
src/openhuman/skills/registry_ops.rs- Remote registry fetch/cache/search/install/uninstall/list logic.
src/openhuman/skills/registry_types.rs- Registry and available/installed type shapes.
src/openhuman/skills/qjs_engine.rs- Runtime engine for discovery/start/stop/rpc/tool execution.
src/openhuman/skills/manifest.rs- Manifest parsing and platform/runtime eligibility checks.
src/openhuman/skills/skill_registry.rs- Running skill registry, message routing, snapshots.
src/openhuman/skills/qjs_skill_instance/*- QuickJS instance lifecycle, event loop, JS handlers.
src/openhuman/skills/quickjs_libs/bootstrap.js- JS environment bootstrap and bridged APIs.
src/openhuman/skills/socket_manager.rs- Socket integration for tool sync and tool-call routing.
src/openhuman/skills/preferences.rs- Persisted per-skill preference state (enabled/setup flags).
Legacy path (non-authoritative for runtime execution)
src/openhuman/skills/ops.rsworkspace/skillsscanner forskill.jsonandSKILL.md.
3) Skill Packaging and Storage
The active runtime expects each skill directory to contain at minimum:
manifest.json- JS entry file (usually
index.js, but depends on manifestentry)
Installed skills are written to:
${workspace_dir}/skills/<skill_id>/manifest.json${workspace_dir}/skills/<skill_id>/<entry_file>
The runtime also has a skills data area:
${base_dir}/skills_data/<skill_id>/...
Where:
base_diris$OPENHUMAN_WORKSPACEif set, otherwise~/.openhumanworkspace_diris${base_dir}/workspace
4) Registry Fetch and Availability Flow
Registry source
The core fetches a JSON registry from:
SKILLS_REGISTRY_URLif set, else- default:
https://raw.githubusercontent.com/tinyhumansai/openhuman-skills/refs/heads/build/skills/registry.json
Caching
The registry is cached to:
${workspace_dir}/skills/.registry-cache.json
Cache TTL is one hour.
Availability API
When UI calls openhuman.skills_list_available, core:
- Fetches or reads cached registry.
- Scans installed skill directories under
workspace/skills. - Merges both views:
installedbooleaninstalled_versionupdate_available
Install API
When UI calls openhuman.skills_install, core:
- Finds skill entry by ID in registry.
- Downloads
manifest_urlanddownload_url. - Verifies checksum if
checksum_sha256exists. - Writes files under
workspace/skills/<skill_id>/.
Uninstall removes that directory.
5) Runtime Bootstrap and Auto-Start
On core startup, bootstrap_skill_runtime():
- Resolves
base_dir. - Creates
skills_datadirectory. - Creates
RuntimeEngine. - Sets
workspace_diron engine (<base_dir>/workspace). - Registers engine globally for RPC handlers.
- Starts ping and cron schedulers.
- Launches async auto-start.
Auto-start behavior is driven by:
- discovered manifests (
discover_skills) - manifest defaults (
auto_start) - preference overrides (
enable/disableand setup state persistence)
6) Discovery and Start Rules
discover_skills() scans two locations:
- Runtime source directory (bundled/dev source path resolution).
- Workspace installed directory (
workspace/skills).
For each candidate:
- Reads
manifest.json - Requires JavaScript runtime compatibility
- Checks current platform compatibility
- Deduplicates by
manifest.id
start_skill(skill_id) behavior:
- Returns existing running/initializing snapshot if already active.
- Resolves directory (source dir first, workspace fallback).
- Validates manifest runtime/platform.
- Creates a QuickJS skill instance.
- Spawns event loop and registers skill in registry.
- Runs lifecycle (
init, thenstart). - Exposes current snapshot/tools/state.
7) Runtime Message Model
Most interactions become messages from engine to skill instance event loop.
Typical operations:
start/stop- generic rpc (
openhuman.skills_rpc) - tool call (
openhuman.skills_call_tool) - setup events (
setup/start,oauth/complete) - sync/tick events (
skill/tick)
Tool calls can be sync or async in JS. Async calls are awaited with runtime polling and timeout handling in the QuickJS event loop layer.
Isolation guarantees
- Each started skill runs in its own QuickJS context (
AsyncContext) and does not share mutable JS globals with other skills. - Restarting a skill creates a fresh context; prior
globalThismutations are not retained. - Host-level policy blocks skill-to-skill tool invocation. A running skill can only invoke its own tool surface.
- External orchestrators (UI/RPC/socket MCP) can still target tools on any running skill by explicit
skill_id.
8) JSON-RPC Surface (openhuman.skills_*)
The skills controllers are registered in src/openhuman/skills/schemas.rs.
Current method families:
- Registry/catalog:
openhuman.skills_registry_fetchopenhuman.skills_searchopenhuman.skills_list_availableopenhuman.skills_list_installedopenhuman.skills_installopenhuman.skills_uninstall
- Runtime lifecycle/state:
openhuman.skills_discoveropenhuman.skills_listopenhuman.skills_startopenhuman.skills_stopopenhuman.skills_statusopenhuman.skills_get_all_snapshots
- Runtime actions:
openhuman.skills_list_toolsopenhuman.skills_call_toolopenhuman.skills_rpcopenhuman.skills_syncopenhuman.skills_setup_start
- Persistence/control:
openhuman.skills_enableopenhuman.skills_disableopenhuman.skills_is_enabledopenhuman.skills_set_setup_completeopenhuman.skills_data_readopenhuman.skills_data_writeopenhuman.skills_data_dir
9) OAuth and Setup Completion Flow (Desktop)
OAuth callback is handled in desktopDeepLinkListener.ts.
For openhuman://oauth/success?...:
- Persist setup complete via
openhuman.skills_set_setup_complete. - Ensure skill is running via
openhuman.skills_start. - Send
oauth/completeviaopenhuman.skills_rpc. - Trigger initial sync (
skillManager.triggerSync). - Emit local skill-state refresh event.
This keeps persistence, runtime, and UI in sync after browser-based auth.
10) State and Snapshot Model
Skill state can be published from JS via bridge APIs (state.* in bootstrap environment).
Core tracks snapshots containing:
- skill id/name/status
- tools
- runtime error (if any)
- published state map
- setup and connection status
Frontend hooks (useSkillSnapshot, useAllSkillSnapshots, etc.) render from these snapshots and refresh on skill events.
11) Tool Sync and Socket Integration
Socket manager bridges runtime tool inventory and MCP-style calls.
High-level pattern:
- Core publishes available tools from running skills.
- Frontend/runtime sync maps snapshots to tool payload.
- Incoming tool calls route to
skill_id+tool_name. - Core executes via runtime and returns
ToolResult.
Tool naming over MCP remains skillId__toolName for external orchestration.
12) Environment Variables and Configuration
Core/runtime relevant
SKILLS_REGISTRY_URL- Override skill catalog URL.
OPENHUMAN_WORKSPACE- Sets base workspace root (
skills_data,workspace/skills, config).
- Sets base workspace root (
OPENHUMAN_CORE_PORT- Core JSON-RPC HTTP port.
OPENHUMAN_CORE_RUN_MODE- Tauri core launch mode behavior.
OPENHUMAN_CORE_BIN- Override core binary path.
Frontend relevant
VITE_SKILLS_GITHUB_REPO- UI-side repository slug default for skills registry context/display.
- Note: runtime fetch authority is still
SKILLS_REGISTRY_URLin core.
VITE_OPENHUMAN_CORE_RPC_URL/OPENHUMAN_CORE_RPC_URL- Core RPC endpoint override for app client.
13) End-to-End Sequence (Install + OAuth + Tool Call)
- User opens Skills screen.
- UI calls
openhuman.skills_list_available. - Core returns registry + installed/enriched availability.
- User clicks install/connect.
- UI calls
openhuman.skills_install. - UI starts skill via
openhuman.skills_start. - Skill initializes in QuickJS (
initthenstart). - OAuth browser completes and deep links back.
- UI marks setup complete, sends
oauth/complete, triggers sync. - Agent or UI calls tool.
- Core routes tool call to skill event loop and returns result.
14) Debugging Guide
Common checks
- Registry errors:
- verify
SKILLS_REGISTRY_URL - inspect cache file under
workspace/skills/.registry-cache.json
- verify
- Install issues:
- check
manifest_url/download_urlaccessibility - validate checksum mismatch logs
- check
- Startup issues:
- ensure
manifest.jsonexists andruntimeis JS-compatible - verify platform filter in manifest
- ensure
- OAuth issues:
- confirm deep-link callback includes
integrationIdandskillId - verify
set_setup_completeandoauth/completeRPCs are invoked
- confirm deep-link callback includes
- Tool-call failures:
- verify skill status is
Running - inspect skill error in snapshot
- cross-skill denied errors mean a skill attempted to invoke another skill's tool; this is blocked by design
- verify skill status is
Useful runtime truths
- Catalog truth: remote registry (+ cache)
- Installed truth:
workspace/skills/*/manifest.json - Running truth: runtime snapshots from
openhuman.skills_status/openhuman.skills_get_all_snapshots
15) Known Split-Brain Risks
There is an intentional but risky overlap between:
- QuickJS runtime manifests (
manifest.json) and - legacy loader semantics (
skill.json+SKILL.md)
Impact:
- Different subsystems can report different views of "what skills exist."
- Documentation or migration work can accidentally target the wrong system.
Recommendation:
- Treat
openhuman.skills_*+ QuickJS manifests as canonical for execution paths. - Keep legacy path use explicitly scoped until fully migrated.
16) Testing Coverage Pointers
- Registry/install/runtime e2e validations:
tests/json_rpc_e2e.rs
- Core unit tests:
src/openhuman/skills/*(registry/runtime modules)
- App integration points:
app/src/lib/skills/*- deep-link flow in
app/src/utils/desktopDeepLinkListener.ts
When changing behavior, test both:
- JSON-RPC behavior from core (
openhuman.skills_*methods) - App orchestration behavior (especially OAuth/setup/sync)
17) Practical Rules for Contributors
- Put business/runtime behavior in Rust core.
- Keep frontend as orchestration and UX.
- Prefer adding/using explicit
openhuman.skills_*methods over side channels. - Preserve setup + enabled flags coherently across restarts.
- Avoid introducing new legacy skill metadata paths.
- Add traceable logs around install/start/setup/tool call boundaries.
18) Skill Author Isolation Contract
Guaranteed
- Your skill runs in its own QuickJS context and event loop when started.
- Your skill's
globalThisstate is isolated from other running skills. - Your skill can publish state only for itself; state is namespaced by
skill_id. - Cross-skill tool invocation from within a skill is denied by host policy.
- External host orchestration (UI/RPC/socket MCP) may call your tools by explicit
skill_id.
Not supported / undefined behavior
- Do not rely on
globalThis.skills.callToolfor inter-skill calls. - Do not rely on in-memory JS globals surviving a stop/restart cycle.
- Do not assume execution ordering across different skills.
- Do not treat runtime-internal bridge objects as stable public APIs.