Agentic AI Atlasby a5c.ai
OverviewWikiGraphFor AgentsEdgesSearchWorkspace
/
GitHubDocsDiscord
iiRecord
Agentic AI Atlas · Agent Capabilities, Model Registry, and Thinking Normalization
page:docs-adapters-reference-06-capabilities-and-modelsa5c.ai
Search record views/
Record · tabs

Available views

II.Record viewspp. 1 - 1
overviewarticlejsongraph
II.
Page JSON

page:docs-adapters-reference-06-capabilities-and-models

Structured · live

Agent Capabilities, Model Registry, and Thinking Normalization json

Inspect the normalized record payload exactly as the atlas UI reads it.

File · wiki/docs/adapters/reference/06-capabilities-and-models.mdCluster · wiki
Record JSON
{
  "id": "page:docs-adapters-reference-06-capabilities-and-models",
  "_kind": "Page",
  "_file": "wiki/docs/adapters/reference/06-capabilities-and-models.md",
  "_cluster": "wiki",
  "attributes": {
    "nodeKind": "Page",
    "sourcePath": "docs/adapters/reference/06-capabilities-and-models.md",
    "sourceKind": "repo-docs",
    "title": "Agent Capabilities, Model Registry, and Thinking Normalization",
    "displayName": "Agent Capabilities, Model Registry, and Thinking Normalization",
    "slug": "docs/adapters/reference/06-capabilities-and-models",
    "articlePath": "wiki/docs/adapters/reference/06-capabilities-and-models.md",
    "article": "\n# Agent Capabilities, Model Registry, and Thinking Normalization\n\n**Specification v1.0** | `@a5c-ai/adapters`\n\n> **Note:** hermes-agent is included as a 10th supported agent per project requirements, extending the original scope's 9 agents. All ten built-in agents (claude, codex, gemini, copilot, cursor, opencode, pi, omp, openclaw, hermes) share the same capability and model interfaces defined here.\n\n---\n\n## 1. Overview\n\nThis specification defines the capability discovery, model introspection, and thinking normalization systems in adapters. These three concerns are tightly related: capabilities determine what an agent can do, the model registry provides per-model granularity within those capabilities, and thinking normalization translates the unified `thinkingEffort` abstraction into each agent's native parameters.\n\nThe capability system serves two primary roles:\n\n1. **Consumer-side feature-gating.** Consumers query capabilities before invoking features, enabling graceful degradation across agents with different feature sets.\n2. **Pre-spawn validation.** The run engine validates `RunOptions` against capabilities before spawning a subprocess, throwing `CapabilityError` for unsupported operations rather than producing cryptic agent-side failures.\n\n### 1.1 Cross-References\n\n| Type / Concept | Spec | Section |\n|---|---|---|\n| `AgentName`, `BuiltInAgentName` | `01-core-types-and-client.md` | 1.4 |\n| `AgentMuxClient`, `createClient()` | `01-core-types-and-client.md` | 5 |\n| `RunOptions` | `02-run-options-and-profiles.md` | 2 |\n| `RunHandle` | `03-run-handle-and-interaction.md` | 2 |\n| `AgentEvent`, `BaseEvent` | `04-agent-events.md` | 2 |\n| `ErrorCode`, `AgentMuxError`, `CapabilityError` | `01-core-types-and-client.md` | 3.1, 3.2 |\n| `AgentAdapter`, `BaseAgentAdapter` | `05-adapter-system.md` | 2, 4 |\n| `AdapterRegistry` | `05-adapter-system.md` | 5 |\n| `InstalledPlugin`, `PluginInstallOptions`, `PluginSearchOptions`, `PluginListing` | `09-plugin-manager.md` | 2 |\n| `AuthState`, `AuthMethod` | `08-config-and-auth.md` | 9, 10 |\n| `McpServerConfig` | `02-run-options-and-profiles.md` | 4 |\n\n---\n\n## 2. AgentCapabilities Interface\n\nThe `AgentCapabilities` interface is the structured manifest of what an agent adapter can do. Every adapter -- built-in or plugin -- exposes a `capabilities` property of this type. Consumers access it via `adapter.adapters.capabilities(agent)` or directly from the adapter instance.\n\n```typescript\ninterface AgentCapabilities {\n  /** The agent this capability manifest describes. */\n  agent: AgentName\n\n  // ── Session ─────────────────────────────────────────────────────────\n\n  /** Whether the agent can resume a prior session by ID. */\n  canResume: boolean\n\n  /** Whether the agent can fork (branch) an existing session. */\n  canFork: boolean\n\n  /** Whether the agent supports multi-turn conversations within a single run. */\n  supportsMultiTurn: boolean\n\n  /**\n   * How the agent persists session data.\n   * - 'none'      — no persistence (ephemeral runs only)\n   * - 'file'      — flat files (JSON, JSONL)\n   * - 'sqlite'    — SQLite database\n   * - 'in-memory' — in-process memory only, lost on exit\n   */\n  sessionPersistence: 'none' | 'file' | 'sqlite' | 'in-memory'\n\n  // ── Streaming ───────────────────────────────────────────────────────\n\n  /** Whether the agent supports real-time text token streaming. */\n  supportsTextStreaming: boolean\n\n  /** Whether tool call arguments stream incrementally. */\n  supportsToolCallStreaming: boolean\n\n  /** Whether thinking/reasoning tokens stream incrementally. */\n  supportsThinkingStreaming: boolean\n\n  // ── Tool Calling ────────────────────────────────────────────────────\n\n  /** Whether the agent supports native tool/function calling. */\n  supportsNativeTools: boolean\n\n  /** Whether the agent supports the Model Context Protocol. */\n  supportsMCP: boolean\n\n  /** Whether the agent can invoke multiple tools in a single turn. */\n  supportsParallelToolCalls: boolean\n\n  /** Whether tool execution requires explicit user approval by default. */\n  requiresToolApproval: boolean\n\n  /** The approval modes this agent supports. */\n  approvalModes: ('yolo' | 'prompt' | 'deny')[]\n\n  // ── Thinking ────────────────────────────────────────────────────────\n\n  /** Whether the agent supports extended thinking / reasoning mode. */\n  supportsThinking: boolean\n\n  /** The discrete thinking effort levels this agent accepts. */\n  thinkingEffortLevels: ThinkingEffortLevel[]\n\n  /** Whether the agent supports a numeric thinking budget in tokens. */\n  supportsThinkingBudgetTokens: boolean\n\n  // ── Output ──────────────────────────────────────────────────────────\n\n  /** Whether the agent supports JSON-only output mode. */\n  supportsJsonMode: boolean\n\n  /** Whether the agent supports structured output with a schema. */\n  supportsStructuredOutput: boolean\n\n  // ── Skills / Agent Docs ─────────────────────────────────────────────\n\n  /** Whether the agent supports loading skill definitions. */\n  supportsSkills: boolean\n\n  /** Whether the agent supports an agents.md / AGENTS.md doc. */\n  supportsAgentsMd: boolean\n\n  /**\n   * The format skills are loaded in, or null if skills are unsupported.\n   * - 'file'          — single-file skill definitions\n   * - 'directory'     — directory-based skill bundles\n   * - 'npm-package'   — npm-installable skill packages\n   */\n  skillsFormat: 'file' | 'directory' | 'npm-package' | null\n\n  // ── Subagents / Parallelism ─────────────────────────────────────────\n\n  /** Whether the agent can dispatch subagent tasks. */\n  supportsSubagentDispatch: boolean\n\n  /** Whether the agent supports running parallel tasks / tool calls. */\n  supportsParallelExecution: boolean\n\n  /** Maximum number of concurrent parallel tasks, if bounded. */\n  maxParallelTasks?: number\n\n  // ── Interaction ─────────────────────────────────────────────────────\n\n  /** Whether the agent has an interactive REPL mode. */\n  supportsInteractiveMode: boolean\n\n  /** Whether adapters can inject text into the agent's stdin mid-run. */\n  supportsStdinInjection: boolean\n\n  // ── Multimodal ──────────────────────────────────────────────────────\n\n  /** Whether the agent accepts image inputs (screenshots, diagrams). */\n  supportsImageInput: boolean\n\n  /** Whether the agent can produce image outputs. */\n  supportsImageOutput: boolean\n\n  /** Whether the agent accepts file attachments beyond images. */\n  supportsFileAttachments: boolean\n\n  // ── Plugin System ───────────────────────────────────────────────────\n\n  /** Whether this agent has a plugin/extension system. */\n  supportsPlugins: boolean\n\n  /** The kinds of plugins this agent supports. */\n  pluginFormats: PluginFormat[]\n\n  /** Native CLI command to install a plugin, if available. */\n  pluginInstallCmd?: string\n\n  /** Native CLI command to list installed plugins, if available. */\n  pluginListCmd?: string\n\n  /** Native CLI command to uninstall a plugin, if available. */\n  pluginUninstallCmd?: string\n\n  /** Official plugin marketplace / registry URL, if available. */\n  pluginMarketplaceUrl?: string\n\n  /** Native CLI command to search for plugins, if available. */\n  pluginSearchCmd?: string\n\n  /** Where plugins are sourced from (npm, custom registries, etc.). */\n  pluginRegistries: PluginRegistry[]\n\n  // ── Process ─────────────────────────────────────────────────────────\n\n  /** Platforms this agent runs on natively. */\n  supportedPlatforms: ('darwin' | 'linux' | 'win32')[]\n\n  /** Whether the agent requires the working directory to be a git repo. */\n  requiresGitRepo: boolean\n\n  /** Whether the agent requires a pseudo-terminal (PTY) to function. */\n  requiresPty: boolean\n\n  // ── Auth ────────────────────────────────────────────────────────────\n\n  /** Authentication methods this agent supports. */\n  authMethods: AuthMethod[]\n\n  /** File paths (relative to home) where auth tokens/keys are stored. */\n  authFiles: string[]\n\n  // ── Install ─────────────────────────────────────────────────────────\n\n  /** Installation methods for this agent, per platform. See Section 7. */\n  installMethods: InstallMethod[]\n}\n\ntype ThinkingEffortLevel = 'low' | 'medium' | 'high' | 'max'\n```\n\n### 2.1 Capability Access Patterns\n\nCapabilities are accessed in two ways:\n\n1. **Via AdapterRegistry** (the standard consumer path):\n   ```typescript\n   const caps = adapter.adapters.capabilities('claude')\n   if (caps.supportsThinking) {\n     // safe to pass thinkingEffort\n   }\n   ```\n\n2. **Via the adapter instance** (for adapter authors and internal use):\n   ```typescript\n   const adapter = adapter.adapters.get('claude')\n   const caps = adapter.capabilities\n   ```\n\nBoth return the same `AgentCapabilities` instance. The object is immutable at runtime; adapters construct it once during initialization.\n\n---\n\n## 3. Supporting Types: PluginFormat, PluginRegistry, InstallMethod\n\n### 3.1 PluginFormat\n\nDefines the structural kinds of plugins an agent can load.\n\n```typescript\n/**\n * The structural format of a plugin.\n * - 'npm-package'      — an npm-installable package\n * - 'skill-file'       — a single-file skill definition (markdown, YAML, etc.)\n * - 'skill-directory'  — a directory-based skill bundle\n * - 'extension-ts'     — a TypeScript extension (Cursor-style)\n * - 'channel-plugin'   — a messaging channel connector (OpenClaw gateways)\n * - 'mcp-server'       — an MCP server that the agent connects to as a client\n */\ntype PluginFormat =\n  | 'npm-package'\n  | 'skill-file'\n  | 'skill-directory'\n  | 'extension-ts'\n  | 'channel-plugin'\n  | 'mcp-server'\n```\n\n### 3.2 PluginRegistry\n\nDescribes where plugins are sourced from for a given agent.\n\n```typescript\ninterface PluginRegistry {\n  /** Registry identifier (e.g., 'npm', 'openclaw-registry', 'agentskills-hub'). */\n  name: string\n\n  /** Base URL of the registry. */\n  url: string\n\n  /** Whether the registry supports programmatic search. */\n  searchable: boolean\n}\n```\n\n### 3.3 InstallMethod\n\nDescribes a single method for installing an agent on a particular platform.\n\n```typescript\ninterface InstallMethod {\n  /**\n   * Which platform this install method applies to.\n   * 'all' means the method works on darwin, linux, and win32.\n   */\n  platform: 'darwin' | 'linux' | 'win32' | 'all'\n\n  /**\n   * The installation mechanism.\n   * - 'npm'          — global npm install\n   * - 'brew'         — Homebrew formula or cask\n   * - 'gh-extension' — GitHub CLI extension\n   * - 'curl'         — shell installer via curl pipe\n   * - 'winget'       — Windows Package Manager\n   * - 'scoop'        — Scoop package manager\n   * - 'manual'       — manual download / no automated install\n   * - 'pip'          — Python pip/uv install\n   * - 'nix'          — Nix flake\n   */\n  type: 'npm' | 'brew' | 'gh-extension' | 'curl' | 'winget' | 'scoop' | 'manual' | 'pip' | 'nix'\n\n  /** The exact command to run for installation. */\n  command: string\n\n  /** Human-readable notes (e.g., prerequisites, RAM requirements). */\n  notes?: string\n\n  /** A command to run to verify a prerequisite is installed (e.g., 'gh --version'). */\n  prerequisiteCheck?: string\n}\n```\n\n> **Scope extension:** `'pip'` and `'nix'` are added by this spec to support hermes-agent; not in the original scope's 7-value union.\n\n---\n\n## 4. ModelRegistry Interface\n\nThe `ModelRegistry` provides per-agent model introspection. It is accessible via `adapter.models` and exposes methods to list models, query individual model capabilities, validate model identifiers, estimate costs, and refresh model data.\n\nModel data is sourced from two tiers:\n\n1. **Bundled** -- a static snapshot shipped with each adapter, always available offline.\n2. **Remote** -- fetched on demand via `refresh()`, reflecting the latest model availability from the agent's provider.\n\n```typescript\ninterface ModelRegistry {\n  /**\n   * Return all known models for an agent.\n   * Returns bundled data immediately; call refresh() first for latest remote data.\n   */\n  models(agent: AgentName): ModelCapabilities[]\n\n  /**\n   * Return the model catalog for an agent, including default-entry metadata.\n   * Preferred for CLI/TUI listings.\n   */\n  catalog(agent: AgentName): Array<ModelCapabilities & { isDefault: boolean }>\n\n  /**\n   * Return capabilities for a specific model, or null if not found.\n   * Matches on both modelId and modelAlias.\n   */\n  model(agent: AgentName, modelId: string): ModelCapabilities | null\n\n  /**\n   * Return the default model for an agent, or null if the agent\n   * has no configured default.\n   */\n  defaultModel(agent: AgentName): ModelCapabilities | null\n\n  /**\n   * Validate a model identifier against the agent's known model list.\n   * Returns a result indicating whether the model is valid, deprecated,\n   * or unknown, with guidance for corrections.\n   */\n  validate(agent: AgentName, modelId: string): ModelValidationResult\n\n  /**\n   * Estimate the cost (in USD) for a given token usage on a specific model.\n   * Returns 0 if pricing data is unavailable.\n   */\n  estimateCost(\n    agent: AgentName,\n    modelId: string,\n    inputTokens: number,\n    outputTokens: number\n  ): number\n\n  /**\n   * Refresh model data for a specific agent from its remote source.\n   * Updates the in-memory model list; does not persist to disk.\n   */\n  refresh(agent: AgentName): Promise<void>\n\n  /**\n   * Refresh model data for all registered agents.\n   * Runs refresh() calls in parallel.\n   */\n  refreshAll(): Promise<void>\n\n  /**\n   * Return the timestamp of the last successful model data update\n   * for an agent. Returns the bundled data timestamp if refresh()\n   * has never been called.\n   */\n  lastUpdated(agent: AgentName): Date\n}\n```\n\n### 4.1 Cost Estimation\n\nThe `estimateCost` method computes a USD estimate using the model's pricing fields:\n\n```\ncost = (inputTokens / 1_000_000) * inputPricePerMillion\n     + (outputTokens / 1_000_000) * outputPricePerMillion\n```\n\nFor models with thinking pricing, consumers should account for thinking tokens separately:\n\n```typescript\nconst thinkingCost = (thinkingTokens / 1_000_000) * model.thinkingPricePerMillion\n```\n\nCached input tokens use `cachedInputPricePerMillion` when available. The method returns `0` when pricing data is not available for the given model.\n\n---\n\n## 5. ModelCapabilities Interface\n\nEach model known to an adapter is described by a `ModelCapabilities` object. This provides model-level granularity beyond the agent-level `AgentCapabilities`.\n\n```typescript\ninterface ModelCapabilities {\n  /** The agent this model belongs to. */\n  agent: AgentName\n\n  /** The canonical model identifier (e.g., 'claude-opus-4-20250514'). */\n  modelId: string\n\n  /** An optional short alias (e.g., 'opus' for 'claude-opus-4-20250514'). */\n  modelAlias?: string\n\n  /** Human-readable display name (e.g., 'Claude Opus 4'). */\n  displayName: string\n\n  /** Whether this model is deprecated and should not be used for new work. */\n  deprecated: boolean\n\n  /** ISO 8601 date string when the model was deprecated. */\n  deprecatedSince?: string\n\n  /** The recommended replacement model when this model is deprecated. */\n  successorModelId?: string\n\n  /** Maximum context window size in tokens. */\n  contextWindow: number\n\n  /** Maximum output tokens the model can generate in a single response. */\n  maxOutputTokens: number\n\n  /** Maximum thinking/reasoning tokens, if the model supports thinking. */\n  maxThinkingTokens?: number\n\n  /** Cost per million input tokens in USD. */\n  inputPricePerMillion?: number\n\n  /** Cost per million output tokens in USD. */\n  outputPricePerMillion?: number\n\n  /** Cost per million thinking tokens in USD. */\n  thinkingPricePerMillion?: number\n\n  /** Cost per million cached input tokens in USD. */\n  cachedInputPricePerMillion?: number\n\n  /** Whether this model supports extended thinking / reasoning mode. */\n  supportsThinking: boolean\n\n  /** The discrete thinking effort levels this model accepts. */\n  thinkingEffortLevels?: ThinkingEffortLevel[]\n\n  /** The valid range [min, max] for thinkingBudgetTokens on this model. */\n  thinkingBudgetRange?: [number, number]\n\n  /** Whether this model supports tool/function calling. */\n  supportsToolCalling: boolean\n\n  /** Whether this model supports parallel tool calls in a single turn. */\n  supportsParallelToolCalls: boolean\n\n  /** Whether tool call arguments stream incrementally on this model. */\n  supportsToolCallStreaming: boolean\n\n  /** Whether this model supports JSON-only output mode. */\n  supportsJsonMode: boolean\n\n  /** Whether this model supports structured output with a schema. */\n  supportsStructuredOutput: boolean\n\n  /** Whether this model supports real-time text token streaming. */\n  supportsTextStreaming: boolean\n\n  /** Whether thinking/reasoning tokens stream on this model. */\n  supportsThinkingStreaming: boolean\n\n  /** Whether this model accepts image inputs. */\n  supportsImageInput: boolean\n\n  /** Whether this model can produce image outputs. */\n  supportsImageOutput: boolean\n\n  /** Whether this model accepts file inputs beyond images. */\n  supportsFileInput: boolean\n\n  /** Normalized provider family backing this entry. */\n  provider?: string\n\n  /** Provider-native model identifier when it differs from modelId. */\n  providerModelId?: string\n\n  /** Normalized request protocol used by the adapter for this model. */\n  protocol?: 'chat' | 'responses' | 'messages' | 'custom'\n\n  /** Typical deployment path for the adapter/model combination. */\n  deployment?: 'hosted' | 'local' | 'gateway' | 'hybrid'\n\n  /** Whether the adapter can route this model entry to local backends. */\n  supportsLocalModels?: boolean\n\n  /**\n   * The CLI argument key used to select this model\n   * (e.g., '--model', '-m', '--provider').\n   */\n  cliArgKey: string\n\n  /**\n   * The CLI argument value passed with cliArgKey\n   * (e.g., 'claude-opus-4-20250514', 'o3').\n   */\n  cliArgValue: string\n\n  /** ISO 8601 timestamp of the last update to this model's data. */\n  lastUpdated: string\n\n  /** Whether this data comes from a bundled snapshot or a remote refresh. */\n  source: 'bundled' | 'remote'\n}\n```\n\n### 5.1 Model vs. Agent Capability Resolution\n\nWhen both agent-level and model-level capabilities exist for the same feature, the model-level value takes precedence. For example:\n\n- `AgentCapabilities.supportsThinking` may be `true` for an agent that has at least one thinking-capable model, but `ModelCapabilities.supportsThinking` for a specific model may be `false`.\n- The run engine validates against the **model-level** capability when a model is specified, and falls back to agent-level only when no model is specified.\n\nThis is particularly relevant for agents like Cursor, OpenCode, Pi, and OpenClaw where thinking support is model-dependent.\n\n---\n\n## 6. ModelValidationResult\n\nReturned by `ModelRegistry.validate()` to give consumers detailed feedback on model identifier validity.\n\n```typescript\ninterface ModelValidationResult {\n  /** Whether the model identifier is recognized and usable. */\n  valid: boolean\n\n  /** The validated model's capabilities, if valid. */\n  model?: ModelCapabilities\n\n  /**\n   * Validation status:\n   * - 'ok'          — model is valid and current\n   * - 'deprecated'  — model is valid but deprecated; see successorModelId\n   * - 'alias'       — an alias was provided; resolved to canonical modelId\n   * - 'unknown'     — model identifier not found\n   * - 'ambiguous'   — model identifier matches multiple models\n   */\n  status: 'ok' | 'deprecated' | 'alias' | 'unknown' | 'ambiguous'\n\n  /** Human-readable message explaining the result. */\n  message: string\n\n  /** Suggested model identifiers when status is 'unknown' or 'ambiguous'. */\n  suggestions?: string[]\n\n  /** The canonical model ID when status is 'alias'. */\n  resolvedModelId?: string\n\n  /** The recommended successor when status is 'deprecated'. */\n  successorModelId?: string\n}\n```\n\n### 6.1 Validation Behavior\n\n- When `status` is `'alias'`, `valid` is `true` and `resolvedModelId` contains the canonical identifier.\n- When `status` is `'deprecated'`, `valid` is `true` (the model still works) but `successorModelId` indicates the recommended migration target.\n- When `status` is `'unknown'`, the registry performs fuzzy matching and populates `suggestions` with the closest matches (up to 5).\n- When `status` is `'ambiguous'`, `suggestions` contains all matching candidates.\n\n---\n\n## 7. Install Metadata Per Built-in Adapter\n\nEach built-in adapter declares an `installMethods` array in its capabilities. These are consumed by `adapters install <agent>` and the `AdapterRegistry.installInstructions()` API.\n\n### 7.1 Claude Code\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g @anthropic-ai/claude-code` |\n| darwin | brew | `brew install claude-code` |\n\n### 7.2 Codex CLI\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g @openai/codex` |\n\n### 7.3 Gemini CLI\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g @google/gemini-cli` |\n\n### 7.4 GitHub Copilot CLI\n\n| Platform | Type | Command | Notes |\n|---|---|---|---|\n| all | gh-extension | `gh extension install github/gh-copilot` | Requires GitHub CLI (gh). Install with: `brew install gh` / `winget install GitHub.cli` |\n\nPrerequisite check: `gh --version`\n\n### 7.5 Cursor\n\n| Platform | Type | Command | Notes |\n|---|---|---|---|\n| darwin | manual | `open https://cursor.sh/download` | macOS .dmg installer |\n| linux | manual | `open https://cursor.sh/download` | AppImage |\n| win32 | winget | `winget install Cursor.Cursor` | No headless-only install. Full app required even for CLI use. |\n\n### 7.6 OpenCode\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g opencode` |\n| darwin | brew | `brew install opencode/tap/opencode` |\n\n### 7.7 Pi\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g @earendil-works/pi-coding-agent` |\n\n### 7.8 omp (oh-my-pi)\n\n| Platform | Type | Command |\n|---|---|---|\n| all | npm | `npm install -g @oh-my-pi/pi-coding-agent` |\n\n### 7.9 OpenClaw\n\n| Platform | Type | Command | Notes |\n|---|---|---|---|\n| all | npm | `npm install -g openclaw` | Requires Node 22.16+, 16GB RAM minimum |\n\n### 7.10 Hermes\n\n| Platform | Type | Command | Notes |\n|---|---|---|---|\n| all | pip | `pip install hermes-agent` | Requires Python >= 3.11 |\n| all | curl | `curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh \\| bash` | Shell installer |\n| all | nix | `nix run github:NousResearch/hermes-agent` | Nix flake available |\n\n---\n\n## 8. Thinking Effort Normalization\n\nThe unified `thinkingEffort` enum (`'low' | 'medium' | 'high' | 'max'`) is translated by each adapter into the agent's native thinking parameters before spawning. This section defines the complete per-adapter translation table and the override merge behavior.\n\n### 8.1 Per-Adapter Translation Table\n\n| Agent | low | medium | high | max | Native Parameter |\n|---|---|---|---|---|---|\n| Claude Code | `budget_tokens: 1024` | `budget_tokens: 8192` | `budget_tokens: 32768` | `budget_tokens: max_budget_tokens` | `--thinking-budget` or `budget_tokens` in API |\n| Codex CLI | `'low'` | `'medium'` | `'high'` | `'high'` | `--reasoning` flag; max maps to high (no separate max tier) |\n| Gemini CLI | `thinkingBudget: 1024` | `thinkingBudget: 8192` | `thinkingBudget: 32768` | `thinkingBudget: max` | `thinkingConfig.thinkingBudget` equivalents |\n| Copilot CLI | n/a | n/a | n/a | n/a | No thinking support |\n| Cursor | model-dependent | model-dependent | model-dependent | model-dependent | Passed as provider-level parameter when model supports thinking |\n| OpenCode | model-dependent | model-dependent | model-dependent | model-dependent | Passed as provider-level parameter when model supports thinking |\n| Pi | model-dependent | model-dependent | model-dependent | model-dependent | Passed as provider-level parameter when model supports thinking |\n| omp | `budget_tokens: 1024` | `budget_tokens: 8192` | `budget_tokens: 32768` | `budget_tokens: max_budget_tokens` | `budget_tokens` passed to provider (always supported; not model-dependent) |\n| OpenClaw | model-dependent | model-dependent | model-dependent | model-dependent | Passed as provider-level parameter when model supports thinking |\n| Hermes | n/a | n/a | n/a | n/a | No explicit extended-thinking mode; focus is on skill creation and procedural learning |\n\nFor agents marked \"model-dependent,\" the adapter inspects the selected model's `ModelCapabilities.supportsThinking` and `thinkingEffortLevels` to determine whether and how to pass the parameter. When the model does support thinking, the adapter translates the effort level to the appropriate provider-level parameter for the underlying model provider (e.g., Anthropic budget_tokens, OpenAI reasoning_effort).\n\n### 8.2 thinkingBudgetTokens Pass-Through\n\nThe `thinkingBudgetTokens` field in `RunOptions` provides direct numeric control. When specified:\n\n- **Claude Code:** Passed directly as `budget_tokens`.\n- **Codex CLI:** Ignored (Codex uses discrete levels only).\n- **Gemini CLI:** Passed as `thinkingConfig.thinkingBudget`.\n- **omp:** Passed directly as `budget_tokens` (always supported, unconditionally).\n- **Model-dependent agents (Cursor, OpenCode, Pi, OpenClaw):** Passed through to the provider when the model supports `supportsThinkingBudgetTokens`.\n- **Copilot CLI, Hermes:** Ignored (no thinking support).\n\n### 8.3 thinkingOverride Merge Behavior\n\nThe `thinkingOverride` field in `RunOptions` is an escape hatch for full native control. The merge order is:\n\n1. The adapter computes normalized parameters from `thinkingEffort` and/or `thinkingBudgetTokens`.\n2. `thinkingOverride` is shallow-merged **on top** of the normalized parameters.\n3. The merged result is passed to the agent's native CLI flags or configuration.\n\nThis means `thinkingOverride` always wins. Example:\n\n```typescript\n// RunOptions\n{\n  agent: 'claude',\n  thinkingEffort: 'high',           // → budget_tokens: 32768\n  thinkingOverride: {\n    budget_tokens: 50000,           // overrides the 32768\n    some_future_param: true         // passed through as-is\n  }\n}\n// Final native params: { budget_tokens: 50000, some_future_param: true }\n```\n\n### 8.4 CapabilityError on Unsupported Thinking\n\nSetting `thinkingEffort` or `thinkingBudgetTokens` in `RunOptions` when the selected model has `supportsThinking: false` throws a `CapabilityError` **before spawning** the subprocess. This is a synchronous validation in the run engine.\n\n```typescript\n// From 01-core-types-and-client.md Section 3.2\nclass CapabilityError extends AgentMuxError {\n  readonly code = 'CAPABILITY_ERROR'\n  readonly agent: AgentName\n  readonly capability: string  // e.g. 'thinking', 'jsonMode', 'plugins'\n}\n```\n\nThe error is thrown in the following cases:\n\n| Condition | CapabilityError message |\n|---|---|\n| `thinkingEffort` set, agent `supportsThinking: false` | `Agent '{agent}' does not support thinking/reasoning mode` |\n| `thinkingEffort` set, model `supportsThinking: false` | `Model '{modelId}' on agent '{agent}' does not support thinking` |\n| `thinkingEffort` value not in model's `thinkingEffortLevels` | `Model '{modelId}' does not support thinking effort level '{level}'` |\n| `thinkingBudgetTokens` set, agent/model `supportsThinkingBudgetTokens: false` | `Agent '{agent}' does not support numeric thinking budget` |\n| `thinkingBudgetTokens` outside model's `thinkingBudgetRange` | `Thinking budget {n} is outside valid range [{min}, {max}] for model '{modelId}'` |\n\n---\n\n## 9. Plugin Support Matrix Per Agent\n\nThis table documents the plugin capabilities for each of the ten built-in agents.\n\n| Agent | supportsPlugins | Format(s) | Registry | Marketplace URL |\n|---|---|---|---|---|\n| Claude Code | partial | skill-directory, mcp-server | -- (via `--add-dir`) | -- |\n| Codex CLI | no | -- | -- | -- |\n| Gemini CLI | no | -- | -- | -- |\n| Copilot CLI | no | -- | -- | -- |\n| Cursor | yes | extension-ts, mcp-server | cursor.sh/extensions | `https://cursor.sh/extensions` |\n| OpenCode | yes | npm-package, skill-file, mcp-server | npm (opencode-*) | `https://www.npmjs.com/search?q=opencode-` |\n| Pi | yes | npm-package, skill-file | npm (@mariozechner/pi-*) | `https://www.npmjs.com/search?q=%40mariozechner%2Fpi-` |\n| omp | yes | npm-package, skill-file | npm (@oh-my-pi/*) | `https://www.npmjs.com/search?q=%40oh-my-pi%2F` |\n| OpenClaw | yes | npm-package, skill-file, channel-plugin | npm + openclaw.ai/plugins | `https://openclaw.ai/plugins` |\n| Hermes | yes | skill-file, skill-directory, mcp-server | agentskills.io | `https://agentskills.io` |\n\n### 9.1 Notes on Hermes Plugin Support\n\nHermes has a rich plugin and skills ecosystem:\n\n- **Built-in plugins:** `context_engine` and `memory` plugins in the `plugins/` directory.\n- **Skills system:** User-created skills stored in `~/.hermes/skills/`; skills are auto-generated after complex tasks and self-improving during use.\n- **Skills Hub:** Compatible with the agentskills.io open standard.\n- **Optional skills:** Additional skills available in the `optional-skills/` directory.\n- **MCP integration:** Full MCP client integration; can connect to any MCP server. Also runs as an MCP server via `mcp_serve.py` and the `hermes-acp` entry point.\n\n---\n\n## 10. Built-in Adapters Summary\n\nThis table provides a complete at-a-glance summary of all ten built-in adapters and their key characteristics.\n\n| Adapter | CLI | Session | Stream | Fork | Thinking | MCP | Skills | Plugins | ACP | Platforms |\n|---|---|---|---|---|---|---|---|---|---|---|\n| Claude Code | `claude` | JSONL | yes | yes | yes | yes | yes | partial | no | darwin, linux, win32 |\n| Codex CLI | `codex` | JSONL | yes | no | yes | yes | no | no | no | darwin, linux, win32 |\n| Gemini CLI | `gemini` | JSONL | yes | no | yes | yes | no | no | no | darwin, linux, win32 |\n| Copilot CLI | `copilot` | JSON | yes | no | no | no | no | no | no | darwin, linux, win32 |\n| Cursor | `cursor` | SQLite | partial | no | model-dep | yes | no | yes | no | darwin, linux, win32 |\n| OpenCode | `opencode` | SQLite | yes | yes | model-dep | yes | yes | yes | yes | darwin, linux, win32 |\n| Pi | `pi` | JSONL tree | yes | yes | model-dep | no | yes | yes | yes | darwin, linux, win32 |\n| omp | `omp` | JSONL tree | yes | yes | yes | no | yes | yes | yes | darwin, linux, win32 |\n| OpenClaw | `openclaw` | JSON | partial | no | model-dep | yes | yes | yes | no | darwin, linux, win32 |\n| Hermes | `hermes` | SQLite | yes | no | no | yes | yes | yes | yes | darwin, linux (WSL2 on win32) |\n\n### 10.1 Column Definitions\n\n| Column | Meaning |\n|---|---|\n| **CLI** | The command-line binary name used to invoke the agent |\n| **Session** | Native session storage format |\n| **Stream** | Real-time text streaming support; \"partial\" means some output types buffer |\n| **Fork** | Whether the agent can branch an existing session into a new one |\n| **Thinking** | Extended thinking/reasoning support; \"model-dep\" means only certain models support it |\n| **MCP** | Model Context Protocol (tool server) support |\n| **Skills** | Whether the agent supports loading skill definitions |\n| **Plugins** | Whether the agent has a plugin/extension system; \"partial\" means limited |\n| **ACP** | Agent Communication Protocol support |\n| **Platforms** | Natively supported operating systems |\n\n### 10.2 Hermes Adapter Notes\n\nHermes (hermes-agent by NousResearch) is the 10th built-in adapter, extending the original scope's 9 agents. Key characteristics:\n\n- **CLI binary:** `hermes` (primary), `hermes-agent` (run agent), `hermes-acp` (ACP adapter).\n- **Multi-provider:** Routes through OpenRouter (200+ models), Anthropic, OpenAI, Nous Portal, GitHub Copilot, Gemini, and any OpenAI-compatible endpoint (LM Studio, Ollama, vLLM, llama.cpp). Default model: `anthropic/claude-opus-4.6`.\n- **Session/Memory:** Persistent memory with agent-curated entries in `~/.hermes/`; FTS5-based session search with LLM summarization for cross-session recall.\n- **Configuration:** YAML-based (`~/.hermes/cli-config.yaml`); env vars override YAML.\n- **Auth:** API keys via env vars or config YAML; OAuth via `hermes login` for Nous Portal and OpenAI Codex; GitHub token for Copilot; command approval/allowlist security model.\n- **Platform:** Linux, macOS, WSL2, Android (Termux). Windows requires WSL2.\n- **Terminal backends:** Local, Docker, SSH, Daytona, Modal, Singularity.\n- **Messaging gateways:** Telegram, Discord, Slack, WhatsApp, Signal, Email, Matrix, Home Assistant.\n- **40+ built-in tools** across multiple domains.\n- **No explicit extended-thinking mode.** The agent focuses on skill creation and procedural learning rather than chain-of-thought reasoning tokens.\n- **Python-based** (Python >= 3.11 required); installed via pip, shell installer, or Nix.\n\n---\n\n## 11. Capability-Gated Validation Rules\n\nThe run engine validates `RunOptions` against capabilities before subprocess creation. This section enumerates all capability-gated validations.\n\n| RunOptions Field | Required Capability | CapabilityError Code |\n|---|---|---|\n| `thinkingEffort` | `supportsThinking: true` on agent or model | `CAPABILITY_ERROR` |\n| `thinkingBudgetTokens` | `supportsThinkingBudgetTokens: true` | `CAPABILITY_ERROR` |\n| `outputFormat: 'json'` | `supportsJsonMode: true` | `CAPABILITY_ERROR` |\n| `sessionId` (resume) | `canResume: true` | `CAPABILITY_ERROR` |\n| `forkSessionId` | `canFork: true` | `CAPABILITY_ERROR` |\n| `skills` | `supportsSkills: true` | `CAPABILITY_ERROR` |\n| `mcpServers` | `supportsMCP: true` | `CAPABILITY_ERROR` |\n| `stream: true` | `supportsTextStreaming: true` | `CAPABILITY_ERROR` |\n| `attachments` (images) | `supportsImageInput: true` | `CAPABILITY_ERROR` |\n| `attachments` (files) | `supportsFileAttachments: true` | `CAPABILITY_ERROR` |\n| Plugin operations | `supportsPlugins: true` | `CAPABILITY_ERROR` |\n\nWhen `stream: 'auto'` is used, the engine silently falls back to buffered emission for unsupported streaming types and emits a `stream_fallback` event (see `04-agent-events.md` Section 20). No `CapabilityError` is thrown in auto mode.\n\n---\n\n## 12. Agent Capability Profiles (Complete Reference)\n\nThis section provides the full `AgentCapabilities` profile for each built-in adapter as a reference for implementors.\n\n### 12.1 Claude Code\n\n```\nsession:     canResume=true, canFork=true, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true, effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=true, structuredOutput=true\nskills:      supportsSkills=true, supportsAgentsMd=true, skillsFormat='directory'\nsubagents:   supportsSubagentDispatch=true, supportsParallelExecution=true\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=true\nplugins:     supportsPlugins=true (partial), pluginFormats=['skill-directory','mcp-server']\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key','oauth'], authFiles=['~/.claude/.credentials']\n```\n\n### 12.2 Codex CLI\n\n```\nsession:     canResume=false, canFork=false, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true, effortLevels=['low','medium','high'], budgetTokens=false\noutput:      jsonMode=true, structuredOutput=false\nskills:      supportsSkills=false, supportsAgentsMd=false, skillsFormat=null\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=false\nplugins:     supportsPlugins=false\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key'], authFiles=['~/.codex/auth.json']\n```\n\n### 12.3 Gemini CLI\n\n```\nsession:     canResume=false, canFork=false, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true, effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=false, supportsAgentsMd=false, skillsFormat=null\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=true, fileAttachments=true\nplugins:     supportsPlugins=false\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key','oauth'], authFiles=['~/.config/gemini/credentials.json']\n```\n\n### 12.4 Copilot CLI\n\n```\nsession:     canResume=false, canFork=false, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=false, thinking=false\ntools:       nativeTools=false, mcp=false, parallelToolCalls=false, requiresToolApproval=false, approvalModes=['prompt']\nthinking:    supportsThinking=false, effortLevels=[], budgetTokens=false\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=false, supportsAgentsMd=false, skillsFormat=null\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=false, imageOutput=false, fileAttachments=false\nplugins:     supportsPlugins=false\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['oauth','github-token'], authFiles=['~/.config/github-copilot/hosts.json']\n```\n\n### 12.5 Cursor\n\n```\nsession:     canResume=false, canFork=false, supportsMultiTurn=true, sessionPersistence='sqlite'\nstreaming:   text=true (partial), toolCall=false, thinking=false\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true (model-dependent), effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=false, supportsAgentsMd=false, skillsFormat=null\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=false\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=true\nplugins:     supportsPlugins=true, pluginFormats=['extension-ts','mcp-server'], registries=[{name:'cursor.sh/extensions'}]\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=true\nauth:        methods=['oauth'], authFiles=['~/.cursor/auth.json']\n```\n\n### 12.6 OpenCode\n\n```\nsession:     canResume=true, canFork=true, supportsMultiTurn=true, sessionPersistence='sqlite'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true (model-dependent), effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=true, structuredOutput=true\nskills:      supportsSkills=true, supportsAgentsMd=true, skillsFormat='file'\nsubagents:   supportsSubagentDispatch=true, supportsParallelExecution=true\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=true\nplugins:     supportsPlugins=true, pluginFormats=['npm-package','skill-file','mcp-server'], registries=[{name:'npm', searchable:true}]\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key'], authFiles=['~/.config/opencode/auth.json']\n```\n\n### 12.7 Pi\n\n```\nsession:     canResume=true, canFork=true, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=false, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true (model-dependent), effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=true, supportsAgentsMd=false, skillsFormat='file'\nsubagents:   supportsSubagentDispatch=true, supportsParallelExecution=true\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=false\nplugins:     supportsPlugins=true, pluginFormats=['npm-package','skill-file'], registries=[{name:'npm', searchable:true}]\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key'], authFiles=['~/.pi/agent/auth.json']\n```\n\n### 12.8 omp (oh-my-pi)\n\n```\nsession:     canResume=true, canFork=true, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true, toolCall=true, thinking=true\ntools:       nativeTools=true, mcp=false, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true, effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=true, supportsAgentsMd=false, skillsFormat='file'\nsubagents:   supportsSubagentDispatch=true, supportsParallelExecution=true\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=false\nplugins:     supportsPlugins=true, pluginFormats=['npm-package','skill-file'], registries=[{name:'npm', searchable:true}]\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key'], authFiles=['~/.omp/agent/auth.json']\n```\n\n### 12.9 OpenClaw\n\n```\nsession:     canResume=false, canFork=false, supportsMultiTurn=true, sessionPersistence='file'\nstreaming:   text=true (partial), toolCall=false, thinking=false\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=true (model-dependent), effortLevels=['low','medium','high','max'], budgetTokens=true\noutput:      jsonMode=true, structuredOutput=false\nskills:      supportsSkills=true, supportsAgentsMd=true, skillsFormat='file'\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=true, imageOutput=false, fileAttachments=true\nplugins:     supportsPlugins=true, pluginFormats=['npm-package','skill-file','channel-plugin'], registries=[{name:'npm', searchable:true},{name:'openclaw-registry', url:'https://openclaw.ai/plugins', searchable:true}]\nprocess:     platforms=['darwin','linux','win32'], requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key'], authFiles=['~/.openclaw/auth.json']\n```\n\n### 12.10 Hermes\n\n```\nsession:     canResume=true, canFork=false, supportsMultiTurn=true, sessionPersistence='sqlite'\nstreaming:   text=true, toolCall=true, thinking=false\ntools:       nativeTools=true, mcp=true, parallelToolCalls=true, requiresToolApproval=true, approvalModes=['yolo','prompt','deny']\nthinking:    supportsThinking=false, effortLevels=[], budgetTokens=false\noutput:      jsonMode=false, structuredOutput=false\nskills:      supportsSkills=true, supportsAgentsMd=false, skillsFormat='directory'\nsubagents:   supportsSubagentDispatch=false, supportsParallelExecution=false\ninteraction: interactiveMode=true, stdinInjection=true\nmultimodal:  imageInput=false, imageOutput=false, fileAttachments=false\nplugins:     supportsPlugins=true, pluginFormats=['skill-file','skill-directory','mcp-server'], registries=[{name:'agentskills-hub', url:'https://agentskills.io', searchable:true}]\nprocess:     platforms=['darwin','linux'] (win32 via WSL2 only), requiresGitRepo=false, requiresPty=false\nauth:        methods=['api-key','oauth'], authFiles=['~/.hermes/.env','~/.hermes/cli-config.yaml']\n```\n\n---\n\n## Implementation Status (2026-04-12)\n\nNo behavioural changes to capabilities or models. The `adapters-remote` adapter (spec 12) declares capabilities that reflect the *transport delegate* rather than a specific harness; callers must read capabilities from the adapter they nest into.\n",
    "documents": []
  },
  "outgoingEdges": [],
  "incomingEdges": [
    {
      "from": "page:docs-adapters-reference",
      "to": "page:docs-adapters-reference-06-capabilities-and-models",
      "kind": "contains_page"
    }
  ]
}

Shortcuts

Back to overview
Open graph tab