@letta-ai/letta-code 0.28.11 → 0.28.12
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/dist/channels-slack.js +2 -11
- package/dist/channels-slack.js.map +3 -3
- package/dist/types/channels/slack/progress.d.ts.map +1 -1
- package/letta.js +26919 -26810
- package/package.json +1 -1
- package/skills/creating-mods/references/events.md +4 -4
- package/skills/self-configuration/LICENSE +21 -0
- package/skills/self-configuration/SKILL.md +432 -0
- package/skills/self-configuration/references/api-patch-examples.md +161 -0
- package/skills/self-configuration/references/compaction-prompt-patterns.md +115 -0
- package/skills/self-configuration/references/model-settings.md +55 -0
- package/skills/self-configuration/scripts/add_permission.py +212 -0
- package/skills/self-configuration/scripts/show_config.py +370 -0
- package/skills/self-configuration/scripts/update-agent-settings.ts +384 -0
- package/skills/self-configuration/scripts/update-compaction-prompt.ts +227 -0
- package/skills/modifying-the-harness/SKILL.md +0 -263
- package/skills/modifying-the-harness/references/hooks.md +0 -261
- package/skills/modifying-the-harness/scripts/add_hook.py +0 -223
- package/skills/modifying-the-harness/scripts/add_permission.py +0 -136
- package/skills/modifying-the-harness/scripts/show_config.py +0 -212
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@ Use events when trusted local code should react to app/session changes or transf
|
|
|
12
12
|
- Conversation status example
|
|
13
13
|
- Rules
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Mod events are typed local extension points. Use the event's documented return contract for transformations, cancellation, or result replacement; otherwise treat the event as notification-only.
|
|
16
16
|
|
|
17
17
|
## Capabilities
|
|
18
18
|
|
|
@@ -124,7 +124,7 @@ Lifecycle handlers are notification-only and should not return values. `turn_sta
|
|
|
124
124
|
}
|
|
125
125
|
```
|
|
126
126
|
|
|
127
|
-
`tool_start` fires immediately before a client-side tool executes. This includes built-in tools, mod tools, and external tools executed through the local tool manager. It runs after permission/approval classification
|
|
127
|
+
`tool_start` fires immediately before a client-side tool executes. This includes built-in tools, mod tools, and external tools executed through the local tool manager. It runs after permission/approval classification, so trusted local mods can change the actual executed arguments after the approval UI has already classified the original request. Mod permission overlays are rechecked after `tool_start` on the final args.
|
|
128
128
|
|
|
129
129
|
Handlers can inspect `event.args`, mutate it directly, or return replacement args:
|
|
130
130
|
|
|
@@ -171,7 +171,7 @@ letta.events.on("tool_end", (event) => {
|
|
|
171
171
|
});
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
The first handler that returns a `result` wins; later handlers are shadowed. Only string results are surfaced — multimodal/image results pass through unchanged. `tool_end`
|
|
174
|
+
The first handler that returns a `result` wins; later handlers are shadowed. Only string results are surfaced — multimodal/image results pass through unchanged. Use `tool_end` when trusted local code should observe or rewrite tool results before the agent sees them.
|
|
175
175
|
|
|
176
176
|
A handler can also react to a specific tool completing by adjusting conversation state. For example, switch model and reasoning effort when entering and exiting plan mode (`tool_end` fires only after the tool succeeds, so a denied approval won't switch):
|
|
177
177
|
|
|
@@ -359,6 +359,6 @@ export default function activate(letta) {
|
|
|
359
359
|
## Rules
|
|
360
360
|
|
|
361
361
|
- Do not block user flow unless the event's typed contract explicitly supports blocking.
|
|
362
|
-
- Do not use lifecycle events for safety decisions
|
|
362
|
+
- Do not use lifecycle events for safety decisions. Use permission registration or an event contract that explicitly supports blocking.
|
|
363
363
|
- Catch expected local errors if the user-facing outcome matters. Uncaught errors are isolated and recorded as mod diagnostics.
|
|
364
364
|
- Return disposers from activation for event registrations, timers, subscriptions, and status values.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Letta, Inc.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: self-configuration
|
|
3
|
+
description: Modify Letta Code's own memory, model, context window, system prompt, compaction, permissions, toolsets, mods, skills, channels, schedules, and local runtime settings. Use when the user asks you to change how you behave or how the harness runs you.
|
|
4
|
+
license: MIT
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Self-Configuration
|
|
8
|
+
|
|
9
|
+
Use this skill when the user asks you to change yourself or the Letta Code runtime around you.
|
|
10
|
+
|
|
11
|
+
The important part is choosing the right layer. Do not smear a preference into deterministic config, and do not bury a deterministic safety rule in prose memory.
|
|
12
|
+
|
|
13
|
+
## First choose the layer
|
|
14
|
+
|
|
15
|
+
| Layer | Use it for | How to change it |
|
|
16
|
+
| --- | --- | --- |
|
|
17
|
+
| Memory and identity | Durable facts, style preferences, persona changes, project knowledge, reusable skills | Edit `$MEMORY_DIR` files and sync the memory repo |
|
|
18
|
+
| Server agent fields | Default model, model settings, context limit, system prompt, compaction, agent name, description | Patch `/v1/agents/{agent_id}` |
|
|
19
|
+
| Server conversation fields | Temporary model/context experiments for one conversation | Patch `/v1/conversations/{conversation_id}` |
|
|
20
|
+
| Local settings | Permissions, environment variables, UI/runtime preferences, pinned agents, toolset overrides, reflection cadence | Edit `~/.letta/settings.json`, `./.letta/settings.json`, or `./.letta/settings.local.json` |
|
|
21
|
+
| Mods | New deterministic tools, slash commands, providers, statusline behavior, or lightweight UI | Load `creating-mods`, `customizing-commands`, or `customizing-statusline` |
|
|
22
|
+
| Skills | Reusable procedural knowledge or bundled scripts | Load `creating-skills` or `acquiring-skills` |
|
|
23
|
+
| Channels | Slack/Discord/Telegram/WhatsApp/Signal accounts, pairing, routing, listener state | Use `letta channels` or channel commands |
|
|
24
|
+
| Schedules | Reminders and recurring prompts | Load `scheduling-tasks` and use `letta cron` |
|
|
25
|
+
|
|
26
|
+
Decision rule: if the model should remember and reason about it, use memory. If the runtime must enforce it or route it before the model decides anything, use settings, API fields, mods, channels, or schedules.
|
|
27
|
+
|
|
28
|
+
## Safe workflow
|
|
29
|
+
|
|
30
|
+
1. Identify scope: current conversation, current agent, project, or global user config.
|
|
31
|
+
2. Inspect current state first and save the relevant safe fields as a rollback patch. Do not copy secrets or full compiled prompts into backups.
|
|
32
|
+
3. Prefer a dry run for API patches and scripts.
|
|
33
|
+
4. Apply the smallest change that satisfies the request.
|
|
34
|
+
5. Verify the effective state after the write.
|
|
35
|
+
6. Tell the user what changed and whether a restart/new conversation is needed.
|
|
36
|
+
|
|
37
|
+
Never print secrets. If inspecting env settings, list keys unless the user explicitly asks for values and the values are safe to reveal.
|
|
38
|
+
|
|
39
|
+
## Guardrails are not security boundaries
|
|
40
|
+
|
|
41
|
+
These helper scripts reduce accidental harm. They are not a security boundary against an agent with unrestricted Bash, raw curl/SDK access, API credentials, or filesystem access. `LETTA_API_KEY` and the installed CLI may have authority over other agents visible to the same account/server.
|
|
42
|
+
|
|
43
|
+
Never target another agent or conversation unless explicitly directed and verified. If `AGENT_ID` or `CONVERSATION_ID` is set, the server-setting helpers reject mismatched live/GET operations unless `--allow-other-agent` is present. If the current env ID is absent, explicit IDs remain usable for out-of-band recovery.
|
|
44
|
+
|
|
45
|
+
If a broken model or prompt prevents the agent from completing a turn, recover out of band from another shell or client with the CLI/API. Do not depend on the broken model to repair itself.
|
|
46
|
+
|
|
47
|
+
## Inspect effective state before changing it
|
|
48
|
+
|
|
49
|
+
Local settings, server state, and the current process are different sources of truth. Inspect the layer you intend to change before writing it.
|
|
50
|
+
|
|
51
|
+
Start with the secret-safe local/runtime report:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
python3 <SKILL_DIR>/scripts/show_config.py --cwd "$PWD"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Before changing server state, read the relevant scopes without printing full system prompts or credentials:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
61
|
+
--target agent --agent-id "$AGENT_ID" --show
|
|
62
|
+
|
|
63
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
64
|
+
--target conversation --conversation-id "$CONVERSATION_ID" --show
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Do not infer an agent default from one conversation or infer a conversation override from the agent. Report both when diagnosing model or context differences.
|
|
68
|
+
|
|
69
|
+
If CLI behavior does not match the docs, stop and inspect `command -v letta`, `type -a letta`, and `letta --version`. A stale or shadowed binary is a config bug, not a reason to guess.
|
|
70
|
+
|
|
71
|
+
## Memory and identity
|
|
72
|
+
|
|
73
|
+
Use memory when the user wants you to remember, prefer, learn, or change your identity/personality.
|
|
74
|
+
|
|
75
|
+
Common files:
|
|
76
|
+
|
|
77
|
+
| Path | Purpose |
|
|
78
|
+
| --- | --- |
|
|
79
|
+
| `$MEMORY_DIR/system/persona.md` | Identity, voice, behavioral defaults |
|
|
80
|
+
| `$MEMORY_DIR/system/human.md` | Durable notes about the person you work with |
|
|
81
|
+
| `$MEMORY_DIR/projects/` | Project-specific long-term context |
|
|
82
|
+
| `$MEMORY_DIR/skills/` | Agent-owned reusable skills |
|
|
83
|
+
| `$MEMORY_DIR/relationships/` | Durable relationship and collaboration notes |
|
|
84
|
+
|
|
85
|
+
After changing memory, inspect and commit the exact changed files. Push/sync according to the current harness reminder or the `syncing-memory-filesystem` skill; some environments sync committed memory automatically.
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
cd "$MEMORY_DIR" && git status
|
|
89
|
+
cd "$MEMORY_DIR" && git add <changed-files> && git commit --author="$AGENT_NAME <$AGENT_ID@letta.com>" -m "memory: <summary>"
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Do not use API system-prompt replacement for ordinary learning. That can clobber the compiled prompt. Edit memory instead.
|
|
93
|
+
|
|
94
|
+
## Server-side agent and conversation settings
|
|
95
|
+
|
|
96
|
+
Server fields control model execution and agent metadata. Use the agent endpoint for persistent defaults. Use the conversation endpoint for scoped experiments.
|
|
97
|
+
|
|
98
|
+
Required environment for live API writes:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
export LETTA_API_KEY=...
|
|
102
|
+
export AGENT_ID=agent-...
|
|
103
|
+
export CONVERSATION_ID=conv-... # only needed for conversation-scoped changes
|
|
104
|
+
export LETTA_BASE_URL=... # required; use the current server, not a hard-coded Cloud URL
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The scripts in this skill default to `AGENT_ID`, `CONVERSATION_ID`, and `LETTA_BASE_URL`. Server reads and writes require `LETTA_BASE_URL` or explicit `--base-url`; they never silently fall back to `api.letta.com`. Keep `LETTA_BASE_URL` paired with the `LETTA_API_KEY` supplied by the current runtime so local, self-hosted, and non-default Cloud environments are not accidentally redirected. Pass explicit IDs when there is any doubt. `--show` fetches the selected agent or conversation and prints only safe effective fields. Server operations reject target IDs that differ from the current env ID unless `--allow-other-agent` is passed. Dry-run output is labeled: `offline_partial_patch` means no server state was fetched; `effective_merged_patch` means the script fetched current server state and shows the merged patch that would be sent.
|
|
108
|
+
|
|
109
|
+
### Dry-runable update script
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts --help
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Patch the current conversation first when testing a risky model/settings change:
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
119
|
+
--target conversation \
|
|
120
|
+
--conversation-id "$CONVERSATION_ID" \
|
|
121
|
+
--model "openai/gpt-5.2" \
|
|
122
|
+
--context-window-limit 64000 \
|
|
123
|
+
--dry-run
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Patch the agent default after the user confirms the change should persist:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
130
|
+
--target agent \
|
|
131
|
+
--agent-id "$AGENT_ID" \
|
|
132
|
+
--model "openai/gpt-5.2" \
|
|
133
|
+
--context-window-limit 64000
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Name and description
|
|
137
|
+
|
|
138
|
+
Name and description are agent-level metadata. Do not pass them with `--target conversation`. Values must be non-empty; the helper does not clear metadata by accident.
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
142
|
+
--target agent \
|
|
143
|
+
--agent-id "$AGENT_ID" \
|
|
144
|
+
--name "repo-maintainer" \
|
|
145
|
+
--dry-run
|
|
146
|
+
|
|
147
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
148
|
+
--target agent \
|
|
149
|
+
--agent-id "$AGENT_ID" \
|
|
150
|
+
--description "Maintains repository configuration and review-ready PRs." \
|
|
151
|
+
--dry-run
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Do not patch `llm_config` directly. Use `model`, `context_window_limit`, and `model_settings`. For metadata, use `name` and `description`. Then read back the agent or conversation and verify the returned `llm_config.context_window`, `model_settings`, and metadata fields.
|
|
155
|
+
|
|
156
|
+
### Model settings
|
|
157
|
+
|
|
158
|
+
`model_settings` is usually replacement-style. Fetch the current object first and preserve fields you still need, or pass `--merge-model-settings`. Merge dry runs fetch current state and require `LETTA_API_KEY` because they preview preserved fields, not just the local patch fragment.
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
cat > /tmp/model-settings.json <<'JSON'
|
|
162
|
+
{
|
|
163
|
+
"provider_type": "openai",
|
|
164
|
+
"parallel_tool_calls": true,
|
|
165
|
+
"reasoning": { "reasoning_effort": "medium" }
|
|
166
|
+
}
|
|
167
|
+
JSON
|
|
168
|
+
|
|
169
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
170
|
+
--target agent \
|
|
171
|
+
--agent-id "$AGENT_ID" \
|
|
172
|
+
--model "openai/gpt-5.2" \
|
|
173
|
+
--model-settings-file /tmp/model-settings.json \
|
|
174
|
+
--merge-model-settings \
|
|
175
|
+
--dry-run
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Provider reasoning fields differ. Read [`references/model-settings.md`](references/model-settings.md) before changing reasoning or provider-specific settings.
|
|
179
|
+
|
|
180
|
+
### Compaction settings
|
|
181
|
+
|
|
182
|
+
Compaction controls how old messages are summarized when context is evicted. Bad compaction prompts cause delayed, progressive context loss as future compactions discard useful state. Good ones preserve goals, files, commands, test results, blockers, and current state.
|
|
183
|
+
|
|
184
|
+
Use the helper for prompt changes. Even `--dry-run` fetches current compaction settings so omitted fields are preserved in the preview. Live writes require `--confirm-compaction-prompt`.
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
npx tsx <SKILL_DIR>/scripts/update-compaction-prompt.ts \
|
|
188
|
+
--prompt-file /tmp/compaction-prompt.txt \
|
|
189
|
+
--mode self_compact_sliding_window \
|
|
190
|
+
--clip-chars 50000 \
|
|
191
|
+
--dry-run
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Read [`references/compaction-prompt-patterns.md`](references/compaction-prompt-patterns.md) before drafting a new prompt.
|
|
195
|
+
|
|
196
|
+
### System prompt replacement
|
|
197
|
+
|
|
198
|
+
This is a sharp tool. A bad system prompt can self-brick the agent. Use it only when the user explicitly asks to replace the server-side system prompt or when repairing a known server-side prompt state. Live writes require `--confirm-system-replacement`; dry runs do not.
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
202
|
+
--target agent \
|
|
203
|
+
--agent-id "$AGENT_ID" \
|
|
204
|
+
--system-file /tmp/new-system-prompt.txt \
|
|
205
|
+
--dry-run
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
For normal behavioral changes, edit memory. For startup preset selection, use `--system <preset>` or `--system-custom <file>` when launching Letta Code.
|
|
209
|
+
|
|
210
|
+
## Local settings files
|
|
211
|
+
|
|
212
|
+
Settings scopes:
|
|
213
|
+
|
|
214
|
+
| File | Scope | Typical contents |
|
|
215
|
+
| --- | --- | --- |
|
|
216
|
+
| `~/.letta/settings.json` | User/global | Permissions, env keys, experiments, UI/runtime preferences, agents[] entries |
|
|
217
|
+
| `./.letta/settings.json` | Project/shared | Project settings committed with the repo |
|
|
218
|
+
| `./.letta/settings.local.json` | Project-local | Personal project overrides, usually gitignored |
|
|
219
|
+
|
|
220
|
+
Precedence is local > project > user. Permission rule lists are merged; scalar settings usually override. When editing JSON directly, preserve unknown fields, keep the file schema-valid, and inspect the effective config afterward instead of rewriting the whole file from a guessed shape.
|
|
221
|
+
|
|
222
|
+
Inspect merged local config and the current runtime with:
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
python3 <SKILL_DIR>/scripts/show_config.py --cwd "$PWD"
|
|
226
|
+
python3 <SKILL_DIR>/scripts/show_config.py --cwd "$PWD" --json
|
|
227
|
+
python3 <SKILL_DIR>/scripts/show_config.py --cwd "$PWD" --section runtime --json
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Selected global settings keys:
|
|
231
|
+
|
|
232
|
+
| Key | Meaning |
|
|
233
|
+
| --- | --- |
|
|
234
|
+
| `tokenStreaming` | Stream tokens in UI |
|
|
235
|
+
| `reasoningTabCycleEnabled` | Let Tab cycle reasoning tiers when enabled |
|
|
236
|
+
| `showCompactions` | Show compaction activity |
|
|
237
|
+
| `sessionContextEnabled` | Send device/agent context at session start |
|
|
238
|
+
| `autoConversationTitles` | Generate conversation titles |
|
|
239
|
+
| `autoSwapOnQuotaLimit` | Auto-switch temporary model on quota errors |
|
|
240
|
+
| `includeWorktreeTool` | Include worktree tool in toolsets |
|
|
241
|
+
| `preferredBackendMode` | Startup backend preference, `api` or `local` |
|
|
242
|
+
| `channelCredentialsStore` | Channel token storage, `file`, `keyring`, or `auto` |
|
|
243
|
+
| `reflectionTrigger` / `reflectionStepCount` | Default reflection cadence |
|
|
244
|
+
| `reflectionSettingsByAgent` | Per-agent reflection cadence |
|
|
245
|
+
| `permissions` | Allow/deny/ask/alwaysAsk rules |
|
|
246
|
+
| `env` | User-wide environment variables for Letta Code |
|
|
247
|
+
| `experiments` | Feature flags |
|
|
248
|
+
| `agents[]` | Per-agent pinned/memfs/toolset/system-prompt metadata |
|
|
249
|
+
|
|
250
|
+
Per-agent `agents[]` entries are keyed by `agentId` plus server. For api.letta.com, `baseUrl` may be omitted. For another server, preserve the server key.
|
|
251
|
+
|
|
252
|
+
Base URL resolution is split between runtime API calls and settings lookup. Runtime API calls require `LETTA_BASE_URL` or an explicit script `--base-url`; do not replace it with a hard-coded Cloud URL. Settings server keys resolve from `LETTA_SETTINGS_BASE_URL`, `env.LETTA_SETTINGS_BASE_URL`, `LETTA_BASE_URL`, `env.LETTA_BASE_URL`, then api.letta.com. Do not move `agents[]` entries across base URLs unless the user is deliberately migrating servers.
|
|
253
|
+
|
|
254
|
+
Toolset values currently include `auto`, `default`, `codex`, `codex_snake`, `gemini`, `gemini_snake`, and `none`. Use `auto` unless the user explicitly wants a manual override.
|
|
255
|
+
|
|
256
|
+
## Permissions
|
|
257
|
+
|
|
258
|
+
Permissions decide whether tool calls are allowed, denied, or require approval. User/global permission rules affect all agents using that settings file: `allow` can weaken review, while `deny` and `alwaysAsk` can brick workflows. Valid modes are `standard`, `acceptEdits`, and `unrestricted`; legacy `default` maps to `standard`, while `bypassPermissions` and `fullAccess` map to `unrestricted`. The default mode is `unrestricted` unless startup flags or settings override it.
|
|
259
|
+
|
|
260
|
+
The removed `memory` mode is invalid; memory access is governed by normal tool permissions plus the server/filesystem checks on the path used. These helper guardrails do not restrict raw Bash/API access. `permissions.mode` supplies a persisted startup default, rule lists still take precedence, and channel accounts have their own `defaultPermissionMode`. Inspect all three when channel approvals differ from the interactive CLI.
|
|
261
|
+
|
|
262
|
+
Rule examples:
|
|
263
|
+
|
|
264
|
+
```json
|
|
265
|
+
{
|
|
266
|
+
"permissions": {
|
|
267
|
+
"mode": "standard",
|
|
268
|
+
"allow": ["Bash(git diff:*)", "Read(src/**)"],
|
|
269
|
+
"deny": ["Bash(rm -rf:*)"],
|
|
270
|
+
"ask": ["Write(**/*.md)"],
|
|
271
|
+
"alwaysAsk": ["Bash(git push:*)"]
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Rule types:
|
|
277
|
+
|
|
278
|
+
| Type | Behavior |
|
|
279
|
+
| --- | --- |
|
|
280
|
+
| `allow` | Approve matching calls |
|
|
281
|
+
| `deny` | Block matching calls |
|
|
282
|
+
| `ask` | Request approval in normal permission modes |
|
|
283
|
+
| `alwaysAsk` | Request approval even in unrestricted/yolo mode |
|
|
284
|
+
|
|
285
|
+
Add a rule with the helper:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
python3 <SKILL_DIR>/scripts/add_permission.py \
|
|
289
|
+
--rule "Bash(git push:*)" \
|
|
290
|
+
--type alwaysAsk \
|
|
291
|
+
--scope user \
|
|
292
|
+
--confirm-user-scope
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
`add_permission.py` only adds rules. Remove rules manually for now. User/global writes require `--confirm-user-scope`; use `--dry-run` to preview. Use project or local scope only when the current working directory is deliberately the project root.
|
|
296
|
+
|
|
297
|
+
## Mods
|
|
298
|
+
|
|
299
|
+
Use mods when the user wants deterministic runtime behavior that cannot be represented as a simple setting. Managed mods are global for the user install, not per-agent:
|
|
300
|
+
|
|
301
|
+
- new tools or command adapters
|
|
302
|
+
- slash commands
|
|
303
|
+
- statusline rendering
|
|
304
|
+
- local model/provider adapters
|
|
305
|
+
- permission overlays for mod-provided tools
|
|
306
|
+
- lightweight UI panels
|
|
307
|
+
|
|
308
|
+
Load `creating-mods` before implementing mods. Load `customizing-commands` for slash commands and `customizing-statusline` for statusline work.
|
|
309
|
+
|
|
310
|
+
Inspect and control managed mod packages with:
|
|
311
|
+
|
|
312
|
+
```bash
|
|
313
|
+
letta mods list
|
|
314
|
+
letta mods disable <package-spec>
|
|
315
|
+
letta mods enable <package-spec>
|
|
316
|
+
letta mods remove <package-spec>
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Run `/reload` in active sessions afterward. Loose source files and agent-scoped mods are not individually registry-toggleable; move, rename, or remove the file, or use `--no-mods` / `LETTA_DISABLE_MODS=1` to disable all mods for a new process.
|
|
320
|
+
|
|
321
|
+
## Skills
|
|
322
|
+
|
|
323
|
+
Use skills when the user wants you to become good at a repeatable workflow. Sources are discovered in this order:
|
|
324
|
+
|
|
325
|
+
1. Project skills: `.agents/skills/` with `.skills/` as legacy fallback
|
|
326
|
+
2. Agent skills: `$MEMORY_DIR/skills/`
|
|
327
|
+
3. Global skills: `~/.letta/skills/`
|
|
328
|
+
4. Bundled skills
|
|
329
|
+
|
|
330
|
+
Load `creating-skills` to create or edit a skill. Load `acquiring-skills` when the user asks for a capability you do not already have. Project, global, bundled, and agent-owned skills have different visibility; verify the target scope before changing skills another agent may load.
|
|
331
|
+
|
|
332
|
+
## Provider connections
|
|
333
|
+
|
|
334
|
+
Provider connection is agent-executable through `letta connect`. This is separate from `LETTA_API_KEY`, which authenticates Letta API requests. Provider connections may be visible to the same account/server; treat that as credential scope to verify, not as a critical exploit by itself.
|
|
335
|
+
|
|
336
|
+
Inspect the installed command shape first:
|
|
337
|
+
|
|
338
|
+
```bash
|
|
339
|
+
letta connect --help
|
|
340
|
+
letta connect <provider> --help
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Use the provider-specific command supported by the installed binary. Current examples include:
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
letta connect chatgpt
|
|
347
|
+
letta connect codex --method device-code
|
|
348
|
+
letta connect lmstudio --base-url http://127.0.0.1:1234/v1 --timeout 600s
|
|
349
|
+
letta connect bedrock --method profile --profile "$AWS_PROFILE" --region "$AWS_REGION"
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Before connecting, verify whether the target agent/backend is API/Constellation or local. A provider saved to the wrong backend does not configure the current agent.
|
|
353
|
+
|
|
354
|
+
Never print provider keys. Shell expansion such as `--api-key "$OPENAI_API_KEY"` still puts the resolved secret in process argv, where process listings may expose it. Prefer the command's interactive secret prompt in a trusted TTY. If no safer input path exists, stop for explicit user approval rather than passing a provider secret autonomously. Browser login, device-code confirmation, or account consent also requires human consent; do not claim success before it completes.
|
|
355
|
+
|
|
356
|
+
After connecting, verify the provider/model from the same backend and process that will run the agent. Do not infer success from a saved credential alone.
|
|
357
|
+
|
|
358
|
+
## Channels
|
|
359
|
+
|
|
360
|
+
Use channels when the user wants to talk through Slack, Discord, Telegram, WhatsApp, or Signal.
|
|
361
|
+
|
|
362
|
+
Useful commands:
|
|
363
|
+
|
|
364
|
+
```bash
|
|
365
|
+
letta channels status
|
|
366
|
+
letta channels configure <channel>
|
|
367
|
+
letta channels install <channel>
|
|
368
|
+
letta channels route list --channel <channel>
|
|
369
|
+
letta channels pair --channel <channel> --code <code> --agent <agent-id> --conversation <conversation-id>
|
|
370
|
+
letta server --channels <channel>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
Channel state lives under `~/.letta/channels/<channel>/` (`config.yaml`, `accounts.json`, routing/pairing files, and channel runtimes). Account tokens may be plaintext in `file` mode or keyring placeholders in `keyring`/`auto` mode. Configure storage with `channelCredentialsStore` (`file`, `keyring`, `auto`) or `LETTA_CHANNEL_CREDENTIALS_STORE`; do not treat keyring placeholders as usable secrets and do not print tokens. Channel configuration and pairing can route external messages to other agents/conversations; verify IDs and get human consent for interactive authorization.
|
|
374
|
+
|
|
375
|
+
`letta channels configure <channel>` is an interactive TTY wizard. Do not launch it as unattended work or claim setup succeeded while it is waiting for input; hand the authorization/setup step to the user.
|
|
376
|
+
|
|
377
|
+
Changing the credential-store mode does not migrate existing tokens. A file/keyring mismatch can make an otherwise configured listener fail with `invalid_auth`; verify where credentials are stored before changing the mode.
|
|
378
|
+
|
|
379
|
+
## Schedules
|
|
380
|
+
|
|
381
|
+
Use `scheduling-tasks` for reminders and recurring prompts. Under the hood it uses `letta cron`.
|
|
382
|
+
|
|
383
|
+
Examples:
|
|
384
|
+
|
|
385
|
+
```bash
|
|
386
|
+
letta cron list
|
|
387
|
+
letta cron add --name "weekly-review" --description "Weekly project review" --prompt "Ask the user for the weekly project review." --cron "0 9 * * 1" --agent "$AGENT_ID" --conversation "$CONVERSATION_ID"
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
Scheduled tasks fire only while a Letta session/listener is running. Cron bindings can target other agents/conversations visible to the account; verify agent and conversation IDs explicitly when exact routing matters.
|
|
391
|
+
|
|
392
|
+
## CLI startup flags
|
|
393
|
+
|
|
394
|
+
Some behavior is easiest to change at startup:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
letta --model <model-id-or-handle>
|
|
398
|
+
letta --system <preset-id>
|
|
399
|
+
letta --system-custom /path/to/system.txt
|
|
400
|
+
letta --toolset auto
|
|
401
|
+
letta --permission-mode standard
|
|
402
|
+
letta --skills /path/to/skills
|
|
403
|
+
letta --skill-sources all,bundled,global,agent,project
|
|
404
|
+
letta --pre-load-skills self-configuration,creating-mods
|
|
405
|
+
letta --no-mods
|
|
406
|
+
letta --reflection-trigger step-count --reflection-step-count 25
|
|
407
|
+
letta --backend local
|
|
408
|
+
letta --memfs
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
Startup flags affect a new process only. They do not rewrite an already-running listener. Persist durable defaults in settings or server fields instead.
|
|
412
|
+
|
|
413
|
+
### Existing listeners and long-running processes
|
|
414
|
+
|
|
415
|
+
Before starting, replacing, or stopping a listener, inspect existing Letta processes and determine ownership: interactive shell, Desktop, launchd/systemd, supervisor, or another agent.
|
|
416
|
+
|
|
417
|
+
Do not start a second listener for the same channel accounts merely to apply new flags. Never stop or restart an existing listener without explicit coordination and user approval. Prefer changing the owned service configuration and then performing one approved restart.
|
|
418
|
+
|
|
419
|
+
## References
|
|
420
|
+
|
|
421
|
+
- [`references/api-patch-examples.md`](references/api-patch-examples.md) — manual API and SDK patch examples
|
|
422
|
+
- [`references/model-settings.md`](references/model-settings.md) — provider-specific model settings shapes
|
|
423
|
+
- [`references/compaction-prompt-patterns.md`](references/compaction-prompt-patterns.md) — compaction prompt templates
|
|
424
|
+
|
|
425
|
+
## Helper scripts
|
|
426
|
+
|
|
427
|
+
| Script | Purpose |
|
|
428
|
+
| --- | --- |
|
|
429
|
+
| `scripts/update-agent-settings.ts` | Show or patch agent/conversation server settings safely |
|
|
430
|
+
| `scripts/update-compaction-prompt.ts` | Preserve existing compaction settings while replacing the prompt |
|
|
431
|
+
| `scripts/add_permission.py` | Add allow/deny/ask/alwaysAsk rules to a chosen settings scope |
|
|
432
|
+
| `scripts/show_config.py` | Show runtime/local settings without dumping secret values |
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
# API Patch Examples
|
|
2
|
+
|
|
3
|
+
Raw curl and SDK calls bypass the helper guardrails. They are useful for recovery, but they are not safer. Use `LETTA_BASE_URL` together with the current runtime's `LETTA_API_KEY`; never hard-code `api.letta.com` or let an SDK default redirect a local/self-hosted agent to Cloud. Verify the target agent/conversation ID, remember that the key may authorize other agents visible to the same account/server, and never paste literal secrets into commands or source files.
|
|
4
|
+
|
|
5
|
+
## General updater script
|
|
6
|
+
|
|
7
|
+
Use `scripts/update-agent-settings.ts` for dry-runable patches.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Examples:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Agent context window
|
|
17
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
18
|
+
--target agent \
|
|
19
|
+
--context-window-limit 64000 \
|
|
20
|
+
--dry-run
|
|
21
|
+
|
|
22
|
+
# Conversation context window
|
|
23
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
24
|
+
--target conversation \
|
|
25
|
+
--conversation-id "$CONVERSATION_ID" \
|
|
26
|
+
--context-window-limit 64000
|
|
27
|
+
|
|
28
|
+
# Agent rename and description update; values must be non-empty
|
|
29
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
30
|
+
--target agent \
|
|
31
|
+
--agent-id "$AGENT_ID" \
|
|
32
|
+
--name "repo-maintainer" \
|
|
33
|
+
--description "Maintains repository configuration and review-ready PRs." \
|
|
34
|
+
--dry-run
|
|
35
|
+
|
|
36
|
+
# Persistent model update, preserving current model_settings and merging a JSON patch
|
|
37
|
+
cat >/tmp/model-settings.json <<'JSON'
|
|
38
|
+
{
|
|
39
|
+
"provider_type": "openai",
|
|
40
|
+
"parallel_tool_calls": true,
|
|
41
|
+
"reasoning": { "reasoning_effort": "medium" }
|
|
42
|
+
}
|
|
43
|
+
JSON
|
|
44
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
45
|
+
--target agent \
|
|
46
|
+
--model openai/gpt-5.2 \
|
|
47
|
+
--model-settings-file /tmp/model-settings.json \
|
|
48
|
+
--merge-model-settings
|
|
49
|
+
|
|
50
|
+
# System prompt replacement from file; can self-brick the agent
|
|
51
|
+
npx tsx <SKILL_DIR>/scripts/update-agent-settings.ts \
|
|
52
|
+
--target agent \
|
|
53
|
+
--system-file /tmp/new-system-prompt.txt \
|
|
54
|
+
--confirm-system-replacement
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Manual curl with preserved compaction settings
|
|
58
|
+
|
|
59
|
+
Bad compaction prompts cause delayed context loss. Confirm the target agent. The `curl` header form below can expose `LETTA_API_KEY` to process-list readers on some systems; prefer a trusted shell and keep secrets out of logs and committed files.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
prompt_file=/tmp/compaction-prompt.txt
|
|
63
|
+
base_url="${LETTA_BASE_URL%/}"
|
|
64
|
+
current=$(curl -sS "$base_url/v1/agents/$AGENT_ID" \
|
|
65
|
+
-H "Authorization: Bearer $LETTA_API_KEY")
|
|
66
|
+
|
|
67
|
+
jq -n \
|
|
68
|
+
--arg prompt "$(cat "$prompt_file")" \
|
|
69
|
+
--argjson current "$(printf '%s' "$current" | jq '.compaction_settings // {}')" \
|
|
70
|
+
'{ compaction_settings: ($current + {
|
|
71
|
+
mode: "self_compact_sliding_window",
|
|
72
|
+
prompt: $prompt,
|
|
73
|
+
clip_chars: 50000
|
|
74
|
+
}) }' > /tmp/compaction-patch.json
|
|
75
|
+
|
|
76
|
+
curl -sS -X PATCH "$base_url/v1/agents/$AGENT_ID" \
|
|
77
|
+
-H "Authorization: Bearer $LETTA_API_KEY" \
|
|
78
|
+
-H "Content-Type: application/json" \
|
|
79
|
+
--data-binary @/tmp/compaction-patch.json
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## TypeScript SDK
|
|
83
|
+
|
|
84
|
+
SDK calls use the same account token authority as raw API calls. Check IDs before update calls; do not hard-code provider keys or other secrets in the patch body.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
import Letta from "@letta-ai/letta-client";
|
|
88
|
+
|
|
89
|
+
const client = new Letta({
|
|
90
|
+
apiKey: process.env.LETTA_API_KEY!,
|
|
91
|
+
baseURL: process.env.LETTA_BASE_URL!,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
await client.agents.update(process.env.AGENT_ID!, {
|
|
95
|
+
model: "openai/gpt-5.2",
|
|
96
|
+
contextWindowLimit: 64000,
|
|
97
|
+
modelSettings: {
|
|
98
|
+
providerType: "openai",
|
|
99
|
+
parallelToolCalls: true,
|
|
100
|
+
reasoning: { reasoningEffort: "medium" },
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Python SDK
|
|
106
|
+
|
|
107
|
+
The Python client also bypasses helper mismatch and confirmation checks. Use it only after explicit target verification.
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
import os
|
|
111
|
+
from letta_client import Letta
|
|
112
|
+
|
|
113
|
+
client = Letta(
|
|
114
|
+
api_key=os.environ["LETTA_API_KEY"],
|
|
115
|
+
base_url=os.environ["LETTA_BASE_URL"],
|
|
116
|
+
)
|
|
117
|
+
client.agents.update(
|
|
118
|
+
agent_id=os.environ["AGENT_ID"],
|
|
119
|
+
model="openai/gpt-5.2",
|
|
120
|
+
context_window_limit=64000,
|
|
121
|
+
model_settings={
|
|
122
|
+
"provider_type": "openai",
|
|
123
|
+
"parallel_tool_calls": True,
|
|
124
|
+
"reasoning": {"reasoning_effort": "medium"},
|
|
125
|
+
},
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
client.conversations.update(
|
|
129
|
+
conversation_id=os.environ["CONVERSATION_ID"],
|
|
130
|
+
context_window_limit=64000,
|
|
131
|
+
)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## TypeScript fetch
|
|
135
|
+
|
|
136
|
+
Fetch is raw API access. Recreate the same checks manually: current target ID, intended scope, no literal secrets, and an explicit human-approved plan for system or compaction prompt replacement.
|
|
137
|
+
|
|
138
|
+
```typescript
|
|
139
|
+
const baseUrl = process.env.LETTA_BASE_URL!;
|
|
140
|
+
const agentId = process.env.AGENT_ID!;
|
|
141
|
+
const apiKey = process.env.LETTA_API_KEY!;
|
|
142
|
+
|
|
143
|
+
await fetch(`${baseUrl}/v1/agents/${agentId}`, {
|
|
144
|
+
method: "PATCH",
|
|
145
|
+
headers: {
|
|
146
|
+
Authorization: `Bearer ${apiKey}`,
|
|
147
|
+
"Content-Type": "application/json",
|
|
148
|
+
},
|
|
149
|
+
body: JSON.stringify({
|
|
150
|
+
name: "repo-maintainer",
|
|
151
|
+
description: "Maintains repository configuration and review-ready PRs.",
|
|
152
|
+
model: "openai/gpt-5.2",
|
|
153
|
+
context_window_limit: 272000,
|
|
154
|
+
model_settings: {
|
|
155
|
+
provider_type: "openai",
|
|
156
|
+
parallel_tool_calls: true,
|
|
157
|
+
reasoning: { reasoning_effort: "medium" },
|
|
158
|
+
},
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
```
|