Reference / Agent profile format
Agent profile format
A hosted agent is defined by one TOML file. This page lists every key, its default and the rules lwisd checks before it loads the profile.
Where profiles live
- One file per agent:
~/.config/lwis/agents/<name>.toml($XDG_CONFIG_HOME/lwis/agents/if set;~/Library/Application Support/lwis/agents/on macOS;$LWIS_CONFIG_DIR/agents/overrides both). - The file name must match
name:receipts.tomlholdsname = "receipts". Only*.tomlfiles are read. - Each file must be a regular file (not a symlink), owned by you, and not writable by group or others. The directory must be a real directory with the same rules; otherwise every profile in it is refused.
- Two files with the same
nameare refused. - lwisd reads the profiles at start. After a change, run
lwis agents reload. Check a file before that withlwis agents check <file>.
Syntax
Profiles use a small subset of TOML: key = value lines and # comments. Values can be strings (basic "…", literal '…', and their multi-line forms """…""" and '''…'''), true or false, non-negative integers (underscores allowed), and arrays of those. Tables ([section]), floats and dates are not supported. Unknown or duplicate keys are errors.
Keys
| Key | Type | Default | Rules |
|---|---|---|---|
name | string | required | 1 to 32 characters of a-z, 0-9, - and _, starting with a letter or digit. Must match the file name. |
runtime | string | required | claude-sdk, command or webhook. codex-exec is refused (not implemented). |
cwd | string | none | Required for claude-sdk and command. Absolute or starting with ~/; must be an existing directory, not /. Your home directory (or a parent of it) gives a warning, and is refused for a published agent. |
model | string | Claude's default | claude-sdk only (passed as --model). At most 64 characters of letters, digits and . _ - [ ] :. |
system | string | none | Extra instructions appended to lwisd's system prompt (claude-sdk). At most 8000 characters. |
tools | array of strings | [] | claude-sdk only; other runtimes ignore them with a warning. See Tools. |
permission_mode | string | "default" | default, manual, dontAsk, plan, acceptEdits or auto. bypassPermissions is refused. acceptEdits and auto are refused while strict = true. dontAsk gives a warning: it denies every call that would prompt, so most tools become unusable. |
strict | boolean | true | Side-effect tools wait for lwis agents approve <id>. |
accept_from | array of strings | ["self"] | Who may send captures. See Who may send. |
idle_minutes | integer | 5 | How long the agent stays warm after a capture. 1 to 240. |
approval_timeout_minutes | integer | 10 | How long a strict approval waits before it is denied. 1 to 60. |
turn_timeout_seconds | integer | 300 | How long one capture may take. 10 to 3600. |
command | array of strings | none | Required for command, refused for other runtimes. The program and its arguments; a leading ~/ in the program is expanded. |
url | string | none | Required for webhook, refused for other runtimes. Must start with http://127.0.0.1, http://localhost or http://[::1] (any port and path). |
publish | boolean | false | Applies the rules for a published agent (below). Listing an agent on your card isn't available yet. |
inbox | string | "closed" | "closed", or "open" for a published agent only. |
allow_unconfined_tools | boolean | false | Lets a published agent list tools that escape cwd. |
add_dir and add_dirs are refused explicitly: agents are confined to cwd.
Tools
Each entry is a Claude Code tool name, or Name(specifier).
- Read-only tools never need approval, even in strict mode:
Read,Glob,Grep,LS,TodoWrite,NotebookRead. - Other known tools:
Edit,MultiEdit,Write,NotebookEdit, and the unconfined ones below. - Unconfined tools can reach outside
cwd(commands, code, network, sub-agents):Bash,BashOutput,KillShell,KillBash,PowerShell,REPL,WebFetch,WebSearch,Task,Agent. Each gives a warning, and a published agent needsallow_unconfined_tools = trueto list them. - MCP tools (
mcp__…) are refused: hosted agents load no MCP server. lwis_replyis accepted as a marker and otherwise ignored: the agent's final answer is always the reply.- An unknown name gives a warning.
- A tool granted only as
Name(specifier)always waits for your approval: lwisd doesn't evaluate the specifier.
With no tools, a claude-sdk agent can only look at the capture and answer.
Who may send
"self": your own paired devices, and captures you forward withlwis forward.- A contact, by petname, identity id or fingerprint (at most 128 characters of letters, digits, spaces and
_ - : .). A contact reaches the agent directly only while you have also runlwis contacts policy <contact> --agents on. Prefer the identity id or fingerprint: at load time lwisd warns when an entry matches no current contact, or matches one by petname, because a petname can later name someone else. "agent:<label>": the agent pairing<label>(lwis pair --agent <label>), another machine's agent. Its reports run strict, in a fresh one-shot process, fenced as that agent's. See Agent to agent.- An empty list gives a warning: nobody can reach the agent.
Published agents
With publish = true, lwisd also requires:
runtime = "claude-sdk"(lwisd can't sandbox other runtimes);strict = true;- a
cwdthat is a dedicated directory, not your home or a parent of it; - no unconfined tool unless
allow_unconfined_tools = true.
Examples
A receipts filer that reads and writes in its own folder, with every write approved by you:
name = "receipts"
runtime = "claude-sdk"
model = "haiku"
cwd = "~/agents/receipts"
tools = ["Read", "Glob", "Write"]
system = """
You file photographed receipts. Append one CSV line per receipt to receipts.csv:
date, merchant, total, currency. Reply with the line you added.
"""
A script that speaks the agent contract, not strict, kept warm for 30 minutes:
name = "triage"
runtime = "command"
cwd = "~/agents/triage"
command = ["~/agents/triage/run.py", "--quiet"]
strict = false
idle_minutes = 30
A local webhook that a trusted contact may also reach (after lwis contacts policy <contact> --agents on):
name = "notes"
runtime = "webhook"
url = "http://127.0.0.1:8750/lwis"
accept_from = ["self", "<contact identity id>"]
turn_timeout_seconds = 60
Check each one before reloading:
lwis agents check ~/.config/lwis/agents/receipts.toml
lwis agents reload