Skip to main content
Dynamic skill injection architecture

Resolution pipeline

On every chat start, the server calls skill_manager.get_user_skills(email). The result resolves through three fallback levels:
  1. Memory cache — 60-second TTL. Same (email) hit twice in a minute = dict lookup.
  2. Settings Wrapper API — if MCP_TOKENS_URL is set, fetches the user’s enabled skills.
  3. Disk cache — if the API is down but we cached last known good, reuse it.
  4. Hardcoded defaults — all 13 public skills.
Without the Settings Wrapper, step 4 always wins — everyone gets the full public set.

System prompt injection

The skill list becomes an <available_skills> XML block injected into the system prompt:
The model reads this once per chat and then consults SKILL.md files on demand.

User skill mounts

User-uploaded skills (ZIPs from the Settings Wrapper) are:
  1. Downloaded and extracted atomically: unzip to temp, rename to final.
  2. Cached under /data/skills-cache/{name}/ on the host.
  3. Tracked in .manifest.json (name → SHA-256 + timestamp).
  4. Bind-mounted into the sandbox at /mnt/skills/user/{name}/read-only.
Stale cache reuse: if the API or download fails but old cache exists, use it. Docker daemon needs host paths for bind mounts — configured via SKILLS_CACHE_HOST_PATH.

Core functions (skill_manager.py)

HTTP endpoints

Configuration

Verify