Semantic Skills

Semantic Skills extend Mayros agents with graph-aware capabilities. Each skill declares its permissions, assertions, and queries in a SKILL.md manifest. Skills run inside a QuickJS WASM sandbox with namespace-scoped access to the Cortex knowledge graph.

SKILL.md manifest

Every semantic skill is defined by a SKILL.md file with YAML frontmatter:

yaml
---
type: semantic
semantic:
  version: 1
  permissions:
    graph: [read, write]
    proofs: [request, verify]
    memory: [recall, remember]
  assertions:
    - predicate: "isVerified"
      requireProof: true
  queries:
    - predicate: "hasStatus"
      scope: namespace
  allowedTools:
    - skill_graph_query
    - skill_assert
    - hub_search
  maxQueries: 100
  skillVersion: "1.0.0"
  dependencies:
    - slug: "verify-kyc"
      version: "^1.0.0"
---

Permission categories

CategoryValuesControls
graphread, writeAccess to graph queries and assertions
proofsrequest, verify, publishZK proof operations
memoryrecall, rememberSkill memory context

Validation rules

  • Assertions with requireProof: true need proofs: [request]
  • Queries require graph: [read]
  • Assertions require graph: [write]
  • maxQueries must be >= 1 if specified
  • skillVersion must be valid semver

Skill loading pipeline

The SkillLoader handles two execution modes:

Sandbox mode (default)

  1. Read skill.ts or skill.js from the skill directory
  2. Run the static scanner (scanSource) — reject if critical findings
  3. Transpile TypeScript to JavaScript
  4. Create a QuickJS WASM sandbox with resource limits
  5. Evaluate the skill code inside the sandbox
  6. Inject graphClient and logger as host functions
  7. Return a SkillRuntime proxy that bridges calls into the sandbox

Direct mode (development only)

Requires MAYROS_UNSAFE_DIRECT_LOAD=1 and sandboxEnabled: false. Uses native import() with a 5-second timeout. Not recommended for production.

Core tools

The plugin registers six agent tools, all gated by permissions:

ToolRequired permissionDescription
skill_graph_querygraph:readQuery the semantic graph with namespace scoping
skill_assertgraph:writePublish an assertion with optional PoL proof
skill_verify_assertiongraph:read, proofs:verifyVerify an assertion's proof
skill_request_zk_proofproofs:requestRequest a zero-knowledge proof
skill_verify_zk_proofproofs:verifyVerify a zero-knowledge proof
skill_memory_contextmemory:recallRetrieve skill-scoped memory context

Enrichment pipeline

When a skill_graph_query executes, the active skill's onQuery handler can enrich results:

  1. Graph query returns raw triples from Cortex
  2. SkillLoader.invokeQuery() calls the skill runtime's onQuery
  3. The skill returns additionalContext (enrichment data)
  4. sanitizeEnrichment() validates the response:
    • Parses as JSON or plain text
    • Strips prompt injection patterns
    • Enforces depth and length limits
    • Wraps in <skill-enrichment type="data"> tags
  5. Enrichment is appended to the tool response (capped at 4096 chars)
  6. A 2-second timeout (Promise.race) prevents DoS via slow enrichment

Namespace isolation

All graph operations are forced to the skill's namespace prefix (${ns}:):

typescript
// enforceNsPrefix ensures all queries are scoped:
// scope: "agent"     → subject = "${ns}:agent:${agentId}"
// scope: "namespace" → subject = "${ns}:${subject}"
// scope: "global"    → subject = "${ns}:${subject}" (capped to own ns)

The sandbox graphClient also enforces namespace prefixes on createTriple, deleteTriple, listTriples, and patternQuery.

Rate limiting

A SkillRateLimiter uses a sliding 1-minute window per skill:

  • Default: 60 calls per minute (configurable via maxCallsPerMinute)
  • Applied to: skill_graph_query, skill_assert, skill_verify_assertion
  • Exceeding the limit returns a rate-limited error response

Query and write limits

Two layers of query limiting:

  1. Per-skill: each skill has maxQueries (from manifest or config default of 50)
  2. Global cap: maxGraphQueries * activeSkillCount

Write limits are enforced inside the QuickJS sandbox via checkWriteLimit() (default: 50 writes per session).

Hot-reload

When hotReload: true, file changes trigger:

  1. Build temporary maps (manifests, resolvers, engines)
  2. Validate all manifests
  3. Downgrade block: reject if allowedTools were removed
  4. Log manifest diffs (changes to permissions, assertions, maxQueries)
  5. Atomic swap: clear active maps, replace with new
  6. Dispose old sandboxes

Configuration

typescript
{
  cortex: {
    host: "127.0.0.1",
    port: 19090,
    authToken?: string
  },
  agentNamespace: "mayros",         // RDF namespace prefix
  skillSandbox: {
    sandboxEnabled: true,           // false requires MAYROS_UNSAFE_DIRECT_LOAD=1
    memoryLimitBytes: 8388608,      // 1MB–256MB (default: 8MB)
    maxStackSizeBytes: 524288,      // 64KB–8MB (default: 512KB)
    executionTimeoutMs: 10000,      // 100–60000 (default: 10s)
    maxCallsPerMinute: 60,          // 1–1000 (default: 60)
    maxGraphQueries: 50,
    maxAssertions: 20,
    proofTimeoutMs: 5000,
    allowZkProofs: true
  },
  verification: {
    requireSignature: true,
    polValidation: true,
    autoScan: true
  },
  hotReload: false
}