Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Capabilities System

Overview

The SheetBot capabilities system allows agents to report their capabilities to the server through a flexible three-tier configuration. This enables intelligent task routing where tasks are matched to agents with the appropriate tools, resources, and characteristics. The system consists of static, dynamic, and override capabilities that are merged in a specific order.

Architecture

The capabilities system works through a three-tier configuration that allows for flexible capability definition:

1. Static Capabilities (.capabilities.json)

Manually defined capabilities that are explicitly set by the user. These include:

  • Custom tool configurations
  • Environment-specific settings
  • User-defined packages or features
  • Static system properties

2. Dynamic Capabilities (.capabilities.dynamic.{extension})

Runtime-detected capabilities that are automatically discovered by executing detection scripts. These include:

  • Installed software versions (git, clang, cmake, node, etc.)
  • Operating system details and versions
  • System resources (memory, load averages)
  • Hardware information
  • Available tools and utilities

3. Override Capabilities (.capabilities.override.json)

Final overrides that can modify or add to any capabilities from the previous tiers. These are useful for:

  • Forcing specific capability values
  • Adding computed or derived capabilities
  • Environment-specific overrides
  • Testing and debugging

File Structure

.agent-directory/
├── .capabilities.json                 # Static capabilities
├── .capabilities.dynamic.ts/.py/.sh  # Dynamic detection script
└── .capabilities.override.json       # Override capabilities

Capability Loading and Merging

Capabilities are loaded and merged in the following order:

  1. Base capabilities - OS and architecture from the runtime environment
  2. Static capabilities - Loaded from .capabilities.json (if exists)
  3. Dynamic capabilities - Generated by .capabilities.dynamic.{extension} script (if exists)
  4. Override capabilities - Applied from .capabilities.override.json (if exists)

Each layer can add new capabilities or override existing ones. The dynamic capabilities receive the static capabilities as input, allowing them to build upon or modify existing definitions.

Static Capabilities Example

{
  "packages": ["typescript", "node"],
  "custom_tools": ["my-custom-linter"],
  "environment": "production",
  "max_concurrent_tasks": 4
}

Override Capabilities Example

{
  "hostname": "custom-agent-name",
  "memory": {
    "total": 16384,
    "unit": "MB"
  },
  "debug_mode": true
}

Dynamic Capabilities Detection

The dynamic capabilities library (scripts/lib/capabilities.ts) automatically detects:

Software Versions

  • Git - Version detection via git --version
  • Clang - Version detection via clang --version
  • CMake - Version detection via cmake --version
  • Node.js - Version detection via node --version
  • UV - Version detection via uv --version
  • SCons - Version detection via scons --version
  • VirtualBox - Version detection via vboxmanage --version
  • Deno - Version from Deno.version.deno

System Information

  • OS Details - OS type, release version with major/minor/patch parsing
  • Memory - Total, free, and available memory in MB
  • Load Average - 1min, 5min, and 15min load averages
  • Hostname - System hostname
  • Build Info - Deno build target, arch, etc.

Linux-Specific Information

  • Distribution Details - Name, version, codename from /etc/os-release
  • Pretty Name - Human-readable OS description

Usage by Agent Runners

All agent runners follow the same capability loading pattern: static → dynamic → override.

TypeScript Agent Runner (scripts/agent.template.ts)

// Load static capabilities
let localCapabilities = {};
const capabilitiesJSONPath = path.resolve("./.capabilities.json");
if (fs.existsSync(capabilitiesJSONPath)) {
  const capabilitiesText = Deno.readTextFileSync(capabilitiesJSONPath);
  localCapabilities = Object.assign({}, localCapabilities, JSON.parse(capabilitiesText));
}

// Load dynamic capabilities
const capabilitiesPath = path.resolve("./.capabilities.dynamic.ts");
if (fs.existsSync(capabilitiesPath)) {
  const { getCapabilities } = await import(path.toFileUrl(capabilitiesPath).toString());
  const dynamicCapabilities = await getCapabilities(localCapabilities);
  localCapabilities = Object.assign({}, localCapabilities, dynamicCapabilities);
}

// Apply overrides
const capabilitiesOverrideJSONPath = path.resolve("./.capabilities.override.json");
if (fs.existsSync(capabilitiesOverrideJSONPath)) {
  const capabilitiesOverrideText = Deno.readTextFileSync(capabilitiesOverrideJSONPath);
  localCapabilities = Object.assign({}, localCapabilities, JSON.parse(capabilitiesOverrideText));
}

