Skip to main content
Skill definitions live in skills/<name>/skill.json. Each manifest declares what the skill does, what it needs, and its security classification.

Schema reference

{
  "name": "string",
  "description": "string",
  "version": "string",
  "sensitivity": "normal | elevated",
  "action_risk": "none | low | medium | high | critical | number",
  "inputs": {
    "field_name": "type_string",
    "optional_field": "type_string?"
  },
  "outputs": {
    "field_name": "type_string"
  },
  "permissions": ["string"],
  "secrets": ["string"],
  "timeout": 30000,
  "capabilities": ["string"],
  "allowed_callers": ["string"],
  "entity_enrichment": {
    "param": "string",
    "default": "caller | agent"
  },
  "install": {
    "requires_secrets": ["string"]
  }
}

Field details

name

Unique identifier for the skill. Must match the directory name under skills/. Convention: lowercase kebab-case.

action_risk

Required. The minimum autonomy score required to invoke this skill without explicit CEO approval. Curia will not start if any skill omits this field.
LabelMinimum scoreUse for
none0Read-only operations with no side effects — web search, reading email, summarizing
low60Internal state writes — memory, contacts, configuration
medium70Outbound communications — sending email, Signal messages
high80Calendar writes, commitments on behalf of the CEO
critical90Financial, destructive, or irreversible operations
A raw number (0–100) can be used for precision (e.g., 75 for a skill that should unlock just above “approval required” but below “spot-check”). Numbers outside [0, 100] produce a validation error at load time.

sensitivity

ValueMeaning
normalCan be auto-approved based on autonomy score
elevatedRequires human approval on first use, regardless of autonomy score

inputs and outputs

Shorthand type declarations for skill parameters and return values. Field names map to type strings:
Type stringMeaning
"string"Required string
"string?"Optional string
"number"Required number
"boolean"Required boolean
"object"Required object (any shape)
For MCP-sourced skills, the full JSON Schema from the MCP server’s tools/list response is used directly instead of this shorthand.

permissions

Array of permission names this skill requires. Validated at load time.

secrets

Array of vault secret keys this skill needs access to via ctx.secret(). Secrets are resolved from the encrypted vault (with an env-var fallback applied at bootstrap). Only secrets declared here are accessible — the skill cannot read arbitrary secrets or environment variables. Keys are lowercase snake_case (e.g. tavily_api_key, nylas_api_key). This is the runtime allowlist — what the handler may read while executing. To require that a secret be present before the skill can be installed or enabled, declare it in install.requires_secrets instead (a skill commonly declares the same key in both).

timeout

Per-invocation timeout in milliseconds. Default: 30000 (30 seconds).

capabilities

Declares which privileged services this skill needs from the execution layer. Only known capability names are accepted — the loader validates against a fixed allowlist at startup and rejects unknown names.
CapabilityService provided
busEvent bus access
agentRegistryAgent registry for delegation
outboundGatewayOutbound message delivery (email, Signal)
heldMessagesHeld message service
schedulerServiceJob scheduling
entityMemoryKnowledge graph read/write
nylasCalendarClientCalendar operations via Nylas
autonomyServiceAutonomy score management
executiveProfileServiceCEO writing voice profile
officeIdentityServiceOffice identity — assistant name/title, tone, decision posture, and behavioral preferences. Used by behavioral-preferences-update.
browserServicePlaywright browser for web interaction
bullpenServiceInter-agent discussion threads
skillSearchSearch the skill registry
actionLogRepoAutonomy action log for approval lifecycle management
executionLayerRe-invoke skills with humanApproved bypass (CEO-only, approve-action skill)
confidencePipelineContact confidence scoring — fire signals when modifying trust-related data
tempFileStoreWrite binary buffers to tmpfs; returns file:// URLs for MCP tool handoff
infraLlmConstrained LLM access — classify() and extract() only, no raw chat()
outboundContextRegister/release outbound-context entries for delegation-aware reply correlation. Pre-scoped to the current conversation.
Services not listed here (contactService, entityContextAssembler, agentPersona) are universal — available to every skill without declaration.

allowed_callers

Optional. Restricts which agents may invoke this skill. When set, only the named agents can call the skill — all others are rejected with a structured failure before any other gate (autonomy, elevation) is evaluated.
"allowed_callers": ["contacts", "coordinator"]
  • Agent names are validated against the loaded agent registry at startup — unknown names cause a hard startup failure.
  • CEO-approved re-executions bypass the caller gate.
  • The special value "system" matches system-layer invocations (checkpoint processor, etc.).
  • Omit the field entirely (or set to []) to allow any agent — this is the default.

context_bridge (input field)

Optional input on send skills (email-send, email-reply, signal-send). A JSON-encoded object that lets the caller attach delegation metadata to the outbound-context entry that’s auto-registered when the message ships.
"context_bridge": "{\"agent_id\": \"research-analyst\", \"expected_reply\": \"clarifying answer\", \"delegation_hint\": \"resume the multi-turn research conversation\", \"expires_in_hours\": 48}"
Recognized fields inside the JSON:
FieldPurpose
agent_idOriginating specialist (coordinator, research-analyst, meeting-debrief, …)
expected_replyShort hint about the kind of reply being awaited
delegation_hintFree-form instruction the dispatcher surfaces back on inbound replies (e.g. “reply belongs to research-analyst”)
metadataOptional JSON object — structured delegation context (e.g. resume_token for multi-turn clarification)
expires_in_hoursOverrides the global contextBridge.explicitExpiryHours for this entry
Send skills auto-register a minimal outbound-context entry on every successful send regardless of whether context_bridge is passed. The optional field is for callers (specialists, the coordinator) that want richer delegation context.

entity_enrichment

Optional. When declared, the execution layer automatically assembles entity context before invoking the skill handler, injecting it into ctx.entityContext.
FieldDescription
paramThe input key containing contact/entity IDs to enrich
defaultWhat to use when the input is not provided: "caller" (the message sender) or "agent" (the agent’s own contact)

install

Optional. Declares install-time requirements that the registry enforces before a skill can be installed or enabled. Skills live in a DB-backed registry and only enabled skills are loaded at startup.
FieldDescription
requires_secretsArray of vault secret keys that must already exist in the vault before the skill can be installed or enabled. The registry rejects the install/enable until every listed key is present.
"install": {
  "requires_secrets": ["tavily_api_key"]
}
This is distinct from the runtime secrets allowlist: secrets controls what the handler may read while executing; install.requires_secrets is a precondition gate checked at install/enable time. The web-search skill is the canonical example — it declares tavily_api_key here so it cannot be enabled until you have provisioned a Tavily key.

Example

{
  "name": "email-send",
  "description": "Send a new email to specified recipients.",
  "version": "1.0.0",
  "sensitivity": "normal",
  "action_risk": "medium",
  "inputs": {
    "to": "string",
    "cc": "string?",
    "subject": "string",
    "body": "string"
  },
  "outputs": {
    "message_id": "string",
    "to": "string",
    "subject": "string"
  },
  "permissions": [],
  "secrets": [],
  "timeout": 30000,
  "capabilities": ["outboundGateway"]
}

Built-in skills

Catalog of all skills Curia ships with.

Autonomy engine

How action_risk connects to the autonomy score.