Agent Skills: Modifying the Harness

Modify the Letta Code harness, such as permission rules, lifecycle hooks, tool availability, model/context settings, schedules, and deterministic runtime configuration.

UncategorizedID: letta-ai/letta-code/modifying-the-harness

Repository

letta-aiLicense: Apache-2.0
2,814322

Install this agent skill to your local

pnpm dlx add-skill https://github.com/letta-ai/letta-code/tree/HEAD/src/skills/builtin/modifying-the-harness

Skill Files

Browse the full folder contents for modifying-the-harness.

Download Skill

Loading file tree…

src/skills/builtin/modifying-the-harness/SKILL.md

Skill Metadata

Name
modifying-the-harness
Description
"Modify the Letta Code harness, such as permission rules, lifecycle hooks, tool availability, model/context settings, schedules, and deterministic runtime configuration."

Modifying the Harness

Use this skill to modify deterministic Letta Code harness behavior, primarily permissions and lifecycle hooks. It can also help with local per-agent settings like toolset, model, context window, name, description, and schedules.

Memory vs harness

Keep these layers separate:

| Layer | What it is | How to change it | |-------|------------|------------------| | Memory | Learned state, memfs files, conversation recall, skills | Edit $MEMORY_DIR, use memory tooling, create/update skill files | | Harness | Deterministic runtime config around the agent | Edit Letta settings JSON or call the Letta API |

Edit memory when the agent should remember or learn something. Edit the harness when runtime behavior should deterministically change.

Do not edit harness settings for ordinary preferences like “remember I prefer concise answers.” Store those in memory. Do edit harness settings for deterministic behavior like “always ask before shell commands,” “add a hook to block unsafe edits,” or “change this agent’s toolset.”

Decision rule: if the LLM is responsible for choosing the behavior, store the instruction in memory. If the harness should enforce the behavior outside the LLM, use this skill.

Examples:

| User asks for... | Use | |------------------|-----| | “Auto-approve safe git diff commands” | Harness permission rule | | “Deny all rm -rf shell commands” | Harness permission rule or hook | | “Run a script before every Bash call” | Harness hook | | “Notify me when you finish a response” | Harness hook | | “Always sign commits like XYZ” | Memory, because the LLM writes commit messages | | “Prefer short answers” | Memory, because the LLM controls response style | | “Remember this repo’s PR checklist” | Memory or project docs, because the LLM applies it |

Where harness changes live

Settings JSON files

| File | Scope | Typical contents | |------|-------|------------------| | ~/.letta/settings.json | User/global | Permissions, hooks, user-wide env vars, agents[] entries | | ./.letta/settings.json | Project/shared | Project permissions and hooks, committed with the repo | | ./.letta/settings.local.json | Project-local | Personal project overrides, gitignored |

Precedence for settings scopes is local > project > user, but list-like entries such as permissions and hooks are merged. For hooks, project-local hooks run first, then project hooks, then user hooks.

Use project or local scope only when the current working directory is intentionally the project root.

Letta API fields

Name, description, model, and context window are server-side agent fields. Change them with PATCH /v1/agents/{agent_id}.

Required environment:

export LETTA_API_KEY=...
export LETTA_AGENT_ID=...

Example:

curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
  -H "Authorization: Bearer $LETTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "new-name"}'

Load the letta-api-client skill for richer SDK examples.


1. Change permissions

Permissions decide which tool calls are allowed, denied, or require approval. Use alwaysAsk when a rule should request human approval even in unrestricted/yolo mode.

Settings ask rules request approval in normal permission modes, but they do not override unrestricted/yolo mode. For a mod tool that must pause for human approval even in unrestricted mode, set approvalPolicy: "alwaysAsk" in the tool registration. For broader dynamic policy, use a mod permission overlay or a blocking hook.

Rule syntax

  • Bash prefix match: Bash(npm install:*), Bash(git:*), Bash(curl:*)
  • File globs: Read(src/**), Edit(**/*.ts), Write(*.md)
  • Broad rules: *, Bash, Read — use sparingly

Add a permission with the helper

python3 <skill-dir>/scripts/add_permission.py \
  --rule "Bash(curl:*)" \
  --type allow \
  --scope user

Force approval even in yolo mode:

python3 <skill-dir>/scripts/add_permission.py \
  --rule "Bash(git push:*)" \
  --type alwaysAsk \
  --scope user

Edit directly

{
  "permissions": {
    "allow": ["Bash(npm:*)", "Read(src/**)"],
    "deny": ["Bash(rm -rf:*)"],
    "ask": [],
    "alwaysAsk": ["Bash(git push:*)"]
  }
}

Permissions loaded from settings files are signature-checked and can update during a running session. If behavior does not update immediately, start a fresh session.