Python Agent Runner (scripts/agent.template.py)

# Load static capabilities
local_capabilities = {}
if os.path.exists("./.capabilities.json"):
    with open("./.capabilities.json") as f:
        local_capabilities.update(json.load(f))

# Load dynamic capabilities
if os.path.exists("./.capabilities.dynamic.py"):
    spec = importlib.util.spec_from_file_location(
        "capabilities", "./.capabilities.dynamic.py"
    )
    if spec and spec.loader:
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)
        dynamic_capabilities = module.getCapabilities(local_capabilities)
        local_capabilities.update(dynamic_capabilities)

# Apply overrides
if os.path.exists("./.capabilities.override.json"):
    with open("./.capabilities.override.json") as f:
        local_capabilities.update(json.load(f))

Node.js Agent Runner (scripts/agent.template.js)

const localCapabilities = {};
if (fs.existsSync("./.capabilities.json")) {
  Object.assign(localCapabilities, JSON.parse(fs.readFileSync("./.capabilities.json", "utf8")));
}
if (fs.existsSync("./.capabilities.dynamic.mjs")) {
  const mod = await import(pathToFileURL("./.capabilities.dynamic.mjs").href);
  if (mod.getCapabilities) Object.assign(localCapabilities, await mod.getCapabilities(localCapabilities));
}
if (fs.existsSync("./.capabilities.override.json")) {
  Object.assign(localCapabilities, JSON.parse(fs.readFileSync("./.capabilities.override.json", "utf8")));
}
const capabilities = { os: os.platform(), arch: os.arch(), node: process.version, ...localCapabilities };

Shell Agent Runner (scripts/agent.template.sh)

# Load static capabilities
local_capabilities="{}"
if [ -f "./.capabilities.json" ]; then
    local_capabilities=$(jq -s '.[0] * .[1]' <(echo "$local_capabilities") ./.capabilities.json)
fi

# Load dynamic capabilities
if [ -f "./.capabilities.dynamic.sh" ]; then
    source "./.capabilities.dynamic.sh"
    if command -v getCapabilities >/dev/null 2>&1; then
        dynamic_caps=$(getCapabilities "$local_capabilities")
        local_capabilities=$(jq -s '.[0] * .[1]' <(echo "$local_capabilities") <(echo "$dynamic_caps"))
    fi
fi

# Apply overrides
if [ -f "./.capabilities.override.json" ]; then
    local_capabilities=$(jq -s '.[0] * .[1]' <(echo "$local_capabilities") ./.capabilities.override.json)
fi

Static Capabilities Setup

Static capabilities are defined in a JSON file that contains manually configured capabilities:

Creating Static Capabilities

Create .capabilities.json in your agent directory:

{
  "packages": ["typescript", "node", "python"],
  "gpu": {
    "available": true,
    "type": "nvidia",
    "memory_gb": 8
  },
  "storage": {
    "type": "ssd",
    "size_gb": 500,
    "available_gb": 200
  },
  "network": {
    "bandwidth_mbps": 100,
    "latency_ms": 5
  },
  "custom_properties": {
    "team": "backend",
    "region": "us-west-2",
    "cost_center": "engineering"
  }
}

Override Capabilities

Create .capabilities.override.json to override any capabilities:

{
  "hostname": "production-agent-01",
  "memory": {
    "total": 32768,
    "unit": "MB"
  },
  "packages": ["typescript", "node", "python", "production-tools"],
  "environment": "production",
  "debug_mode": false
}

Setup Instructions

For TypeScript Agents

  1. Download the capabilities library:

    curl -o .capabilities.dynamic.ts <sheetbot_baseurl>/scripts/lib/capabilities.ts
    
  2. Or create a dynamic import file .capabilities.dynamic.ts:

    async function getCapabilities(staticCapabilities) {
        const lib = await import(Deno.env.get("SHEETBOT_BASEURL") + "/scripts/lib/capabilities.ts");
        return await lib.getCapabilities(staticCapabilities);
    }
    
    export { getCapabilities };
    
  3. Test the capabilities:

    deno run --allow-all .capabilities.dynamic.ts
    

For Python Agents

Create .capabilities.dynamic.py:

import subprocess
import json
import platform
import psutil  # pip install psutil

