Files

14 KiB
Raw Permalink Blame History

title, description
title description
Configuration Reference Complete field reference for environment variables, Project config, Agent config, the Vault, and Schedules.

PenguinHarness configuration has three layers: environment variables shape the deployment, the Project config manages models and credentials, and the Agent config defines a single Agent's behavior. Each Agent additionally has two kinds of state files: the Vault (private environment variables) and Schedules (timed tasks).

Environment variables

The CLI and the server automatically load a .env file from the working directory on startup.

Variable Description Default
PENGUIN_HOME Data root directory ~/.penguin/data
PORT Web service listen port 7364
HOST Web service listen address 127.0.0.1
PENGUIN_WEB_DB Server SQLite database path <root>/web.db
PENGUIN_WEB_DIST Front-end static assets directory the npm server package falls back to its bundled web-dist
PENGUIN_PREVIEW_ORIGIN Origin that serves Workspace HTML previews, e.g. https://preview.example.com unset — the loopback counterpart is derived per request
PENGUIN_SEED_ADMIN_PASSWORD Fixed initial password for the seeded built-in admin (automated tests / e2e) unset — a random penguin-<4 digits> password is generated and printed once at seed time
PENGUIN_LANG CLI language (en / zh), set via penguin config lang en
PENGUIN_UPDATE_CHECK off disables the web app's new-release check (the server's only outbound internet call) enabled

These configure PenguinHarness itself, so PORT, HOST, PENGUIN_WEB_DIST and the internal PENGUIN_CLI_ENTRY are removed from the environment of commands the Agent runs — otherwise a dev server started by exec_command would read PORT and try to bind the port meant for PenguinHarness instead of choosing its own. The rest of the host environment passes through, with one further exception: GIT_EDITOR, GIT_TERMINAL_PROMPT, TERM, NO_COLOR, PAGER and GIT_PAGER are always forced to fixed values, so that a command cannot hang waiting on an editor, a credential prompt or a pager. The Agent's vault is applied on top of the host environment — setting PORT there does reach commands — but not on top of those six.

PENGUIN_PREVIEW_ORIGIN must differ from the app's origin by hostname, not just port: cookies ignore ports, so a second port would still share the session cookie. Leave it unset for local use — the app is canonicalized onto localhost and previews are served from 127.0.0.1, which needs no configuration and no DNS. Set it when the app is reached over a LAN address or a real domain; otherwise previews there fall back to a same-origin sandbox where localStorage, cookies and third-party embeds do not work. When you do set it on a real domain, keep the session cookie host-only (no Domain=), or a sibling subdomain shares it. An unparseable value is a startup error rather than a silent fallback.

Provider credential variables

When a model entry has no inline api_key, the AgentHub gateway falls back to the provider's environment variable; the *_BASE_URL variants override the base URL the same way:

Provider API key Base URL
deepseek DEEPSEEK_API_KEY DEEPSEEK_BASE_URL
anthropic ANTHROPIC_API_KEY ANTHROPIC_BASE_URL
openai, openrouter, fireworks, siliconflow, qwen-token-plan, qwen-pay-as-you-go, custom OPENAI_API_KEY OPENAI_BASE_URL
google GEMINI_API_KEY GEMINI_BASE_URL
zhipu ZAI_API_KEY ZAI_BASE_URL
moonshot MOONSHOT_API_KEY MOONSHOT_BASE_URL

The openrouter, fireworks, siliconflow, qwen-token-plan, qwen-pay-as-you-go, and custom groups speak the OpenAI-compatible protocol, hence the shared OPENAI_* variables. Provider groups and the built-in model catalog are covered in Models & Providers.

Project config

<root>/<project>/.project_config.toml is the Project's single config file: a hidden file written with mode 0600, with credentials inlined on the model entries. Model identity is always the (provider, model_id) pair — string concatenation is forbidden everywhere, and every reference into this file carries both halves: the provider is never inferred from a bare model_id.