2. Add hooks

Hooks run shell commands or LLM prompt checks in response to Letta Code events. Use them to audit actions, inject context, enforce policy, auto-format after edits, notify on completion, or block unsafe actions.

Before adding hooks, inspect existing config to avoid duplicates or contradictory policy:

python3 <skill-dir>/scripts/show_config.py

Read references/hooks.md when adding, debugging, or explaining hooks. It covers scopes, merge order, events, matchers, command hooks, prompt hooks, input JSON, exit codes, direct JSON format, and practical patterns.

Quick examples:

# Log every Bash tool call
python3 <skill-dir>/scripts/add_hook.py \
  --event PreToolUse \
  --matcher Bash \
  --type command \
  --command 'python3 ~/.letta/hooks/log-bash.py' \
  --scope user

# Gate edits with an LLM prompt hook
python3 <skill-dir>/scripts/add_hook.py \
  --event PreToolUse \
  --matcher "Edit|Write" \
  --type prompt \
  --prompt 'Allow only edits under src/ unless the user explicitly requested otherwise. Input: $ARGUMENTS' \
  --scope project

External edits to hook settings through scripts or direct JSON may require a fresh session because hooks are read through the settings manager cache. Hooks changed through in-app hook management APIs update in-memory settings immediately.


3. Change agent configuration

Agent config splits between the Letta server and local settings.

Server-side fields (use the Letta API)

Use PATCH /v1/agents/{agent_id} with $LETTA_AGENT_ID.

Change your model and context window:

curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
  -H "Authorization: Bearer $LETTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "anthropic/claude-sonnet-4-5-20250929",
    "context_window_limit": 200000
  }'

Do not patch llm_config directly; the Letta API rejects deprecated llm_config updates. Use model and context_window_limit instead. After the patch, read the agent back and verify the effective llm_config.context_window reported by the server.

Rename yourself:

curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
  -H "Authorization: Bearer $LETTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "draft-v2"}'

Update your description:

curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
  -H "Authorization: Bearer $LETTA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"description": "..."}'

For Python / TypeScript SDK usage, see docs.letta.com/api-overview/introduction or load the letta-api-client skill.

Local per-agent harness (edit ~/.letta/settings.json)

The agents[] array stores per-agent harness preferences you can edit directly:

{
  "agents": [
    {
      "agentId": "agent-abc123",
      "baseUrl": "https://api.letta.com",
      "pinned": true,
      "toolset": "default"
    }
  ]
}
  • toolset — which tool set to load for this agent
  • pinned — quick-switch visibility

Find your own entry by matching agentId === $LETTA_AGENT_ID, then edit the fields you need.


Quick reference

| Goal | Command/change | |------|----------------| | Auto-approve curl | add_permission.py --rule "Bash(curl:*)" --type allow --scope user | | Block rm -rf | Add "Bash(rm -rf:*)" to permissions.deny, or add a PreToolUse hook | | Always ask before git push | add_permission.py --rule "Bash(git push:*)" --type alwaysAsk --scope user | | Log all Bash calls | add_hook.py --event PreToolUse --matcher Bash --type command --command '...' --scope user | | Auto-format after edits | add_hook.py --event PostToolUse --matcher "Edit|Write" --type command --command '...' --scope project | | Gate edits with LLM | add_hook.py --event PreToolUse --matcher "Edit|Write" --type prompt --prompt '...' --scope user | | Notify when done | add_hook.py --event Stop --type command --command 'say done' --scope user | | Show config | python3 <skill-dir>/scripts/show_config.py | | Change model | PATCH /v1/agents/$LETTA_AGENT_ID with model | | Change context window | PATCH /v1/agents/$LETTA_AGENT_ID with context_window_limit; verify returned llm_config.context_window | | Rename | PATCH /v1/agents/$LETTA_AGENT_ID with name | | Update description | PATCH /v1/agents/$LETTA_AGENT_ID with description | | Change toolset | Edit agents[].toolset in ~/.letta/settings.json |

After making changes

  • Permissions — file changes are signature-checked and generally hot-reload; restart if behavior does not update.
  • Hooks — external file edits may require a fresh session; in-app hook management updates in-memory settings immediately.
  • Letta API changes — apply server-side immediately, but current session state may not fully reflect them until restart.
  • Model changes — start a fresh conversation after changing them for a clean context.

Helper scripts in this skill

| Script | Purpose | |--------|---------| | scripts/add_permission.py | Add an allow/deny/ask/alwaysAsk rule to any scope | | scripts/add_hook.py | Add a command or prompt hook to any event | | scripts/show_config.py | Show merged permissions, hooks, and per-agent settings across all scopes |

All three accept --scope user|project|local. Run --help for full usage.