JARVIS Documentation

repository·main·Indexed 20 days ago

https://github.com/vierisid/jarvis

An always-on autonomous AI daemon designed for desktop awareness and multi-agent task execution. JARVIS uses a split architecture consisting of a central Daemon (the brain) for LLM management and memory, and lightweight Sidecars (the hands) that provide access to a host machine's terminal, filesystem, clipboard, and screenshots. Features include an Ambient Mode with a 'Pebble' UI for Windows, a built-in authority engine for security, and support for providers like Anthropic, OpenAI, Google Gemini, and Ollama.

Tokens
103.9K
Snippets
317
Records
468
Agent score
68%

What's inside JARVIS

  1. Overview of the Sidecar Communication Protocol

    main

    The Brain and Sidecars communicate via a single WebSocket connection using an asynchronous, event-oriented protocol. There is no request-response coupling at the transport level; instead, the WebSocket acts as a bidirectional pipe for typed messages.

    • Brain → Sidecar: Uses RPC requests to trigger execution on the sidecar.
    • Sidecar → Brain: Uses Events to send notifications, including RPC results.

    All events from all sidecars are funneled into a central event scheduler on the Brain for ordered processing.

  2. Overview of Personality Engine modules

    main

    The Personality Engine is organized into several core modules that handle different aspects of the adaptive system:

    • model.ts: Manages the personality state structure and how it is persisted.
    • learner.ts: Responsible for signal extraction (detecting user preferences) and the learning logic.
    • adapter.ts: Handles channel adaptation (e.g., adjusting for WhatsApp, Email, or Terminal) and generating the final LLM prompts.
    • index.ts: The main entry point providing the public API exports.
  3. Understand the webview_go patches in JARVIS

    main

    The sidecar component uses a custom-patched version of webview_go to resolve platform-specific UI issues:

    Win32 (Windows)

    Problem: The default engine constructor shows the window before WebView2 is initialized, causing a black flash. Solution: The win32_edge_engine constructor in libs/webview/include/webview.h is patched to use ShowWindow(m_window, SW_HIDE) instead of SW_SHOW. The host (sidecar) then manually reveals the window using revealWebviewOnLoad once the page has loaded.

    Cocoa (macOS)

    Problem: Cocoa requires NSWindow creation on the main thread. Since panels are often spawned on background goroutines, calling webview.New() off-main results in the error: "NSWindow should only be instantiated on the main thread!". Solution: The cocoa_wkwebview_engine::set_up_window() method is patched to detect if it is running off-main and, if so, uses dispatch_sync_f to marshal the call onto the main queue.

    GTK (Linux)

    No patches are applied to the GTK path.

  4. Manage system services with the Service Registry

    main

    The ServiceRegistry manages the lifecycle of all system services (such as observers, agents, and the WebSocket server). Services must implement the Service interface. You can register services and then trigger startAll() or stopAll() (which stops services in reverse order of registration).

    import { ServiceRegistry } from './src/daemon/services.ts';
    
    const registry = new ServiceRegistry();
    
    // Register your service
    registry.register({
      name: 'my-service',
      async start() {
        // Start logic
      },
      async stop() {
        // Stop logic
      },
      status() {
        return 'running';
      }
    });
    
    // Start all services
    await registry.startAll();
    
    // Stop all services (in reverse order)
    await registry.stopAll();
  5. Understanding the risks of desktop_press_keys in templates

    main

    Some templates attempt to use desktop_press_keys to perform actions like 'Create Page' or 'Enter Data'. This tool is an OS-level injector to the currently focused desktop window, not the browser session.

    Risks and Constraints:

    • Focus Dependency: If the browser is not the focused window, keystrokes will land in the wrong application, which can be destructive.
    • Headless Failure: It breaks if the browser is running in headless mode (as there is no DISPLAY to receive keys).
    • Chrome Shortcuts: Many common shortcuts are reserved by the OS or Chrome and will not work as intended (e.g., Ctrl+N opens a new browser window instead of a Notion page; Ctrl+1..9 switches browser tabs).
    • Remote/Sidecar Issues: If the browser and desktop are running in different sidecars or remote environments, the tool may fail to target the correct window.
  6. How the Sidecar is launched on macOS

    main

    The npm launcher (npm/jarvis-sidecar/bin/jarvis) is designed to prefer the bundle-associated binary at bin/Jarvis.app/Contents/MacOS/jarvis when running on Darwin. If the bundle is not present, it falls back to a bare bin/jarvis binary.

    Note that running the inner binary directly still associates the process with the bundle, allowing notifications to function.

  7. Configure the brain address in enrollment tokens

    main

    When you run jarvis enroll, the resulting token embeds the brain's address so the sidecar knows where to connect. This address is determined by the daemon.brain_domain setting in config.yaml (or the JARVIS_BRAIN_DOMAIN environment variable).

    Scheme Rules:

    • Full URL: If you provide a full URL like https://jarvis.example.com, the token uses wss.
    • Bare Host: If you provide a bare host, Jarvis assumes it is secure (wss/https) unless it is a loopback address (localhost, 127.0.0.1, [::1]).
    • LAN Warning: For a LAN setup using a bare IP (e.g., 192.168.1.10:3142), you must explicitly prefix it with http://. If you omit the prefix, the sidecar will attempt a wss:// connection and fail the TLS handshake against the plain-HTTP daemon.
    • Loopback: For localhost, Jarvis automatically uses plain ws.
    daemon:
      brain_domain: "http://192.168.1.10:3142"
  8. How desktop templates are matched and scored

    main

    Desktop templates are selected using the matchWebappTemplatesScored(message, context?) function. The matching logic uses an awareness system's context tracker to provide { foregroundProcess?, foregroundTitle? } to the matcher.

    Scoring Rules:

    • Foreground Process Match: +100 points (e.g., if the user is in VS Code and asks to "split the editor", the VS Code template is selected even without naming it).
    • Window Title Match: +50 points.
    • Message Keywords: Process names mentioned in the user's message (e.g., "open it in vscode") are matched using word boundaries.

    Constraints:

    • kind:desktop templates only match if a desktop-capable sidecar is currently connected. Otherwise, they are skipped to prevent the model from receiving unexecutable instructions.
  9. JARVIS Security and Authority Engine

    main

    JARVIS implements a built-in authority engine that gates every action at runtime.

    Key security features include:

    • Audit Trails: All tool executions are logged.
    • Explicit Approval: Sensitive operations require manual approval via the dashboard, Telegram, or Discord.
    • Emergency Controls: Emergency pause and kill controls are always available to halt operations.
  10. How the Personality Engine architecture works

    main

    The Personality Engine is composed of three main modules that manage how J.A.R.V.I.S. learns and communicates:

    1. Model (personality/model.ts): Manages the state and persistence of the personality. It defines the structure of traits, preferences, and relationship data.
    2. Learner (personality/learner.ts): The intelligence layer that extracts preference signals (like verbosity or formality) from user messages and applies them to the model.
    3. Adapter (personality/adapter.ts): The presentation layer that adapts the personality for specific communication channels (e.g., WhatsApp vs. Email) and converts the personality state into a text prompt for LLMs.

    Together, these modules allow the system to evolve its communication style based on user feedback and interaction history.

    import {
      getPersonality,
      savePersonality,
      extractSignals,
      applySignals,
      recordInteraction,
      getChannelPersonality,
      personalityToPrompt,
    } from '@/personality';
    
    // Typical workflow: 
    // 1. Get personality -> 2. Extract signals -> 3. Apply signals -> 4. Record interaction -> 5. Save
  11. How Sidecar authentication and enrollment works

    main

    Sidecar authentication uses asymmetric ES256 (ECDSA P-256) signing to ensure that tokens are authentic and untampered without requiring the Sidecar to hold the Brain's private signing key.

    The Lifecycle:

    1. Token Generation: The Brain signs a JWT containing the brain (WebSocket URL) and jwks (Public Key URL) claims using its private key.
    2. Verification: The Sidecar decodes the JWT to find the jwks URL, fetches the Brain's public key via HTTPS, and verifies the JWT signature.
    3. Connection: Once verified, the Sidecar connects to the brain WebSocket endpoint, passing the JWT in the Authorization: Bearer <jwt> header.
    4. Validation: The Brain verifies the signature again and checks if the sid (Sidecar UUID) is still active in its database.

    Key Management:

    • Storage: Keys are stored at {data_dir}/sidecar-keys/private.pem and {data_dir}/sidecar-keys/public.pem.
    • Rotation: To rotate keys, delete the .pem files and restart the daemon. Warning: This invalidates all existing Sidecar tokens; all devices must re-enroll.
  12. Understand the 10-level Authority System

    main

    The Role Engine uses a 1-10 scale to control agent capabilities. Higher levels unlock more sensitive action categories:

    • Levels 1-2 (Read Only): read_data
    • Levels 3-4 (Read & Write): write_data, send_message
    • Levels 5-6 (Command Execution): execute_command, access_browser, control_app
    • Levels 7-8 (Agent Management): spawn_agent, send_email, install_software
    • Levels 9-10 (Full Access): make_payment, modify_settings, delete_data, terminate_agent