# Example plyngent configuration (no real secrets). # Copy to the path shown by: plyngent config path # Then set access_key_or_token and adjust models. # Optional. If omitted (or SQLite url unset/empty), the CLI uses a durable file under # the platform user data dir (e.g. ~/.local/share/plyngent/chat.db). # url = ":memory:" → true in-memory SQLite (CLI warns; useful for tests). # [database] # implementation = "sqlite" # url = "/path/to/chat.db" # # url = ":memory:" # Third-party plugins (entry-point names). Default: load none. # Today the CLI loads allowlisted ``plyngent.tools`` entry points; the section is # not tool-specific so other extension points can share the same allowlist later. # See doc/plugins.md. # [plugins] # enable = ["acme"] # enable = ["*"] # disable = ["legacy"] # Host network / SSRF policy for tools such as ``fetch``. # Clash TUN + Fake-IP often resolves public names to 198.18.0.0/15; list that # pool so fetch does not treat every site as private/loopback. # Metadata (169.254.169.254) stays forbidden even if listed. # [networking] # ssrf_assume_public_cidrs = ["198.18.0.0/15"] # Skills: instruction directories holding a SKILL.md (YAML frontmatter with at # least name/description, then a Markdown body) plus optional bundled files. # Discovery roots in priority order: explicit `paths`, our own user config # directory (/skills), other harnesses' skill directories # (~/.claude/skills; plus /.claude/skills when include_project_roots). # The catalog (name + description) is folded into the system prompt when tools # are on; the model reads bodies with skill_read and searches with skill_search. # Reading needs no grant (the roots are read-only roots); creating or editing a # skill asks once/session, or stands with allow_write = true. See doc/skills.md. # [skills] # enabled = true # paths = ["~/team-skills", "/srv/skills"] # discover = ["plyngent", "claude"] # include_project_roots = true # inject_catalog = true # max_catalog_skills = 50 # allow_write = false # MCP (Model Context Protocol) servers exposed as agent tools. # Defining a server opts it in; tools appear as mcp____ with # LOCAL tags (confirm flow applies). Names in disable stay disconnected. # A server's initialize ``instructions`` (usage guidance) is folded into the # agent system prompt when tools are on ([agent] mcp_instructions = false # disables; /mcp previews what each server sent). # [mcp.servers.docs] # command = "npx" # args = ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] # env = { RUST_LOG = "info" } # cwd = "/path" # spawn working directory (empty = inherit) # timeout = 30.0 # per-request timeout (seconds) # read_only = true # mark every tool READ_ONLY (eligible for /btw --tools=read) # [mcp] # disable = ["docs"] # defined but never connected [agent] # Persona (omit → built-in coding-agent line). tool_directives is the tool playbook. # Override with multi-line literals (prefer ''' so nested " is fine): # system_prompt = ''' # Your custom persona... # ''' # tool_directives = ''' # ### Workspace # ... # ''' # system_prompt = "" # persona off; keep default tool_directives unless also cleared # tool_directives = "" # playbook off (set both "" to disable system entirely) max_tool_result_chars = 32000 parallel_tools = true confirm_destructive = true path_denylist = ["/secrets/", ".ssh/", ".gnupg/"] # Static out-of-workspace pre-allow (path → read|write|exec); never prompts. # The model can also request access at runtime via request_directory_access # (human approves; /grants lists or revokes). Denylist still applies, and # grants are a tool-policy gate, not a sandbox (commands reach the FS). # allow_paths = { "/data/datasets" = "read", "/tmp/build" = "exec" } # Auto-raise tool/PTY limits without prompting (same as answering yyy). # auto_continue_limits = false # Soft context budget in estimated tokens (~4 chars/token when API usage missing). max_context_tokens = 200000 # Fold connected MCP servers' initialize ``instructions`` (usage guidance) # into the agent system prompt when tools are on. false = ignore server text. # mcp_instructions = true # How open-todo nags are injected into model context: # developer (default) | user | synthetic_tool | none # (legacy system is treated as developer) # todo_nag_strategy = "developer" # Append-only developer playbook checkpoints every N prompt_tokens (0 = off). # directive_reminder_tokens = 100000 # directive_reminder_text = "" # Thinking strength, resolved as [agent] < provider < model (the most specific # layer wins, per field). "" = send nothing and keep the provider's own default. # none | minimal | low | medium | high | xhigh | max # ``max`` is DeepSeek's own top level on chat completions and clamps to ``xhigh`` # everywhere else. The agent maps the value onto each API surface: chat # completions send ``reasoning_effort``, Responses send ``reasoning.effort``, and # Anthropic — which has no effort level — sends ``thinking`` with the token # budget the level maps onto (minimal 1024 … xhigh/max 32768, none = disabled). # reasoning_effort = "medium" # Explicit Anthropic thinking budget in tokens, overriding that mapping; the # request stays valid either way (floor 1024, and ``max_tokens`` grows when the # budget would leave no room for the answer). # thinking_budget_tokens = 8000 # Optional: custom compact/summarisation prompts (empty = built-in defaults). # compact_system_prompt = "" # compact_user_prefix = "Summarize the following conversation:\n\n{transcript}" # compact_seed_text = "Compacted from {src}:\n\n{summary}\n\nCarry on." # --- OpenAI-compatible (vLLM, LiteLLM, OpenAI, …) --- [providers.openai_compat] preset = "openai-compatible" url = "https://api.openai.com/v1" access_key_or_token = "sk-replace-me" # HTTP timeouts for LLM API calls (seconds). Default if omitted: connect=10, read=600. # timeout = 120 # timeout = { connect = 10, read = 600 } [providers.openai_compat.models] "gpt-4o-mini" = { text = true, cost_factor = 1.0 } # --- OpenAI platform (Responses API; tag is "openai") --- # If ``preset`` is omitted, it defaults to "openai". # If ``models`` is omitted, defaults: gpt-5.4, gpt-5.4-mini, gpt-5.4-nano. # [providers.openai] # preset = "openai" # access_key_or_token = "sk-replace-me" # url = "https://api.openai.com/v1" # # Thinking strength for every model of this provider ("" = inherit [agent]; # # a model entry may override it). Values as in the [agent] section above. # reasoning_effort = "medium" # # Hosted/provider-side tools (OpenAI runs them; not local ToolRegistry). # # Default if omitted: [{ type = "web_search" }]. Use [] to disable. # # Examples: web_search, file_search (+ vector_store_ids), code_interpreter, mcp. # provider_tools = [ # { type = "web_search" }, # # { type = "file_search", vector_store_ids = ["vs_xxx"] }, # # { type = "code_interpreter", container = { type = "auto" } }, # ] # # [providers.openai.models] # "gpt-4o-mini" = { text = true } # # Model-level preset/url overrides let one provider route models via different APIs. # # `preset = "openai"` means /responses; `openai-compatible` means /chat/completions; # # `anthropic` means native /messages. # # "claude-sonnet" = { text = true, preset = "anthropic", url = "https://api.anthropic.com/v1" } # # A model entry may also set its own thinking strength, e.g. skip thinking for # # a cheap model: "gpt-5.4-nano" = { text = true, reasoning_effort = "none" } # --- Anthropic (Messages API) --- # [providers.anthropic] # preset = "anthropic" # access_key_or_token = "sk-ant-replace-me" # # url defaults to https://api.anthropic.com/v1 when empty # timeout = { connect = 10, read = 600 } # # Anthropic has no effort level: an effort becomes a thinking token budget # # (see the [agent] section above). Set the budget explicitly to pin it: # reasoning_effort = "high" # thinking_budget_tokens = 16000 # # [providers.anthropic.models] # "claude-sonnet-4-20250514" = { text = true } # "claude-opus-4-20250514" = { text = true, reasoning_effort = "xhigh" } # --- DeepSeek (OpenAI-compatible / Responses / Anthropic conventions) --- # Default models if [providers.*.models] is omitted: deepseek-flash, deepseek-v4-pro. # [providers.deepseek] # preset = "deepseek" # access_key_or_token = "sk-replace-me" # # url defaults by convention: https://api.deepseek.com (chat/responses), # # https://api.deepseek.com/anthropic (anthropic). Set explicitly to override. # # Convention selects the API surface: # # "" / "openai" → OpenAI-compatible chat completions (default) # # "responses" → OpenAI Responses API (POST /responses; today only # # deepseek-flash supports it) # # "anthropic" → Anthropic Messages API (POST /messages on # # https://api.deepseek.com/anthropic; claude-* model ids # # map server-side to deepseek-v4-pro / deepseek-flash) # convention = "responses" # # Legacy location (parsed but ignored; prefer the typed field above): # # extras = { convention = "openai" } # # Thinking strength for this provider; per-model entries below may override it. # # reasoning_effort = "high" # # [providers.deepseek.models] # "deepseek-flash" = { text = true, convention = "responses" } # "deepseek-v4-pro" = { text = true, reasoning_effort = "max" }