Skip to main content

Personality config reference

A personality is a directory at ~/.ethos/personalities/<id>/ with three files:

FilePurpose
SOUL.mdFirst-person identity prose. Loaded as the system-prompt baseline. Free-form markdown.
config.yamlFlat key: value config — fields documented below. Dotted keys (e.g. fs_reach.read) express nested structure.
toolset.yamlFlat YAML list of tool names this personality is allowed to call.

Source

The schema type lives in packages/types/src/personality.ts (PersonalityConfig). The loader / parser lives in extensions/personalities/src/index.tsparseConfigYaml (flat keys + the safety: nested block) and parseToolsetYaml (the - name list).

The schema is frozen — adding a top-level field requires the personality-schema-change PR label and a bump to .personality-field-count. Internal-only fields (id, soulFile, skillsDirs, metadata) are populated by the loader and are not user-editable.

Minimal example

# ~/.ethos/personalities/researcher/config.yaml
name: Researcher
description: Deep reading and synthesis.
model: claude-opus-4-7
# ~/.ethos/personalities/researcher/toolset.yaml
- read_file
- write_file
- web_search

Memory is always per-personality — each personality reads and writes ~/.ethos/personalities/<id>/MEMORY.md automatically. No configuration field is required.

name

Type: string · Default: title-cased directory id · Required

Human-readable label. Surfaces in ethos personality list, the picker UIs, and the chat header.

name: Engineer Paired

description

Type: string · Default: unset

One-line summary shown in pickers and ethos personality list.

description: Builds and ships features for this repo.

model

Type: string · Default: top-level config.yaml model

Per-personality model override. Used by the LLM provider when this personality drives the turn. Falls back to the global model from ~/.ethos/config.yaml when unset. The wiring layer also honours modelRouting.<id> from config.yaml — both routes converge on the same per-personality model.

model: claude-opus-4-7

provider

Type: string · Default: top-level config.yaml provider

Per-personality provider override. Only meaningful when the wiring layer has the named provider registered.

provider: openrouter

platform

Type: string · Default: unset

Channel binding hint. Recognised values (used by the load-time safety gate): telegram, discord, slack, whatsapp, email. Bound channels combined with safety.approvalMode: off are rejected at config load.

platform: slack

capabilities

Type: comma-separated strings · Default: unset

Free-form capability tags. Surfaces to skill-filtering and adapter routing.

capabilities: read, write, web

streamingTimeoutMs

Type: integer (ms) · Default: AgentLoop default (120000)

Watchdog for the LLM stream. If no chunk arrives within this many milliseconds, the agent aborts the stream and emits an error event. Reset on every chunk — slow-but-progressing streams are unaffected. Thinking-mode personalities (Opus extended thinking) typically need longer; fast personalities (Haiku) can pick tighter.

streamingTimeoutMs: 300000

fs_reach.read / fs_reach.write

Type: comma-separated absolute paths · Default: AgentLoop fallback scope

Per-personality filesystem allowlist for the read_file / write_file tools. The runtime resolves these substitutions at construction time:

TokenResolves to
${ETHOS_HOME}~/.ethos
${self}This personality's id.
${CWD}AgentLoop.workingDir.

When unset, the fallback is:

read: [~/.ethos/personalities/<self>/, ~/.ethos/skills/, ${CWD}]
write: [~/.ethos/personalities/<self>/, ${CWD}]

Paths outside the allowlist surface as a BoundaryError from ScopedStorage and are rendered as a user-facing tool error.

fs_reach.read: ${CWD}, ${ETHOS_HOME}/skills, ${ETHOS_HOME}/personalities/${self}
fs_reach.write: ${CWD}, ${ETHOS_HOME}/personalities/${self}

mcp_servers

Type: space-separated strings · Default: unset (no MCP access)

MCP server names this personality may reach. Server configs live globally in ~/.ethos/mcp.json; this is a per-personality allowlist. Missing or empty means no MCP access — explicit opt-in only.

mcp_servers: github linear

Notes:

  • Manage attachments interactively with ethos personality mcp <id> --attach <name> / --detach <name>.

plugins

Type: space-separated strings · Default: unset (no plugins active)

Plugins attached to this personality. Default-deny: a plugin not listed here is dormant for this personality — its tools, hooks, and injectors do not fire.

plugins: weather invoice-checker

Notes:

  • Manage attachments interactively with ethos personality plugins <id> --attach <id> / --detach <id>.
  • Use ethos plugins (plural) for the global attachment matrix.

budgetCapUsd

Type: float (USD) · Default: unset (no cap)

Per-session spending cap. When the running cost for the current session crosses this value, the next turn is refused with a typed BUDGET_EXCEEDED error. Session-scoped — resets on /new or ethos chat in a different working directory. Override mid-session with /budget reset.

budgetCapUsd: 1.00

context_engine

Type: string · Default: drop_oldest

Context-compaction engine name. Resolved against the runtime's engine registry when the conversation approaches the model's context window. Unknown names fall back to the built-in drop_oldest.

context_engine: summarize_oldest

context_engine_options.*

Type: scalar (string / number / boolean) · Default: unset

