@letta-ai/letta-code 0.24.5 → 0.24.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.24.5",
3
+ "version": "0.24.7",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,242 @@
1
+ ---
2
+ name: configuring-your-harness
3
+ description: "Configure deterministic Letta Code harness behavior, such as permission rules, lifecycle hooks, and model configuration."
4
+ ---
5
+
6
+ # Configuring Your Harness
7
+
8
+ Use this skill to configure 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, and description.
9
+
10
+ ## Memory vs harness
11
+
12
+ Keep these layers separate:
13
+
14
+ | Layer | What it is | How to change it |
15
+ |-------|------------|------------------|
16
+ | **Memory** | Learned state, memfs files, conversation recall, skills | Edit `$MEMORY_DIR`, use memory tooling, create/update skill files |
17
+ | **Harness** | Deterministic runtime config around the agent | Edit Letta settings JSON or call the Letta API |
18
+
19
+ Edit memory when the agent should remember or learn something. Edit the harness when runtime behavior should deterministically change.
20
+
21
+ 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.”
22
+
23
+ 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.
24
+
25
+ Examples:
26
+
27
+ | User asks for... | Use |
28
+ |------------------|-----|
29
+ | “Auto-approve safe `git diff` commands” | Harness permission rule |
30
+ | “Deny all `rm -rf` shell commands” | Harness permission rule or hook |
31
+ | “Run a script before every Bash call” | Harness hook |
32
+ | “Notify me when you finish a response” | Harness hook |
33
+ | “Always sign commits like XYZ” | Memory, because the LLM writes commit messages |
34
+ | “Prefer short answers” | Memory, because the LLM controls response style |
35
+ | “Remember this repo’s PR checklist” | Memory or project docs, because the LLM applies it |
36
+
37
+ ## Where harness changes live
38
+
39
+ ### Settings JSON files
40
+
41
+ | File | Scope | Typical contents |
42
+ |------|-------|------------------|
43
+ | `~/.letta/settings.json` | User/global | Permissions, hooks, user-wide env vars, `agents[]` entries |
44
+ | `./.letta/settings.json` | Project/shared | Project permissions and hooks, committed with the repo |
45
+ | `./.letta/settings.local.json` | Project-local | Personal project overrides, gitignored |
46
+
47
+ 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.
48
+
49
+ Use project or local scope only when the current working directory is intentionally the project root.
50
+
51
+ ### Letta API fields
52
+
53
+ Name, description, model, and context window are server-side agent fields. Change them with `PATCH /v1/agents/{agent_id}`.
54
+
55
+ Required environment:
56
+
57
+ ```bash
58
+ export LETTA_API_KEY=...
59
+ export LETTA_AGENT_ID=...
60
+ ```
61
+
62
+ Example:
63
+
64
+ ```bash
65
+ curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
66
+ -H "Authorization: Bearer $LETTA_API_KEY" \
67
+ -H "Content-Type: application/json" \
68
+ -d '{"name": "new-name"}'
69
+ ```
70
+
71
+ Load the `letta-api-client` skill for richer SDK examples.
72
+
73
+ ---
74
+
75
+ ## 1. Change permissions
76
+
77
+ Permissions decide which tool calls are allowed, denied, or require approval.
78
+
79
+ ### Rule syntax
80
+
81
+ - Bash prefix match: `Bash(npm install:*)`, `Bash(git:*)`, `Bash(curl:*)`
82
+ - File globs: `Read(src/**)`, `Edit(**/*.ts)`, `Write(*.md)`
83
+ - Broad rules: `*`, `Bash`, `Read` — use sparingly
84
+
85
+ ### Add a permission with the helper
86
+
87
+ ```bash
88
+ python3 <skill-dir>/scripts/add_permission.py \
89
+ --rule "Bash(curl:*)" \
90
+ --type allow \
91
+ --scope user
92
+ ```
93
+
94
+ ### Edit directly
95
+
96
+ ```json
97
+ {
98
+ "permissions": {
99
+ "allow": ["Bash(npm:*)", "Read(src/**)"],
100
+ "deny": ["Bash(rm -rf:*)"],
101
+ "ask": []
102
+ }
103
+ }
104
+ ```
105
+
106
+ 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.
107
+
108
+ ---
109
+
110
+ ## 2. Add hooks
111
+
112
+ 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.
113
+
114
+ Before adding hooks, inspect existing config to avoid duplicates or contradictory policy:
115
+
116
+ ```bash
117
+ python3 <skill-dir>/scripts/show_config.py
118
+ ```
119
+
120
+ Read [`references/hooks.md`](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.
121
+
122
+ Quick examples:
123
+
124
+ ```bash
125
+ # Log every Bash tool call
126
+ python3 <skill-dir>/scripts/add_hook.py \
127
+ --event PreToolUse \
128
+ --matcher Bash \
129
+ --type command \
130
+ --command 'python3 ~/.letta/hooks/log-bash.py' \
131
+ --scope user
132
+
133
+ # Gate edits with an LLM prompt hook
134
+ python3 <skill-dir>/scripts/add_hook.py \
135
+ --event PreToolUse \
136
+ --matcher "Edit|Write" \
137
+ --type prompt \
138
+ --prompt 'Allow only edits under src/ unless the user explicitly requested otherwise. Input: $ARGUMENTS' \
139
+ --scope project
140
+ ```
141
+
142
+ 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.
143
+
144
+ ---
145
+
146
+ ## 3. Change agent configuration
147
+
148
+ Agent config splits between the Letta server and local settings.
149
+
150
+ ### Server-side fields (use the Letta API)
151
+
152
+ Use `PATCH /v1/agents/{agent_id}` with `$LETTA_AGENT_ID`.
153
+
154
+ **Change your model and context window:**
155
+ ```bash
156
+ curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
157
+ -H "Authorization: Bearer $LETTA_API_KEY" \
158
+ -H "Content-Type: application/json" \
159
+ -d '{
160
+ "llm_config": {
161
+ "model": "claude-sonnet-4.5",
162
+ "model_endpoint_type": "anthropic",
163
+ "context_window": 200000
164
+ }
165
+ }'
166
+ ```
167
+
168
+ **Rename yourself:**
169
+ ```bash
170
+ curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
171
+ -H "Authorization: Bearer $LETTA_API_KEY" \
172
+ -H "Content-Type: application/json" \
173
+ -d '{"name": "draft-v2"}'
174
+ ```
175
+
176
+ **Update your description:**
177
+ ```bash
178
+ curl -X PATCH "https://api.letta.com/v1/agents/$LETTA_AGENT_ID" \
179
+ -H "Authorization: Bearer $LETTA_API_KEY" \
180
+ -H "Content-Type: application/json" \
181
+ -d '{"description": "..."}'
182
+ ```
183
+
184
+ For Python / TypeScript SDK usage, see `docs.letta.com/api-overview/introduction` or load the `letta-api-client` skill.
185
+
186
+ ### Local per-agent harness (edit `~/.letta/settings.json`)
187
+
188
+ The `agents[]` array stores per-agent harness preferences you can edit directly:
189
+
190
+ ```json
191
+ {
192
+ "agents": [
193
+ {
194
+ "agentId": "agent-abc123",
195
+ "baseUrl": "https://api.letta.com",
196
+ "pinned": true,
197
+ "toolset": "default"
198
+ }
199
+ ]
200
+ }
201
+ ```
202
+
203
+ - **`toolset`** — which tool set to load for this agent
204
+ - **`pinned`** — quick-switch visibility
205
+
206
+ Find your own entry by matching `agentId === $LETTA_AGENT_ID`, then edit the fields you need.
207
+
208
+ ---
209
+
210
+ ## Quick reference
211
+
212
+ | Goal | Command/change |
213
+ |------|----------------|
214
+ | Auto-approve curl | `add_permission.py --rule "Bash(curl:*)" --type allow --scope user` |
215
+ | Block `rm -rf` | Add `"Bash(rm -rf:*)"` to `permissions.deny`, or add a `PreToolUse` hook |
216
+ | Log all Bash calls | `add_hook.py --event PreToolUse --matcher Bash --type command --command '...' --scope user` |
217
+ | Auto-format after edits | `add_hook.py --event PostToolUse --matcher "Edit|Write" --type command --command '...' --scope project` |
218
+ | Gate edits with LLM | `add_hook.py --event PreToolUse --matcher "Edit|Write" --type prompt --prompt '...' --scope user` |
219
+ | Notify when done | `add_hook.py --event Stop --type command --command 'say done' --scope user` |
220
+ | Show config | `python3 <skill-dir>/scripts/show_config.py` |
221
+ | Change model | `PATCH /v1/agents/$LETTA_AGENT_ID` with `llm_config.model` |
222
+ | Change context window | `PATCH /v1/agents/$LETTA_AGENT_ID` with `llm_config.context_window` |
223
+ | Rename | `PATCH /v1/agents/$LETTA_AGENT_ID` with `name` |
224
+ | Update description | `PATCH /v1/agents/$LETTA_AGENT_ID` with `description` |
225
+ | Change toolset | Edit `agents[].toolset` in `~/.letta/settings.json` |
226
+
227
+ ## After making changes
228
+
229
+ - **Permissions** — file changes are signature-checked and generally hot-reload; restart if behavior does not update.
230
+ - **Hooks** — external file edits may require a fresh session; in-app hook management updates in-memory settings immediately.
231
+ - **Letta API changes** — apply server-side immediately, but current session state may not fully reflect them until restart.
232
+ - **Model changes** — start a fresh conversation after changing them for a clean context.
233
+
234
+ ## Helper scripts in this skill
235
+
236
+ | Script | Purpose |
237
+ |--------|---------|
238
+ | `scripts/add_permission.py` | Add an allow/deny/ask rule to any scope |
239
+ | `scripts/add_hook.py` | Add a command or prompt hook to any event |
240
+ | `scripts/show_config.py` | Show merged permissions, hooks, and per-agent settings across all scopes |
241
+
242
+ All three accept `--scope user|project|local`. Run `--help` for full usage.
@@ -0,0 +1,261 @@
1
+ # Hook configuration reference
2
+
3
+ Use this reference when adding, debugging, or explaining Letta Code hooks.
4
+
5
+ ## Contents
6
+
7
+ - [Before adding a hook](#before-adding-a-hook)
8
+ - [Scopes and merge order](#scopes-and-merge-order)
9
+ - [Events](#events)
10
+ - [Matchers](#matchers)
11
+ - [Command hooks](#command-hooks)
12
+ - [Prompt hooks](#prompt-hooks)
13
+ - [Direct JSON format](#direct-json-format)
14
+ - [Hook input fields](#hook-input-fields)
15
+ - [Practical patterns](#practical-patterns)
16
+ - [Debugging](#debugging)
17
+
18
+ ## Before adding a hook
19
+
20
+ Inspect existing config first:
21
+
22
+ ```bash
23
+ python3 <skill-dir>/scripts/show_config.py
24
+ ```
25
+
26
+ Avoid duplicate hooks, contradictory safety policies, and broad hooks when a narrow matcher is enough.
27
+
28
+ ## Scopes and merge order
29
+
30
+ | Scope | File | Use for |
31
+ |-------|------|---------|
32
+ | User | `~/.letta/settings.json` | Personal global hooks: audit logs, notifications, global safety rails |
33
+ | Project | `./.letta/settings.json` | Team-shared hooks committed with the repo |
34
+ | Local | `./.letta/settings.local.json` | Personal project overrides or experiments; should be gitignored |
35
+
36
+ Hooks from all scopes are merged. Execution order is:
37
+
38
+ 1. Project-local hooks
39
+ 2. Project hooks
40
+ 3. User hooks
41
+
42
+ Project or local scope only works as intended when Letta Code is running from the project root.
43
+
44
+ ## Events
45
+
46
+ Tool events require a `matcher`:
47
+
48
+ | Event | When it runs | Blocking behavior |
49
+ |-------|--------------|-------------------|
50
+ | `PreToolUse` | Before a tool runs | Exit 2 blocks the tool |
51
+ | `PostToolUse` | After a tool succeeds | Good for logging/context; do not rely on it to undo work |
52
+ | `PostToolUseFailure` | After a tool fails | Good for diagnostics; it cannot make the failed tool succeed |
53
+ | `PermissionRequest` | When an approval dialog would show | Exit 0 allows; exit 2 denies |
54
+
55
+ Simple events do not use a matcher:
56
+
57
+ | Event | When it runs |
58
+ |-------|--------------|
59
+ | `UserPromptSubmit` | User submits a normal prompt, not a slash command |
60
+ | `Stop` | Agent finishes a response |
61
+ | `SubagentStop` | Subagent completes |
62
+ | `PreCompact` | Before context compaction |
63
+ | `SessionStart` | Session starts |
64
+ | `SessionEnd` | Session ends |
65
+ | `Notification` | Notification event fires |
66
+
67
+ ## Matchers
68
+
69
+ Tool-event matchers are case-sensitive regex patterns over the tool name. `*` is the special match-all value.
70
+
71
+ Common matchers:
72
+
73
+ ```text
74
+ Bash # shell commands
75
+ Edit|Write # edits and writes
76
+ Read|Grep # reads/searches
77
+ * # all tools
78
+ ```
79
+
80
+ Prefer narrow matchers. Use `*` only for cheap logging or broad policy checks.
81
+
82
+ ## Command hooks
83
+
84
+ Command hooks run a shell command. The hook input JSON is written to stdin. The command also receives:
85
+
86
+ - `LETTA_HOOK_EVENT` — event name
87
+ - `LETTA_WORKING_DIR` / `USER_CWD` — working directory
88
+ - `LETTA_AGENT_ID` / `AGENT_ID` — present when the event has an agent id
89
+
90
+ Exit codes:
91
+
92
+ - `0` — allow / success
93
+ - `2` — block, for blocking-capable events
94
+ - Any other code or timeout — hook error
95
+
96
+ Example: log every Bash invocation as one JSON line:
97
+
98
+ ```bash
99
+ mkdir -p ~/.letta/hooks
100
+ cat > ~/.letta/hooks/log-bash.py <<'PY'
101
+ import pathlib
102
+ import sys
103
+
104
+ path = pathlib.Path.home() / ".letta" / "bash-audit.jsonl"
105
+ path.parent.mkdir(exist_ok=True)
106
+ path.open("a").write(sys.stdin.read() + "\n")
107
+ PY
108
+
109
+ python3 <skill-dir>/scripts/add_hook.py \
110
+ --event PreToolUse \
111
+ --matcher Bash \
112
+ --type command \
113
+ --command 'python3 ~/.letta/hooks/log-bash.py' \
114
+ --scope user
115
+ ```
116
+
117
+ Example: block shell commands containing `rm -rf`:
118
+
119
+ ```bash
120
+ mkdir -p ~/.letta/hooks
121
+ cat > ~/.letta/hooks/check-bash.py <<'PY'
122
+ import json
123
+ import sys
124
+
125
+ data = json.load(sys.stdin)
126
+ cmd = str(data.get("tool_input", {}).get("command", ""))
127
+ if "rm -rf" in cmd:
128
+ print("rm -rf is blocked by hook", file=sys.stderr)
129
+ sys.exit(2)
130
+ PY
131
+
132
+ python3 <skill-dir>/scripts/add_hook.py \
133
+ --event PreToolUse \
134
+ --matcher Bash \
135
+ --type command \
136
+ --command 'python3 ~/.letta/hooks/check-bash.py' \
137
+ --scope user
138
+ ```
139
+
140
+ For anything non-trivial, write a script somewhere stable and call it from the hook. This avoids brittle shell quoting:
141
+
142
+ ```json
143
+ {
144
+ "type": "command",
145
+ "command": "python3 ~/.letta/hooks/check-bash.py",
146
+ "timeout": 60000
147
+ }
148
+ ```
149
+
150
+ ## Prompt hooks
151
+
152
+ Prompt hooks send the hook input to an LLM evaluator. Use `$ARGUMENTS` inside the prompt to insert the event JSON; if omitted, the JSON is appended automatically.
153
+
154
+ Supported prompt-hook events:
155
+ `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `UserPromptSubmit`, `Stop`, `SubagentStop`.
156
+
157
+ The evaluator must return JSON with `ok: true` or `ok: false`; when blocking, include `reason`.
158
+
159
+ Example:
160
+
161
+ ```bash
162
+ python3 <skill-dir>/scripts/add_hook.py \
163
+ --event PreToolUse \
164
+ --matcher "Edit|Write" \
165
+ --type prompt \
166
+ --prompt 'Allow only edits under src/ unless the user explicitly requested otherwise. Input: $ARGUMENTS' \
167
+ --scope project
168
+ ```
169
+
170
+ Add `--model <model-name>` only when a specific model is required.
171
+
172
+ ## Direct JSON format
173
+
174
+ Tool events group hooks under matcher entries:
175
+
176
+ ```json
177
+ {
178
+ "hooks": {
179
+ "PreToolUse": [
180
+ {
181
+ "matcher": "Bash",
182
+ "hooks": [
183
+ {
184
+ "type": "command",
185
+ "command": "python3 ~/.letta/hooks/check-bash.py",
186
+ "timeout": 60000
187
+ }
188
+ ]
189
+ }
190
+ ]
191
+ }
192
+ }
193
+ ```
194
+
195
+ Simple events omit matchers:
196
+
197
+ ```json
198
+ {
199
+ "hooks": {
200
+ "Stop": [
201
+ {
202
+ "hooks": [
203
+ { "type": "command", "command": "say done" }
204
+ ]
205
+ }
206
+ ]
207
+ }
208
+ }
209
+ ```
210
+
211
+ Disable hooks in a settings file with:
212
+
213
+ ```json
214
+ {
215
+ "hooks": {
216
+ "disabled": true
217
+ }
218
+ }
219
+ ```
220
+
221
+ A user-level `"disabled": false` explicitly keeps hooks enabled and overrides project/local `disabled: true`. Without that user-level override, project or local `disabled: true` disables hooks.
222
+
223
+ ## Hook input fields
224
+
225
+ All hook inputs include `event_type` and `working_directory`. Event-specific fields commonly include:
226
+
227
+ - Tool events: `tool_name`, `tool_input`, `tool_call_id`
228
+ - `PostToolUse`: `tool_result`
229
+ - `PostToolUseFailure`: `error_message`, `error_type`
230
+ - `PermissionRequest`: `permission`, `session_permissions`
231
+ - `UserPromptSubmit`: `prompt`, `conversation_id`, `agent_id`
232
+ - `Stop`: `stop_reason`, `message_count`, `tool_call_count`, `assistant_message`, `user_message`
233
+ - `SessionStart` / `SessionEnd`: session metadata, `agent_id`, `conversation_id`
234
+
235
+ When unsure, add a temporary logging hook and inspect the JSON it writes.
236
+
237
+ ## Practical patterns
238
+
239
+ - **Audit tools**: `PreToolUse` + `matcher: "*"` + append stdin to JSONL.
240
+ - **Safety gate**: `PreToolUse` on `Bash` or `Edit|Write`; exit 2 with a stderr reason to block.
241
+ - **Permission policy**: `PermissionRequest`; exit 0 for known-safe requests and exit 2 for known-dangerous ones.
242
+ - **Auto-format**: `PostToolUse` on `Edit|Write`; run a fast idempotent formatter.
243
+ - **Context injection**: `UserPromptSubmit` or `SessionStart`; stdout can be fed back as context.
244
+ - **Notifications**: `Stop` or `SessionEnd`; call `say`, `terminal-notifier`, Slack scripts, etc.
245
+
246
+ ## Debugging
247
+
248
+ Show merged config:
249
+
250
+ ```bash
251
+ python3 <skill-dir>/scripts/show_config.py
252
+ ```
253
+
254
+ Common gotchas:
255
+
256
+ - External edits to hook settings through scripts or direct JSON may require a fresh session because hooks are read through the settings manager cache.
257
+ - In-app hook management APIs update the in-memory settings immediately.
258
+ - Project/local hooks depend on starting Letta Code from the intended project root.
259
+ - JSON quoting inside shell one-liners is fragile; use a separate script for real logic.
260
+ - Long-running hooks block the agent. Keep hooks fast and set `timeout`.
261
+ - Prompt hooks require an agent id and LLM access.