Field Description
name Project display name (the id is shown when unset)
default_model Paired reference { provider, model_id } to the default model; must point to an entry in models
vision_model The vision model that reads images on behalf of text-only models (used by describe_image); a paired reference
[[models]] The list of available model entries

Model entry ([[models]]) fields:

Field Description
provider Provider group; together with model_id forms the entry's unique key
model_id Upstream request id, sent to AgentHub unchanged
context_window Context window size
client_type AgentHub client protocol; inferred from model_id by default — third-party OpenAI-compatible models should set openai
display_name Display name; persisted only when it differs from the built-in catalog
vision Whether image input is supported; defaults to supported
max_tokens Per-model max output tokens; overrides the Agent's model.max_tokens when set, omitted = inherit it
pricing Three price buckets cache_read / cache_write / output, in USD per million Tokens (unit = "usd_per_mtok")
api_key Inline credential; when empty, falls back to the provider environment variable
base_url Custom base URL; preset for gateway models
created_at Write timestamp of api_key (ISO 8601; a display field maintained by the interface layer)
default_model = { provider = "deepseek", model_id = "deepseek-v4-flash" }

[[models]]
provider = "deepseek"
model_id = "deepseek-v4-flash"
context_window = 1000000
vision = false
api_key = "sk-..."

[models.pricing]
unit = "usd_per_mtok"
cache_read = 0.003571
cache_write = 0.428571
output = 0.857143

pricing.unit is currently always usd_per_mtok (USD per million tokens); the three buckets map onto token_usage's three counters.

Edit this file via the CLI (penguin config model …) or the Web Models page — never by hand while the service is running, and never by the model itself, which has no right to read or write it.

Agent config

agent_state/system_config.yaml defines a single Agent's behavior (YAML; comments are preserved when edited via the Web UI):

Field Default Description
name — Agent display name (falls back to the id)
description — Agent description
version 1 Agent State version (a natural number), incremented on each successful optimization
system_prompt built-in template Required; the only template with placeholder substitution
max_turns -1 Maximum LLM turns per Task (-1 = unlimited; a positive integer caps the Task)
model.max_tokens 32000 Output Token ceiling per Request (-1 = no cap, provider default); each request clamps the effective value to the model's context_window minus the estimated input, so a small-window model never gets asked for more than fits
model.thinking_level medium none / low / medium / high / xhigh; the session default, overridable per-Task
model.timeoutMs 120000 Per-Request timeout (milliseconds)
compaction.max_context_length 128000 Context Token threshold that triggers compaction; the effective threshold is capped at the model's context_window − 2048 so compaction fires before a small window overflows
compaction.max_session_turns -1 Cumulative Session turn threshold (-1 = unlimited)
compaction.mode summarize summarize / discard
compaction.prompt built-in template Prompt used for summarize compaction
tools.builtin full default toolset when omitted Tool entries: name / description / parameters / permission (r or rw) / forModel / timeoutMs / maxOutputLength / call_description (per-tool toggle for the description call argument, required while on; missing = kept); once written it replaces the default list wholesale
tools.mcpServers [] MCP Server configuration (name + config); reserved for the MCP adapter layer

Tool permissions and approval semantics are covered in Tools & Approval.

A partial-override example (edit the file the init step generated). Note that this file is not deep-merged with the defaults: a key you write out takes effect wholesale, and only omitted keys fall back to the defaults above at their use sites; system_prompt is required (loading refuses without it), so keep the full generated template when editing other fields:

name: default_agent
description: General-purpose agent
version: 3

# Required: keep the full generated default template ({{AGENTS_MD}} and friends; elided here).
system_prompt: |
  …

# -1 (the default) = unlimited; set a positive integer to cap the turns of a single Task.
max_turns: -1

model:
  max_tokens: 32000
  thinking_level: medium
  timeoutMs: 120000

compaction:
  max_context_length: 128000
  max_session_turns: -1
  mode: summarize

