Files
openhuman/gitbooks/developing/skills.md
T

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:

  1. 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.
  2. Legacy metadata path (still present):

    • Workspace scanning of skill.json + SKILL.md.
    • Used for older prompt/context loading flows, not the primary execution runtime.

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.).
  • 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.

Rust core

  • src/core/jsonrpc.rs
    • Core server startup and runtime bootstrap (bootstrap_skill_runtime).
  • src/openhuman/skills/schemas.rs
    • Controller schemas and handlers for openhuman.skills_* methods.
  • 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.rs
    • workspace/skills scanner for skill.json and SKILL.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 manifest entry)

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_dir is $OPENHUMAN_WORKSPACE if set, otherwise ~/.openhuman
  • workspace_dir is ${base_dir}/workspace

4) Registry Fetch and Availability Flow

Registry source

The core fetches a JSON registry from:

  • SKILLS_REGISTRY_URL if 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:

  1. Fetches or reads cached registry.
  2. Scans installed skill directories under workspace/skills.
  3. Merges both views:
    • installed boolean
    • installed_version
    • update_available

Install API

When UI calls openhuman.skills_install, core:

  1. Finds skill entry by ID in registry.
  2. Downloads manifest_url and download_url.
  3. Verifies checksum if checksum_sha256 exists.
  4. Writes files under workspace/skills/<skill_id>/.

Uninstall removes that directory.


5) Runtime Bootstrap and Auto-Start

On core startup, bootstrap_skill_runtime():

  1. Resolves base_dir.
  2. Creates skills_data directory.
  3. Creates RuntimeEngine.
  4. Sets workspace_dir on engine (<base_dir>/workspace).
  5. Registers engine globally for RPC handlers.
  6. Starts ping and cron schedulers.
  7. Launches async auto-start.

Auto-start behavior is driven by:

  • discovered manifests (discover_skills)
  • manifest defaults (auto_start)
  • preference overrides (enable/disable and setup state persistence)

6) Discovery and Start Rules

discover_skills() scans two locations:

  1. Runtime source directory (bundled/dev source path resolution).
  2. 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:

  1. Returns existing running/initializing snapshot if already active.
  2. Resolves directory (source dir first, workspace fallback).
  3. Validates manifest runtime/platform.
  4. Creates a QuickJS skill instance.
  5. Spawns event loop and registers skill in registry.
  6. Runs lifecycle (init, then start).
  7. 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 globalThis mutations 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_fetch
    • openhuman.skills_search
    • openhuman.skills_list_available
    • openhuman.skills_list_installed
    • openhuman.skills_install
    • openhuman.skills_uninstall
  • Runtime lifecycle/state:
    • openhuman.skills_discover
    • openhuman.skills_list
    • openhuman.skills_start
    • openhuman.skills_stop
    • openhuman.skills_status
    • openhuman.skills_get_all_snapshots
  • Runtime actions:
    • openhuman.skills_list_tools
    • openhuman.skills_call_tool
    • openhuman.skills_rpc
    • openhuman.skills_sync
    • openhuman.skills_setup_start
  • Persistence/control:
    • openhuman.skills_enable
    • openhuman.skills_disable
    • openhuman.skills_is_enabled
    • openhuman.skills_set_setup_complete
    • openhuman.skills_data_read
    • openhuman.skills_data_write
    • openhuman.skills_data_dir

9) OAuth and Setup Completion Flow (Desktop)

OAuth callback is handled in desktopDeepLinkListener.ts.

For openhuman://oauth/success?...:

  1. Persist setup complete via openhuman.skills_set_setup_complete.
  2. Ensure skill is running via openhuman.skills_start.
  3. Send oauth/complete via openhuman.skills_rpc.
  4. Trigger initial sync (skillManager.triggerSync).
  5. 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:

  1. Core publishes available tools from running skills.
  2. Frontend/runtime sync maps snapshots to tool payload.
  3. Incoming tool calls route to skill_id + tool_name.
  4. 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).
  • 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_URL in 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)

  1. User opens Skills screen.
  2. UI calls openhuman.skills_list_available.
  3. Core returns registry + installed/enriched availability.
  4. User clicks install/connect.
  5. UI calls openhuman.skills_install.
  6. UI starts skill via openhuman.skills_start.
  7. Skill initializes in QuickJS (init then start).
  8. OAuth browser completes and deep links back.
  9. UI marks setup complete, sends oauth/complete, triggers sync.
  10. Agent or UI calls tool.
  11. Core routes tool call to skill event loop and returns result.

14) Debugging Guide

Common checks

  1. Registry errors:
    • verify SKILLS_REGISTRY_URL
    • inspect cache file under workspace/skills/.registry-cache.json
  2. Install issues:
    • check manifest_url/download_url accessibility
    • validate checksum mismatch logs
  3. Startup issues:
    • ensure manifest.json exists and runtime is JS-compatible
    • verify platform filter in manifest
  4. OAuth issues:
    • confirm deep-link callback includes integrationId and skillId
    • verify set_setup_complete and oauth/complete RPCs are invoked
  5. 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

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:

  1. JSON-RPC behavior from core (openhuman.skills_* methods)
  2. 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 globalThis state 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.callTool for 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.