Free-form per-engine options. Keys are dotted (context_engine_options.<key>); values are typed automatically — integers, floats, true / false, otherwise strings.

context_engine_options.keep_last_n: 8
context_engine_options.summary_model: claude-haiku-4-5

context_layering.*

Workspace-aware context layering. Controls how AGENTS.md / CLAUDE.md files are discovered as the agent navigates the workspace.

FieldTypeDefaultDescription
context_layering.modestatic | progressive | offstaticstatic loads context once at session start from workingDir. progressive also discovers sub-AGENTS.md as the agent reads / writes files; injected on the next turn. off skips context-file injection entirely.
context_layering.max_depthintegerruntime defaultMaximum directory depth to walk when discovering context files.
context_layering.discovery_filescomma-separated stringsAGENTS.md, CLAUDE.mdFilenames to scan for at each depth.
context_layering.cap_total_charsintegerruntime defaultCap on the total character budget injected.
context_layering.mode: progressive
context_layering.max_depth: 3
context_layering.discovery_files: AGENTS.md, CLAUDE.md, SOUL.md
context_layering.cap_total_chars: 12000

skill_evolution.*

Auto-triggered skill evolution. When enabled: true, the skill-evolver auto-trigger queues an analysis after every turn that crosses the min_tool_calls threshold and is outside the cooldown window. The built-in engineer personality ships with it enabled (min_tool_calls: 5, cooldown_minutes: 60).

FieldTypeDefaultDescription
skill_evolution.enabledbooleanfalseMaster switch. Off by default — opt-in per personality.
skill_evolution.min_tool_callsintegerruntime defaultMinimum tool calls in a turn before evolution runs.
skill_evolution.cooldown_minutesintegerruntime defaultCooldown between evolution runs.
skill_evolution.modelstringtop-level modelOverride which LLM the evolver uses for analysis. Falls back to the personality's model when unset.
skill_evolution.enabled: true
skill_evolution.min_tool_calls: 5
skill_evolution.cooldown_minutes: 60
skill_evolution.model: claude-sonnet-4-6

Notes:

  • The global cron schedule for running the evolver lives in config.yaml (evolver.cron_enabled, evolver.schedule), not here. These personality-level keys control whether a personality participates and the per-turn trigger thresholds.

safety

Per-personality safety config. Unlike the other fields, safety: is a true nested block — YAML indentation matters here.

safety:
approvalMode: manual
observability:
storeToolArgs: redacted
storeToolBodies: redacted
storeLlmPayloads: metadata
redactPatterns:
- sk-ant-
- sk-or-

safety.approvalMode

Type: manual | smart | off · Default: manual

Decides what happens when a tool call is classified dangerous.

ValueBehaviour
manualEvery dangerous classification surfaces the approval modal; safe auto-fires; blocked errors out.
smartAn auxiliary fast-model call reviews each dangerous classification and either auto-approves, auto-denies, or escalates to manual. Trades latency and dollars for reduced approval fatigue.
offdangerous classifications auto-fire without prompting; the hardline blocked floor still applies.

Notes:

  • approvalMode: off paired with any channel ingress (platform: telegram / discord / slack / whatsapp / email) is rejected at config load.

safety.observability.*

Controls what the observability store persists for this personality.

FieldValuesDescription
safety.observability.storeToolArgsnone | redacted | fullTool-call arguments.
safety.observability.storeToolBodiesnone | redacted | fullTool-call result bodies.
safety.observability.storeLlmPayloadsnone | metadata | fullLLM request and response payloads.
safety.observability.redactPatternsstring[]Substrings redacted from anything stored.

toolset.yaml

Flat YAML list of tool names. Each entry on its own line, prefixed with - . Tools missing from this list are filtered out before the LLM sees them.

# ~/.ethos/personalities/researcher/toolset.yaml
- read_file
- write_file
- web_search
- web_extract
- browse_url

Notes:

  • An empty file (or one with only comments) means the personality runs with no external tools. The file may be omitted entirely for an internal-only personality.
  • Tools the personality requests but does not list are rejected by DefaultToolRegistry and returned to the LLM as is_error: true so the Anthropic tool-result contract remains intact.

SOUL.md

The first-person identity file. Markdown, no front-matter required. Loaded as part of the system prompt at every turn — combined with memory context and the dynamic personality config.

The file is mtime-cached by FilePersonalityRegistry.loadFromDirectory(); the loader re-reads it only when the on-disk mtime changes, so editing it during a chat session takes effect on the next turn.

skills/

Optional sibling directory at ~/.ethos/personalities/<id>/skills/. Per-personality skill files (markdown with frontmatter). The universal skill scanner picks them up alongside the global ~/.ethos/skills/ directory. Per-personality skills are always loaded unfiltered; global skills are filtered by capability mode by default.

See also

  • config.yaml reference — the user-level ~/.ethos/config.yaml that picks which personality runs (different file, different schema).
  • CLI reference — the ethos personality subcommands that scaffold and edit these files.
  • Glossary: personality — one-line definition shared across every page that names the construct.
  • Glossary: fs_reach — the path-allowlist field this file declares; backed by ScopedStorage.