# Omitting the whole tools section = the full default toolset. Writing tools.builtin
# REPLACES the default list wholesale: carry the complete definition (including the
# parameters JSON Schema) for every tool you keep — see Tools & Approval.

An existing Agent always runs with its on-disk config verbatim — newer code defaults are never merged in automatically. To adopt the current defaults (for example an updated built-in system prompt), use the settings page's Restore default configuration action: like a skill update it overwrites the existing configuration — custom system prompt, tool list, model/compaction settings and MCP Servers — keeping only name, description and version.

System prompt placeholders

system_prompt is the only template with placeholder substitution. Available placeholders:

Placeholder Injected content
{{AGENTS_MD}} Full text of AGENTS.md
{{VAULT_KEYS}} List of Vault key names (names only)
{{SKILL_METADATA}} Metadata of installed Skills
{{PLATFORM}} Runtime platform
{{OS_VERSION}} Operating system version
{{DATE}} Current date
{{PROJECT_DIR}} App Data Dir: the PenguinHarness app data root (the Project directory)
{{AGENT_ID}} Agent id
{{CWD}} Workspace path
{{PROVIDER}} Model provider group
{{MODEL_ID}} Upstream model id
{{SESSION_ID}} Session id

{{PROJECT_DIR}} is surfaced to the model as the App Data Dir: PenguinHarness's application data root, holding every Agent's data files (agents/<agent_id>/…) and the project-level data — deliberately not described as a project or task directory, so the model does not mistake it for the task's working directory (CWD).

On Windows, {{PROJECT_DIR}} and {{CWD}} are injected with forward slashes — like every other path core composes for the model (attachment lines, the goal-file line, truncated-output recovery paths). The model re-emits these spellings into JSON tool arguments and shell commands; forward slashes are accepted by Node's fs APIs and the package's (Git) Bash tool shell, and avoid JSON backslash-escaping mistakes.

agent_state/AGENTS.md is the developer-editable instruction file, injected via {{AGENTS_MD}} and empty by default — it is also the file an optimizer edits most (see Self-Improvement).

Vault

agent_state/.vault.toml is the Agent-level environment-variable vault: a hidden file written with mode 0600.

  • Key names must match ^[A-Za-z_][A-Za-z0-9_]*$ (shell environment variable naming rules);
  • Values are injected only into tool subprocess environments and never enter the model context or the Trace;
  • Only key names are disclosed in the system prompt via {{VAULT_KEYS}};
  • Saving through the Web/API invalidates the Agent's cached Session runtimes: the next Task on any of its Sessions re-resumes and runs with the new values; a Task already in flight keeps the values it started with (a direct CLI file edit reaches a running server only when a Session is next created or resumed);
  • Managed via penguin config vault set/list/remove or the Web Vault tab.

Schedules

Each file agent_state/schedule/<name>.toml describes one scheduled task (the filename is its identity) that sends a preset Prompt to the Agent on a cadence. Schedules execute only while the Web service (the server runtime) is running, and are managed in the Web Agent settings → Schedule tab.

Field Required Description
prompt yes The Prompt sent on each trigger
enabled no Enabled switch; defaults to false
start_at yes First trigger time (ISO 8601)
period no Cadence such as 30m / 12h / 7d, minimum 5 minutes; omitted means a one-shot task
end_at no End time; must be later than start_at
session_id no Bind to an existing Session; mutually exclusive with the three fields below
workspace no Workspace for new-Session mode
provider / model_id no Paired model reference for new-Session mode; write both or neither — a lone model_id is rejected, and with neither the Project's default model is used
prompt = "Check yesterday's builds and summarize the failures"
enabled = true
start_at = 2026-08-01T09:00:00Z
period = "12h"

Design principle

An Agent's behavior lives entirely in editable files on disk — prompts, Skills, and configuration are data, not code. That is what makes Agents improvable by Agents: an optimizer edits exactly the same files you edit by hand. See Self-Improvement and the CLI Reference.