def getCapabilities(staticCapabilities):
    capabilities = {}

    # Detect software versions
    try:
        result = subprocess.run(['git', '--version'], capture_output=True, text=True)
        if result.returncode == 0:
            version = result.stdout.split()[-1]
            capabilities['git'] = {'version': version}
    except:
        pass

    # Add system info
    capabilities.update({
        'os': platform.system().lower(),
        'arch': platform.machine(),
        'memory': {
            'total': psutil.virtual_memory().total // (1024*1024),
            'available': psutil.virtual_memory().available // (1024*1024),
            'unit': 'MB'
        }
    })

    return capabilities

For Shell Agents

Create .capabilities.dynamic.sh:

#!/bin/bash

getCapabilities() {
    local static_caps="$1"
    local caps="{}"

    # Detect git
    if command -v git >/dev/null 2>&1; then
        local git_version=$(git --version | awk '{print $3}')
        caps=$(jq -n --arg version "$git_version" '{"git": {"version": $version}}')
    fi

    # Detect node
    if command -v node >/dev/null 2>&1; then
        local node_version=$(node --version | sed 's/v//')
        local node_caps=$(jq -n --arg version "$node_version" '{"node": {"version": $version}}')
        caps=$(jq -s '.[0] * .[1]' <(echo "$caps") <(echo "$node_caps"))
    fi

    # Add system info
    local mem_info=$(free -m | awk 'NR==2{printf "{\"total\":%s,\"free\":%s,\"available\":%s,\"unit\":\"MB\"}", $2, $4, $7}')
    local mem_caps=$(jq -n --argjson mem "$mem_info" '{"memory": $mem}')

    caps=$(jq -s '.[0] * .[1]' <(echo "$caps") <(echo "$mem_caps"))

    echo "$caps"
}

Capabilities Data Structure

The final capabilities object sent to the server has this structure:

{
  "os": "linux",
  "arch": "x86_64",
  "hostname": "agent-host",
  "software": {
    "git": {
      "version": "2.34.1",
      "major_version": 2,
      "minor_version": 34,
      "patch_version": 1
    },
    "deno": {
      "version": "1.28.0",
      "major_version": 1,
      "minor_version": 28,
      "patch_version": 0
    }
  },
  "packages": ["git", "deno"],
  "build": {
    "target": "x86_64-unknown-linux-gnu",
    "arch": "x86_64",
    "os": "linux"
  },
  "os": {
    "os": "linux",
    "release": {
      "version": "5.15.0",
      "major_version": 5,
      "minor_version": 15,
      "patch_version": 0
    }
  },
  "memory": {
    "total": 8192,
    "free": 2048,
    "available": 4096,
    "unit": "MB"
  },
  "loadavg": {
    "1min": 0.5,
    "5min": 0.3,
    "15min": 0.2
  },
  "linux": {
    "release": {
      "version": "20.04",
      "major_version": 20,
      "minor_version": 4,
      "patch_version": 0
    },
    "pretty_name": "Ubuntu 20.04.4 LTS",
    "name": "Ubuntu",
    "versionCodename": "focal",
    "versionId": "20.04"
  }
}

Task Matching

The server uses reported capabilities to match tasks to appropriate agents. Tasks can specify required capabilities, and only agents with matching capabilities will be assigned those tasks.

Testing and Validation

Testing Dynamic Capabilities

For TypeScript dynamic capabilities:

# Test the capabilities detection
deno run --allow-all scripts/lib/capabilities.ts

For Python dynamic capabilities:

# Test by importing and running your dynamic script
python3 -c "
import sys
sys.path.append('.')
import importlib.util
spec = importlib.util.spec_from_file_location('capabilities', './.capabilities.dynamic.py')
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
caps = module.getCapabilities({})
import json
print(json.dumps(caps, indent=2))
"

For Shell dynamic capabilities:

# Source and test the function
source ./.capabilities.dynamic.sh
getCapabilities "{}"

Validating Final Capabilities

To see what capabilities your agent will report:

# For TypeScript agents - temporarily modify agent.template.ts to print capabilities
# Look for the line: console.log("capabilities:", localCapabilities);

# For Python agents - temporarily add: print("capabilities:", local_capabilities)

# For Shell agents - temporarily add: echo "capabilities: $local_capabilities"

Error Handling

The capabilities loading process is designed to be fault-tolerant:

  • Missing files are ignored
  • Detection failures for individual tools don’t prevent other detections
  • The agent continues with whatever capabilities were successfully detected
  • Errors are logged but don’t stop agent execution