config.json Reference
July 19, 2026 · View on GitHub
This document describes the application configuration file used by the agent server. It is based on the runtime schema in packages/agent-server/src/config.ts and the example at data/config.example.json.
Table of Contents
- Source files
- Environment variables
- Application configuration (config.json)
- Security Notes
- Full Example
Source files
packages/agent-server/src/config.tsdata/config.example.json
Environment variables
Provider API keys (Pi SDK)
Pi SDK chat uses provider-specific environment variables. Common examples include:
- OpenAI:
OPENAI_API_KEY - Anthropic:
ANTHROPIC_OAUTH_TOKEN(preferred) orANTHROPIC_API_KEY - Google Gemini:
GEMINI_API_KEY - Groq:
GROQ_API_KEY - Mistral:
MISTRAL_API_KEY - OpenRouter:
OPENROUTER_API_KEY - XAI:
XAI_API_KEY - Cerebras:
CEREBRAS_API_KEY - Minimax:
MINIMAX_API_KEY - Minimax (CN):
MINIMAX_CN_API_KEY - ZAI:
ZAI_API_KEY - Vercel AI Gateway:
AI_GATEWAY_API_KEY - OpenCode:
OPENCODE_API_KEY - Azure OpenAI:
AZURE_OPENAI_API_KEY(withAZURE_OPENAI_BASE_URL/AZURE_OPENAI_RESOURCE_NAME)
The assistant does not resolve these itself; it passes requests to the Pi SDK, which
reads the provider environment variables directly. For a complete list, see the
@earendil-works/pi-ai README.
Server
| Variable | Default | Description |
|---|---|---|
PORT | 3000 | HTTP/WebSocket server port |
DATA_DIR | ./data | Directory for session data (event logs, preferences, plugin settings) |
APP_CONFIG_PATH | - | Override config file location |
TTS
| Variable | Default | Description |
|---|---|---|
TTS_BACKEND | openai | TTS backend: openai or elevenlabs |
OPENAI_TTS_MODEL | gpt-4o-mini-tts | OpenAI TTS model |
TTS_VOICE | alloy | Voice name for TTS output |
TTS_FRAME_DURATION_MS | 250 | PCM frame duration for TTS output; larger values reduce client scheduling overhead |
AUDIO_OUTPUT_SPEED | - | Playback speed multiplier (e.g., 1.2) |
OpenAI TTS requires OPENAI_API_KEY.
Realtime voice (Android)
Realtime duplex voice reuses the same server-side OpenAI credential as TTS/chat:
| Variable | Default | Description |
|---|---|---|
OPENAI_API_KEY | - | Required for Realtime. Held only on the agent-server host; never sent to Android/web clients. |
OPENAI_REALTIME_MODEL | gpt-realtime-2.1 | Pinned Realtime model id (matches T3). |
OPENAI_REALTIME_VOICE | marin | OpenAI Realtime output voice (matches T3 default; female). |
Storage plan: set OPENAI_API_KEY in the agent-server environment (systemd unit, shell profile, or secrets manager that injects env). Do not put the raw key in client apps, Capacitor assets, or committed config.json. Optional ${OPENAI_API_KEY} substitution in config.json is fine for other agent fields; Realtime reads the env via loadEnvConfig() the same way OpenAI TTS does.
Capability probe: GET /api/voice/capabilities reports agentRealtime.status as ready or not-configured.
Realtime tool allowlist (config.json)
Realtime tool exposure is explicit opt-in. Configure under voice.realtime:
{
"voice": {
"realtime": {
"toolAllowlist": ["lists_*", "web_search"],
"toolDenylist": []
}
}
}
| Field | Description |
|---|---|
toolAllowlist | Glob patterns for tool names available on Realtime sessions. Missing or empty ⇒ no host tools (hangup may still be built-in). |
toolDenylist | Optional globs removed after allowlist matching. |
instructions | Optional system-instruction override for the Realtime model. |
Text agents use per-agent toolAllowlist / toolDenylist independently of this block.
Built-in web_search tool
web_search is a built-in tool (not a plugin). It runs the local Grok CLI headless for live public web and X research.
| Item | Detail |
|---|---|
| Parameters | query (required natural-language question), continue (optional bool to resume prior Grok session for this conversation) |
| Enable for text agents | Add web_search to the agent’s toolAllowlist |
| Enable for Realtime | Add web_search to voice.realtime.toolAllowlist |
| Host prerequisites | grok on PATH (or ASSISTANT_GROK_BIN); Grok auth (~/.grok/auth.json and/or XAI_API_KEY); do not lock off bypassPermissions in Grok requirements; avoid untrusted Grok MCP servers for the service user |
| Not the same as | Plugin search_* tools (in-app notes/lists search) |
Design: docs/design/web-search-tool.md.
ElevenLabs TTS
Used when TTS_BACKEND=elevenlabs.
| Variable | Default | Description |
|---|---|---|
ELEVENLABS_API_KEY | - | ElevenLabs API key (required) |
ELEVENLABS_TTS_VOICE_ID | - | ElevenLabs voice ID (required) |
ELEVENLABS_TTS_MODEL | eleven_multilingual_v2 | ElevenLabs model ID |
ELEVENLABS_TTS_BASE_URL | https://api.elevenlabs.io | API base URL |
MCP Tools
| Variable | Default | Description |
|---|---|---|
MCP_TOOLS_ENABLED | auto | Explicit enable/disable for external MCP servers (true/1 or false/0) |
External MCP servers are configured in config.json under mcpServers.
Rate Limits
Per session, 1-minute sliding window.
| Variable | Default | Description |
|---|---|---|
MAX_MESSAGES_PER_MINUTE | 60 | Max client messages per minute |
MAX_AUDIO_BYTES_PER_MINUTE | 2000000 | Max audio bytes per minute |
MAX_TOOL_CALLS_PER_MINUTE | 30 | Max tool calls per minute |
Audio
| Variable | Default | Description |
|---|---|---|
AUDIO_SAMPLE_RATE | 24000 | Audio sample rate in Hz |
AUDIO_INPUT_MODE | manual | Input mode: server_vad or manual |
AUDIO_TRANSCRIPTION_ENABLED | false | Forward transcription events to client |
Debug
| Variable | Default | Description |
|---|---|---|
DEBUG_CHAT_COMPLETIONS | false | Log Pi SDK request/response payloads (tools included, auth redacted) |
DEBUG_HTTP_REQUESTS | false | Log HTTP request details |
Application configuration (config.json)
Location and Loading
The agent server loads configuration from:
${DATA_DIR}/config.jsonby defaultAPP_CONFIG_PATHwhen set (absolute or relative path)
Environment defaults such as DATA_DIR are described above.
Environment Variable Substitution
config.json supports ${VARNAME} substitution in all string values. This is useful for:
- Paths that depend on home directory:
"workspaceRoot": "${HOME}/workspaces" - API keys and secrets:
"apiKey": "${LOCAL_LLM_KEY}" - Runtime-specific paths:
"socketPath": "${XDG_RUNTIME_DIR}/podman/podman.sock"
If the environment variable is missing, the placeholder is replaced with an empty string.
Examples:
{
"plugins": {
"coding": {
"enabled": true,
"local": {
"workspaceRoot": "${HOME}/coding-workspaces"
}
}
},
"agents": [
{
"agentId": "claude-cli",
"displayName": "Claude",
"description": "Claude via CLI",
"chat": {
"provider": "claude-cli",
"config": {
"workdir": "${HOME}/projects",
"wrapper": {
"path": "${HOME}/bin/claude-wrapper.sh"
}
}
}
}
],
"mcpServers": [
{
"command": "${HOME}/bin/mcp-server",
"env": { "GITHUB_TOKEN": "${GITHUB_TOKEN}" }
}
]
}
Top-Level Keys
| Key | Type | Description |
|---|---|---|
sessions | object | Session cache settings. |
agents | array | Agent persona definitions and chat provider config. |
profiles | array | Shared profile (instance) definitions for cross-plugin scoping. |
plugins | object | Plugin enablement and per-plugin config. |
mcpServers | array | External MCP servers launched over stdio. |
sessions
Controls session cache behavior.
{ "sessions": { "maxCached": 100 } }
maxCached: maximum number of sessions cached in memory
agents
Defines agent personas and chat providers.
{
"agents": [
{
"agentId": "general",
"displayName": "General Assistant",
"description": "A helpful assistant.",
"systemPrompt": "You are a helpful assistant.",
"toolAllowlist": ["*"],
"chat": {
"provider": "pi"
}
}
]
}
See the Agents section below for the full schema and provider-specific config.
profiles
Defines shared profile identifiers that can be reused across plugin instances. Instance ids must
match an entry in this list (the built-in default profile is always available).
{
"profiles": [
{ "id": "default", "label": "Global" },
{ "id": "work", "label": "Work" },
{ "id": "personal", "label": "Personal" }
]
}
Use these ids in plugin instance configuration:
{
"profiles": [{ "id": "default" }, { "id": "work" }],
"plugins": {
"notes": { "enabled": true, "instances": ["default", "work"] }
}
}
plugins
Enable or configure server plugins.
{
"plugins": {
"sessions": { "enabled": true },
"agents": { "enabled": true },
"scheduled-sessions": { "enabled": true },
"lists": { "enabled": true },
"notes": { "enabled": true },
"questions": { "enabled": true },
"url-fetch": { "enabled": true }
}
}
Many plugins accept additional settings. See each plugin README for details.
Plugins that support multiple data instances can also define instances:
{
"plugins": {
"time-tracker": {
"enabled": true,
"instances": ["work", { "id": "personal", "label": "Personal" }]
}
}
}
- Instance entries can be strings or objects. Object entries may include
labeland plugin-specific overrides (either inline or underconfig):
{
"plugins": {
"diff": {
"enabled": true,
"workspaceRoot": "/path/to/workspace",
"instances": [
"work",
{ "id": "oss", "label": "Open Source", "workspaceRoot": "/path/to/oss" },
{ "id": "client", "config": { "workspaceRoot": "/path/to/client" } }
]
}
}
}
- Instance ids are lowercased slugs (
[a-z0-9_-]). - The default instance id is always
defaultand cannot be renamed or removed.
Plugins can opt into automatic git snapshots of their data directories using gitVersioning:
{
"plugins": {
"notes": {
"enabled": true,
"gitVersioning": {
"enabled": true,
"intervalMinutes": 5
}
}
}
}
- One git repository is created per plugin instance directory.
- Snapshots are committed on the configured interval and use a local "AI Assistant" git author.
- Requires
gitavailable on the serverPATH.
Execution Mode (Coding Plugins)
Plugins that execute code (like the coding/terminal plugin) can run in local or sidecar mode.
In local mode, local.workspaceRoot may use the session-scoped macro ${session.workingDir} so
relative file paths and bash commands anchor to the session picker’s working directory.
{
"plugins": {
"coding": {
"enabled": true,
"mode": "local",
"local": {
"workspaceRoot": "/path/to/workspaces"
}
}
}
}
| Field | Type | Description |
|---|---|---|
mode | string | Execution mode. Only local is supported. |
local.workspaceRoot | string | Root directory for local workspaces. ${session.workingDir} resolves from attributes.core.workingDir for interactive tool calls. |
Notes:
- Coding tools now execute directly through the imported
@earendil-works/pi-coding-agentlocal tool implementations.
Agents plugin tools (when enabled)
Agent coordination is provided by the agents plugin. Enable it in config.json and use the generated tools:
| Tool | Description |
|---|---|
agents_message | Send a message to another agent (sync or async) without switching sessions. |
agents_list | List configured agents for delegation or messaging. |
Terminology: users may say "agent" to refer to a configured assistant persona (for example, "journal agent" or "todo agent").
mcpServers
Defines external MCP tool servers (Model Context Protocol) launched over stdio.
{
"mcpServers": [
{
"name": "github",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-github"],
"env": {
"GITHUB_TOKEN": "${GITHUB_TOKEN}"
}
}
]
}
name: optional display namecommand: executable to launchargs: optional arguments listenv: optional environment map (supports${ENV}substitution)
Agents
Common Fields
{
"agentId": "notes",
"displayName": "Notes",
"description": "Manages notes.",
"systemPrompt": "You are a notes assistant.",
"type": "chat",
"toolAllowlist": ["notes_*"],
"toolDenylist": [],
"toolExposure": "skills",
"skillAllowlist": ["notes"],
"sessionWorkingDir": {
"mode": "prompt",
"roots": ["/home/kevin/worktrees"]
},
"skills": [
{ "root": "~/skills", "available": ["*"], "inline": ["my-critical-*"] },
{ "root": "worktrees/assistant/skills" }
],
"capabilityAllowlist": ["*"],
"agentAllowlist": ["*"],
"uiVisible": true,
"apiExposed": false
}
| Field | Type | Description |
|---|---|---|
agentId | string | Unique id (used by tools and session routing). |
displayName | string | UI label. |
description | string | UI description and prompt context. |
type | string | Agent type: chat (default) or external. |
systemPrompt | string | Optional custom prompt. |
toolAllowlist | array | Glob patterns for tool access. |
toolDenylist | array | Glob patterns for tool denylist. |
toolExposure | string | tools, skills, or mixed. |
skillAllowlist | array | Plugin ids exposed as CLI skills. |
skillDenylist | array | Plugin ids blocked from skill exposure. |
skills | array | Instruction skills (filesystem SKILL.md discovery + Pi-style prompt inclusion). |
capabilityAllowlist | array | Glob patterns for capability access. |
capabilityDenylist | array | Glob patterns for capability denylist. |
agentAllowlist | array | Glob patterns for agents this agent can delegate to. |
agentDenylist | array | Glob patterns for agents blocked from delegation. |
sessionWorkingDir | object | Optional working-directory policy for new sessions. Use { "mode": "fixed", "path": "/abs/path" }, { "mode": "prompt", "roots": ["/abs/root"] }, or { "mode": "none" } to leave core.workingDir unset by default. |
uiVisible | boolean | Hide from built-in UI if false. |
apiExposed | boolean | Reserved for external API tools (currently unused). |
Working directory behavior
When sessionWorkingDir is set to { "mode": "prompt", "roots": [...] }, the UI shows a
working directory picker for new sessions created with that agent. The options are the
immediate subdirectories of each configured root (all of which must be absolute paths).
When sessionWorkingDir is set to { "mode": "fixed", "path": "/abs/path" }, new sessions
for that agent automatically use the fixed directory without prompting.
When sessionWorkingDir is set to { "mode": "none" }, new sessions do not get a default
working directory from the agent. This is equivalent to omitting the field and makes the
intent explicit in config.
The selected or fixed directory is stored in attributes.core.workingDir and can be consumed
by other systems such as ${session.workingDir} CLI macros and the coding plugin's local
workspace configuration.
Instruction skills (skills)
The skills field is for instruction skills discovered from SKILL.md files on disk and injected
into the agent's system prompt. This is separate from plugin/manifest-driven “CLI skills” controlled by
toolExposure + skillAllowlist.
Each entry is a “source”:
{
"skills": [
{
"root": "~/skills",
"available": ["*"],
"inline": ["my-critical-*"],
},
],
}
root: directory to recursively scan forSKILL.md.available: glob patterns over skillnameto include in<available_skills>.inline: glob patterns over skillnameto inline as<skill name="...">...</skill>.- If both
availableandinlineare omitted for a source, it defaults toavailable: ["*"].
Context files (contextFiles)
The contextFiles field injects ordinary UTF-8 text files into the agent's system prompt after
instruction skills. This is intended for static project context that should be loaded once at
startup and reused for later prompt rebuilds.
Each entry is a rooted source:
{
"contextFiles": [
{
"root": "./context",
"include": ["README.md", "product/overview.md", "product/policies/*.md", "shared/**/*.md"],
},
],
}
root: directory to search within. Relative roots resolve against the config file directory when the config is loaded.include: root-relative file paths or glob patterns. Matches are ordered by source order, then include-pattern order, then lexical path order within each pattern.- Matches are restricted to files inside the declared root, even when symlinks are involved.
- Duplicate matches are kept only on the first match.
- Startup fails if a root is missing, unreadable, outside-root traversal is detected, an include pattern matches nothing, or a matched file is binary / invalid UTF-8.
- File contents are cached at startup. Changes on disk are not picked up until restart.
External Agents
When type is external, the agent forwards messages to an external HTTP endpoint instead of
using a local chat provider. This is useful for integrating with external AI services or
custom agent implementations.
{
"agentId": "external-example",
"displayName": "External Agent",
"description": "An external agent implementation.",
"type": "external",
"external": {
"inputUrl": "http://external.internal/v1/assistant/input",
"callbackBaseUrl": "http://agent-server.internal"
}
}
| Field | Type | Description |
|---|---|---|
external.inputUrl | string | URL where messages are POSTed to the external agent. |
external.callbackBaseUrl | string | Base URL the external agent uses to call back with responses. |
External agents receive messages via POST and respond asynchronously via callbacks. See
docs/design/external-agents.md for the full protocol specification.
chat Provider Selection
{
"chat": {
"provider": "claude-cli",
"config": {
"workdir": "/path/to/workspace",
"extraArgs": ["--model", "sonnet"]
}
}
}
Supported providers:
pi(default)claude-clicodex-clipi-cli
| Provider | CLI Tool | Description |
|---|---|---|
pi | - | Pi SDK in-process chat (upstream providers configured via Pi). |
claude-cli | claude | Anthropic Claude CLI with tool use. |
codex-cli | codex | OpenAI Codex CLI with file editing and shell. |
pi-cli | pi | Pi CLI agent. |
CLI provider example
{
"agents": [
{
"agentId": "claude-cli",
"displayName": "Claude",
"description": "Claude via CLI",
"chat": {
"provider": "claude-cli",
"config": {
"workdir": "/path/to/workspace",
"extraArgs": ["--model", "sonnet", "--dangerously-skip-permissions"]
}
}
}
]
}
Scheduled sessions
Scheduled sessions are no longer configured on agents. Use the scheduled-sessions plugin to
create and manage them dynamically. They are stored in the plugin data directory under
data/plugins/scheduled-sessions/default/schedules.json.
The same plugin also stores pending one-shot session wake-ups under
data/plugins/scheduled-sessions/default/wakeups.json. Wake-ups are limited to native Pi SDK
sessions (chat.provider: "pi"), target the current tool session for create/list/update/cancel
operations, and send the configured message back to that session at the requested time. If the
session is busy when the wake-up fires, the message is added to the existing per-session message
queue. A session can have up to 25 active wake-ups.
Each persisted schedule record includes:
agentIdscheduleIdcron- optional
prompt - optional
preCheck - optional
sessionTitle - optional
sessionConfig- optional
model - optional
thinking - optional
workingDir - optional
skills
- optional
enabledreuseSession(defaults totrue)maxConcurrent
Notes:
- Each schedule must define
prompt,preCheck, or both. sessionConfigvalues are validated against the selected agent on create/update and revalidated when a scheduled run starts.sessionTitlestays a top-level schedule field; it is not part of scheduledsessionConfig.preCheckruns in the agentchat.config.workdirand uses the wrapper environment when configured.- When
reuseSessionistrue, scheduled runs reuse one backing session peragentId + scheduleId. - Reused scheduled sessions are created up front and reconciled from the schedule on later runs after edits.
- When
reuseSessionisfalse, each run creates a fresh backing session. maxConcurrentonly matters whenreuseSessionisfalse.- Enable the
scheduled-sessionsplugin to create, edit, delete, run, and toggle schedules, and to list/create/update/cancel pending session wake-ups.
Wake-up tools:
scheduled_sessions_wakeup_createmessage- exactly one of
runAtordelaySeconds runAtmust be an absolute ISO timestamp with an explicit timezone offset orZ, for example2026-06-03T08:56:00-05:00or2026-06-03T13:56:00Z
scheduled_sessions_wakeup_updatewakeupId- at least one of
message,runAt, ordelaySeconds - if a time is provided, provide exactly one of
runAtordelaySeconds
scheduled_sessions_wakeup_cancelwakeupId
scheduled_sessions_wakeup_list
Wake-up tools intentionally do not accept a sessionId; they use the current tool context.
update and cancel only operate on wake-ups belonging to the current session. In a session
context, list returns manageable current-session wake-ups with IDs and message details plus
read-only other-session wake-ups with only safe summary fields: kind, scope, manageable,
runAt, status, summary, false capabilities, and an omitted list for redacted fields. The
scheduled-sessions panel uses the same plugin operation without a session context to show all
wake-ups for administration.
pi Provider
{
"chat": {
"provider": "pi",
"models": ["anthropic/claude-sonnet-4-5", "openai-codex/gpt-5.2-codex"],
"thinking": ["off", "low", "medium", "high", "xhigh"],
"config": {
"provider": "anthropic",
"apiKey": "${ANTHROPIC_API_KEY}",
"baseUrl": "https://api.anthropic.com",
"maxTokens": 4096,
"temperature": 0.7,
"maxToolIterations": 100,
"compaction": {
"enabled": true,
"reserveTokens": 16384,
"keepRecentTokens": 20000
},
"headers": {
"X-Request-Source": "assistant"
}
}
}
}
models: optional list of allowed model ids (first is default). Entries may beprovider/model.thinking: optional list of allowed thinking levels (first is default). Selected level is passed to the Pi SDK reasoning option; useoffto disable reasoning.config.provider: default provider used when a model omits a prefix (required if any model omits a prefix).config.apiKey,config.baseUrl,config.headers: optional connection overrides applied when the resolved provider matchesconfig.provider.- When
config.baseUrlis set and the chosen provider has no built-in Pi model catalog entry, the server synthesizes anopenai-responsesmodel for the configuredprovider/modelid. This allows custom endpoints such as local mocks or proxies to be targeted without adding extra config fields. config.maxTokens,config.temperature,config.timeoutMs: optional Pi SDK request overrides.config.contextWindow: optional context window override used for synthesized models (providers without a built-in Pi catalog entry whenconfig.baseUrlis set).config.maxToolIterations: max consecutive tool iterations before aborting with an error (default: 100).config.compaction: optional Pi SDK context compaction controls. Compaction is enabled by default with Pi-compatible defaults:reserveTokens: 16384andkeepRecentTokens: 20000. Manual compaction is available from the chat request history menu for in-process Pi SDK sessions, and automatic threshold compaction runs after completed Pi turns when usage exceedscontextWindow - reserveTokens.
Pi SDK sessions are mirrored to the Pi JSONL format so they can be resumed by the
pi-mono CLI. Sessions are written to:
~/.pi/agent/sessions/<encoded-cwd>/*_<pi-session-id>.jsonl.
The cwd comes from attributes.core.workingDir when available (otherwise the
server working directory).
Canceled runs still write partial assistant/tool entries so the pi-mono CLI can resume.
Pi-backed sessions always persist canonical Pi JSONL history for replay and reload. Compacted Pi
JSONL logs keep the full raw history on disk and add Pi-compatible compaction entries; future
model context is rebuilt as summary plus the recent kept messages.
CLI Providers (claude-cli, codex-cli, pi-cli)
All CLI providers share the same config shape:
{
"chat": {
"provider": "pi-cli",
"models": ["anthropic/claude-sonnet-4-5", "openai-codex/gpt-5.2-codex"],
"thinking": ["off", "low", "medium", "high", "xhigh"],
"config": {
"wrapper": {
"path": "/home/kevin/devtools/container/run.sh",
"env": {
"PERSISTENT": "1",
"PROXY": "1",
"CONTAINER_NAME": "assistant"
}
}
}
}
}
models: optional list of allowed model ids for CLI providers (first is default). Forpi-cli, entries may beprovider/modeland are split into--provider+--model. When set, do not include--modelinextraArgs(and forpi-cli, do not include--provider).thinking: optional list of allowed thinking levels forpi-cliandcodex-cli(first is default). Forcodex-cli, the selected level maps to--config model_reasoning_effort=<level>. When set, do not include--thinkinginextraArgs(pi) ormodel_reasoning_effortoverrides inextraArgs(codex).workdir: optional working directory for the CLI processextraArgs: optional extra CLI flags (reserved flags are managed by the server)wrapper.path: optional wrapper executable used to run the CLI (for containerized runs)wrapper.env: optional environment map for the wrapper (supports${ENV}substitution)
For pi-cli, history is read from the default Pi sessions directory:
~/.pi/agent/sessions/<encoded-cwd>/*_<pi-session-id>.jsonl. The cwd comes from the Pi
session header, so set workdir if you need a stable path across runs. No extra config
is required.
For claude-cli, history is read from the default Claude projects directory:
~/.claude/projects/<encoded-cwd>/<session-id>.jsonl. The CLI defaults to the user home
directory when workdir is not set, so history typically lands under
~/.claude/projects/-home-<user>. Set workdir if you need Claude history under a
different path. No extra config is required.
For codex-cli, history is read from the default Codex sessions directory:
~/.codex/sessions/<yyyy>/<mm>/<dd>/...-<codex-session-id>.jsonl. The session id is
emitted by the CLI and tracked automatically; no extra config is required.
Reserved flags (must not be in extraArgs):
claude-cli:--output-format,--session-id,--resume,-p,--include-partial-messages,--verbosecodex-cli:--json,resumepi-cli:--mode,--session,--session-dir,--continue,-p
When chat.models is set for a CLI provider, --model is managed by the server and must not be included in extraArgs. For pi-cli, --provider is also managed by the server when chat.models is set, and --thinking is managed by the server when chat.thinking is set. For codex-cli, model_reasoning_effort is managed by the server when chat.thinking is set.
Security Notes
- Store API keys in environment variables or a secure secrets manager; avoid committing them to
config.json. - Limit
wrapper.pathusage to trusted scripts or containers because the wrapper runs as the server user. - MCP server commands run as the server user and inherit
envsettings, so review each command and token scope.
Full Example
See data/config.example.json for a complete working example including plugins and agents.