@letta-ai/letta-code 0.27.8 → 0.27.9

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.
Files changed (35) hide show
  1. package/README.md +2 -2
  2. package/dist/app-server-client.js +387 -0
  3. package/dist/app-server-client.js.map +10 -0
  4. package/dist/types/app-server-client.d.ts +99 -0
  5. package/dist/types/app-server-client.d.ts.map +1 -0
  6. package/dist/types/types/app-server-protocol.d.ts +3 -0
  7. package/dist/types/types/app-server-protocol.d.ts.map +1 -0
  8. package/dist/types/types/protocol.d.ts.map +1 -0
  9. package/dist/types/types/protocol_v2.d.ts +2277 -0
  10. package/dist/types/types/protocol_v2.d.ts.map +1 -0
  11. package/letta.js +21106 -18642
  12. package/package.json +11 -2
  13. package/scripts/check-test-coverage.cjs +1 -1
  14. package/scripts/run-unit-tests.cjs +1 -1
  15. package/skills/{creating-extensions → creating-mods}/SKILL.md +29 -29
  16. package/skills/{creating-extensions → creating-mods}/references/architecture.md +9 -9
  17. package/skills/{creating-extensions → creating-mods}/references/commands.md +10 -10
  18. package/skills/{creating-extensions → creating-mods}/references/events.md +10 -10
  19. package/skills/{creating-extensions → creating-mods}/references/permissions.md +3 -3
  20. package/skills/{creating-extensions → creating-mods}/references/plan-mode.md +10 -8
  21. package/skills/{creating-extensions → creating-mods}/references/providers.md +7 -7
  22. package/skills/{creating-extensions → creating-mods}/references/tools.md +20 -2
  23. package/skills/{creating-extensions → creating-mods}/references/ui.md +4 -4
  24. package/skills/customizing-commands/SKILL.md +18 -18
  25. package/skills/customizing-statusline/SKILL.md +11 -11
  26. package/skills/customizing-statusline/references/api.md +8 -8
  27. package/skills/customizing-statusline/references/examples.md +1 -1
  28. package/skills/customizing-statusline/references/migration.md +1 -1
  29. package/skills/editing-letta-code-desktop-preferences/SKILL.md +67 -0
  30. package/skills/image-generation/SKILL.md +22 -12
  31. package/skills/modifying-the-harness/SKILL.md +21 -2
  32. package/skills/modifying-the-harness/scripts/add_permission.py +2 -1
  33. package/skills/modifying-the-harness/scripts/show_config.py +4 -3
  34. package/dist/types/protocol.d.ts.map +0 -1
  35. /package/dist/types/{protocol.d.ts → types/protocol.d.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@letta-ai/letta-code",
3
- "version": "0.27.8",
3
+ "version": "0.27.9",
4
4
  "description": "Letta Code is a CLI tool for interacting with stateful Letta agents from the terminal.",
5
5
  "type": "module",
6
6
  "packageManager": "bun@1.3.0",
@@ -14,11 +14,20 @@
14
14
  "scripts",
15
15
  "skills",
16
16
  "vendor",
17
+ "dist/app-server-client.js",
18
+ "dist/app-server-client.js.map",
17
19
  "dist/types",
18
20
  "docs"
19
21
  ],
20
22
  "exports": {
21
23
  ".": "./letta.js",
24
+ "./app-server-protocol": {
25
+ "types": "./dist/types/app-server-protocol.d.ts"
26
+ },
27
+ "./app-server-client": {
28
+ "types": "./dist/types/app-server-client.d.ts",
29
+ "import": "./dist/app-server-client.js"
30
+ },
22
31
  "./protocol": {
23
32
  "types": "./dist/types/protocol.d.ts"
24
33
  }
@@ -35,8 +44,8 @@
35
44
  "access": "public"
36
45
  },
37
46
  "dependencies": {
47
+ "@earendil-works/pi-ai": "^0.79.1",
38
48
  "@letta-ai/letta-client": "^1.10.2",
39
- "@earendil-works/pi-ai": "^0.78.1",
40
49
  "@pierre/diffs": "1.2.2",
41
50
  "glob": "^13.0.0",
42
51
  "ink-link": "^5.0.0",
@@ -73,9 +73,9 @@ const ciDirs = [
73
73
  "src/cli",
74
74
  "src/cron",
75
75
  "src/experiments",
76
- "src/extensions",
77
76
  "src/hooks",
78
77
  "src/lsp",
78
+ "src/mods",
79
79
  "src/permissions",
80
80
  "src/providers",
81
81
  "src/queue",
@@ -13,9 +13,9 @@ const dirs = [
13
13
  "src/cli",
14
14
  "src/cron",
15
15
  "src/experiments",
16
- "src/extensions",
17
16
  "src/hooks",
18
17
  "src/lsp",
18
+ "src/mods",
19
19
  "src/permissions",
20
20
  "src/providers",
21
21
  "src/queue",
@@ -1,42 +1,42 @@
1
1
  ---
2
- name: creating-extensions
3
- description: Creates and edits trusted local Letta Code extensions, including tools, slash commands, local-only model providers, lifecycle/turn events, scoped conversation helpers, panels, status values, and capability-gated behavior. Use when asked to make an extension, add an agent-callable tool, add a slash command, add a local provider/model adapter, transform turns, react to app events, or add lightweight extension UI outside the dedicated /statusline flow.
2
+ name: creating-mods
3
+ description: Creates and edits trusted local Letta Code mods, including tools, slash commands, local-only model providers, lifecycle/turn events, scoped conversation helpers, panels, status values, and capability-gated behavior. Use when asked to make a mod, add an agent-callable tool, add a slash command, add a local provider/model adapter, transform turns, react to app events, or add lightweight mod UI outside the dedicated /statusline flow.
4
4
  ---
5
5
 
6
- # Creating Extensions
6
+ # Creating Mods
7
7
 
8
- Use this skill to create or update trusted global Letta Code extensions in:
8
+ Use this skill to create or update trusted global Letta Code mods in:
9
9
 
10
10
  ```text
11
- ~/.letta/extensions/
11
+ ~/.letta/mods/
12
12
  ```
13
13
 
14
- Extensions are trusted local apps for Letta Code. They add small composable capabilities through extension APIs, not by importing app internals. Prefer scoped handles (`ctx.conversation`, `ctx.cwd`, `ctx.agent`, `letta.getContext()`) and guard optional UI with `letta.capabilities`.
14
+ Mods are trusted local code for Letta Code. They add small composable capabilities through mod APIs, not by importing app internals. Prefer scoped handles (`ctx.conversation`, `ctx.cwd`, `ctx.agent`, `letta.getContext()`) and guard optional UI with `letta.capabilities`.
15
15
 
16
- Capabilities vary by surface. TUI/headless may load tools, commands, events, UI, and providers; the desktop listener loads provider-only extensions for local provider discovery. Always guard optional capabilities.
16
+ Capabilities vary by surface. TUI/headless may load tools, commands, events, UI, and providers; the desktop listener loads provider-only mods for local provider discovery. Always guard optional capabilities.
17
17
 
18
18
  ## Choose the right capability
19
19
 
20
20
  | User wants | Build |
21
21
  | --- | --- |
22
- | Agent/model should autonomously call a local capability | Extension tool |
23
- | User wants `/foo` to send a prompt or run local UI logic | Extension command |
24
- | Slash command represents a reusable agent workflow | Skill + thin extension command |
22
+ | Agent/model should autonomously call a local capability | Mod tool |
23
+ | User wants `/foo` to send a prompt or run local UI logic | Mod command |
24
+ | Slash command represents a reusable agent workflow | Skill + thin mod command |
25
25
  | Command should work while the main agent is busy | Command with `runWhenBusy: true`, `handled`, panel/status, and usually `ctx.conversation.fork()` |
26
26
  | Show transient output above input | Panel, usually from a command |
27
27
  | Show small persistent state | Status value |
28
28
  | React to app/session lifecycle or transform outbound turns | Event |
29
29
  | Enforce dynamic allow/ask/deny policy for tool calls | Permission overlay |
30
- | Add a custom model/API provider for local agents | Provider extension (local agents only) |
30
+ | Add a custom model/API provider for local agents | Provider mod (local agents only) |
31
31
  | Change the bottom statusline appearance | Use `customizing-statusline`, not this skill |
32
32
 
33
33
  Default to a **tool** when the model should decide when to use the capability. Default to a **command** when the human explicitly invokes it. Compose capabilities when the UX needs it, e.g. command + panel + scoped conversation fork.
34
34
 
35
35
  ## Workflow
36
36
 
37
- 1. Inspect `~/.letta/extensions/` for related files.
38
- 2. Preserve unrelated extension code. Prefer a focused new file if merging would be messy.
39
- 3. Choose the extension shape and load only the needed recipe:
37
+ 1. Inspect `~/.letta/mods/` for related files.
38
+ 2. Preserve unrelated mod code. Prefer a focused new file if merging would be messy.
39
+ 3. Choose the mod shape and load only the needed recipe:
40
40
  - tools: `references/tools.md`
41
41
  - commands: `references/commands.md`
42
42
  - local custom providers: `references/providers.md`
@@ -44,13 +44,13 @@ Default to a **tool** when the model should decide when to use the capability. D
44
44
  - permissions: `references/permissions.md`
45
45
  - panels/status/capabilities: `references/ui.md`
46
46
  - complex plan-mode composition: `references/plan-mode.md`
47
- 4. For multi-capability or stateful extensions, also read `references/architecture.md`.
48
- 5. Write a single-file extension unless the user asks for something larger.
47
+ 4. For multi-capability or stateful mods, also read `references/architecture.md`.
48
+ 5. Write a single-file mod unless the user asks for something larger.
49
49
  6. Return disposers for registered providers/commands/tools/events, timers, subscriptions, and panels that should close on reload.
50
50
  7. Do a basic review: valid names, descriptions present, schemas are object schemas, optional capabilities guarded, scoped APIs used, cleanup returned.
51
- 8. Tell the user the absolute file path changed and to run `/reload`. If an extension breaks startup or command handling, recover with `letta --no-extensions` or `LETTA_DISABLE_EXTENSIONS=1 letta`.
51
+ 8. Tell the user the absolute file path changed and to run `/reload`. If a mod breaks startup or command handling, recover with `letta --no-mods` or `LETTA_DISABLE_MODS=1 letta`.
52
52
 
53
- ## Core extension shape
53
+ ## Core mod shape
54
54
 
55
55
  ```ts
56
56
  export default function activate(letta) {
@@ -93,25 +93,25 @@ letta.capabilities.ui.customStatuslineRenderer
93
93
  - `forked.sendMessageStream([...])` to stream from a fork
94
94
  - In tools, use `ctx.conversation.getHistory()` when the tool needs recent context.
95
95
  - Use `letta.client` only for server-specific Letta API calls; do not use it as a substitute for scoped conversation helpers.
96
- - Do not import `@/backend`, `@/cli`, or other Letta Code internals from extension files.
96
+ - Do not import `@/backend`, `@/cli`, or other Letta Code internals from mod files.
97
97
 
98
98
  ## Diagnostics
99
99
 
100
- Use `letta.diagnostics.report({ message, severity })` sparingly as a debug utility for extension setup/runtime problems an agent should inspect, such as missing required environment variables or failed local configuration. Default severity is `"error"`; use `severity: "warning"` only for optional/degraded behavior. Keep messages short and actionable, and do not dump routine logs or large state.
100
+ Use `letta.diagnostics.report({ message, severity })` sparingly as a debug utility for mod setup/runtime problems an agent should inspect, such as missing required environment variables or failed local configuration. Default severity is `"error"`; use `severity: "warning"` only for optional/degraded behavior. Keep messages short and actionable, and do not dump routine logs or large state.
101
101
 
102
- Agents can inspect local extension diagnostics at:
102
+ Agents can inspect local mod diagnostics at:
103
103
 
104
104
  ```text
105
- ~/.letta/extensions/diagnostics/latest.json
105
+ ~/.letta/mods/diagnostics/latest.json
106
106
  ```
107
107
 
108
108
  ## Rules
109
109
 
110
- - Global trusted code only for now. Do not create project extensions.
111
- - Custom provider extensions are local-backend/local-agent only. They do not add providers for Constellation/cloud agents.
112
- - Provider extensions may run in a provider-only listener context; keep provider registration independent from commands/tools/UI and guard everything else.
110
+ - Global trusted code only for now. Do not create project mods.
111
+ - Custom provider mods are local-backend/local-agent only. They do not add providers for Constellation/cloud agents.
112
+ - Provider mods may run in a provider-only listener context; keep provider registration independent from commands/tools/UI and guard everything else.
113
113
  - Do not assume extra npm packages are available.
114
- - Do not do surprising side effects on startup; extensions activate on app start and `/reload`.
114
+ - Do not do surprising side effects on startup; mods activate on app start and `/reload`.
115
115
  - Keep user-facing output short and intentional.
116
116
  - Prefer Node/Bun standard APIs (`node:child_process`, `node:fs`, etc.) for local work.
117
117
  - For shell execution, prefer `execFile`/`spawn` over shell strings.
@@ -119,16 +119,16 @@ Agents can inspect local extension diagnostics at:
119
119
  - For `runWhenBusy: true`, do not return `prompt`; return `handled` and own the UI/background work.
120
120
  - Treat `turn_start` as powerful trusted code: keep transforms narrow and unsurprising.
121
121
 
122
- ## Pre-flight checklist for complex extensions
122
+ ## Pre-flight checklist for complex mods
123
123
 
124
124
  Before finishing, verify:
125
125
 
126
- - The extension has one clear owner/file and does not mix unrelated features.
126
+ - The mod has one clear owner/file and does not mix unrelated features.
127
127
  - Command/tool IDs are valid; command overrides of built-ins are intentional, and tool IDs do not collide with built-ins.
128
128
  - Tool descriptions explain when the model should call them.
129
129
  - JSON schemas are object schemas with useful descriptions.
130
130
  - Optional UI/event/statusline APIs are capability-guarded.
131
- - Provider extensions are capability-guarded and clearly documented as local-agent only.
131
+ - Provider mods are capability-guarded and clearly documented as local-agent only.
132
132
  - Timers, intervals, event registrations, and panels are cleaned up in a disposer.
133
133
  - Busy commands return `{ type: "handled" }` quickly and avoid main-conversation sends.
134
134
  - Conversation work uses `ctx.conversation` or forked handles, not app internals.
@@ -1,6 +1,6 @@
1
- # Extension architecture patterns
1
+ # Mod architecture patterns
2
2
 
3
- Use this reference for non-trivial extensions: multiple capabilities, local state, timers, background model work, or UI.
3
+ Use this reference for non-trivial mods: multiple capabilities, local state, timers, background model work, or UI.
4
4
 
5
5
  ## Contents
6
6
 
@@ -14,14 +14,14 @@ Use this reference for non-trivial extensions: multiple capabilities, local stat
14
14
 
15
15
  ## Mental model
16
16
 
17
- An extension is trusted local code that registers capabilities during activation and cleans them up on reload/shutdown. Keep the public surface small:
17
+ A mod is trusted local code that registers capabilities during activation and cleans them up on reload/shutdown. Keep the public surface small:
18
18
 
19
19
  - activation registers commands/tools/events/UI
20
20
  - command/tool/event handlers receive scoped context
21
21
  - state is local and explicit
22
22
  - cleanup is returned from activation
23
23
 
24
- Do not import Letta Code internals. If the extension API does not expose a capability yet, avoid reaching around it.
24
+ Do not import Letta Code internals. If the mod API does not expose a capability yet, avoid reaching around it.
25
25
 
26
26
  Capabilities vary by host surface. Keep each registration behind the matching `letta.capabilities` guard so one file can run in TUI, headless, and provider-only listener contexts.
27
27
 
@@ -92,14 +92,14 @@ if (letta.capabilities.events.lifecycle && letta.capabilities.ui.statusValues) {
92
92
 
93
93
  ### `turn_start` transform
94
94
 
95
- Use `turn_start` only when the extension needs to inspect or transform the outbound user-message turn. Keep transforms local and predictable. Prefer appending/prepending focused context or replacing explicit shortcuts over broad rewrites.
95
+ Use `turn_start` only when the mod needs to inspect or transform the outbound user-message turn. Keep transforms local and predictable. Prefer appending/prepending focused context or replacing explicit shortcuts over broad rewrites.
96
96
 
97
97
  ## Local state
98
98
 
99
- For small persistent state, use a clearly named file under `~/.letta/extensions/`, for example:
99
+ For small persistent state, use a clearly named file under `~/.letta/mods/`, for example:
100
100
 
101
101
  ```text
102
- ~/.letta/extensions/my-extension.state.json
102
+ ~/.letta/mods/my-mod.state.json
103
103
  ```
104
104
 
105
105
  Use atomic-ish writes when practical: write the full JSON file from an in-memory object after each change. Validate parsed state and fall back gracefully if the file is missing or malformed.
@@ -108,7 +108,7 @@ Keep state separate from source code. Do not store secrets in plain JSON; use ex
108
108
 
109
109
  ## Timers and subscriptions
110
110
 
111
- Timers are okay for active-session behavior, but they only run while the extension engine is alive. Always clear them:
111
+ Timers are okay for active-session behavior, but they only run while the mod engine is alive. Always clear them:
112
112
 
113
113
  ```ts
114
114
  const timer = setInterval(update, 30_000);
@@ -147,4 +147,4 @@ Tools currently receive `ctx.conversation.getHistory()` but not fork/send helper
147
147
  - `runWhenBusy: true` commands return `handled`, not `prompt`.
148
148
  - Background model work uses a forked conversation.
149
149
  - Local filesystem and shell work uses scoped paths and `execFile`/`spawn`.
150
- - Extension output is concise and actionable.
150
+ - Mod output is concise and actionable.
@@ -1,8 +1,8 @@
1
- # Extension command recipes
1
+ # Mod command recipes
2
2
 
3
3
  Use commands when the human explicitly invokes `/foo`.
4
4
 
5
- For complex command-driven extensions with panels, timers, local state, or background model work, also read `architecture.md`.
5
+ For complex command-driven mods with panels, timers, local state, or background model work, also read `architecture.md`.
6
6
 
7
7
  ## Contents
8
8
 
@@ -17,10 +17,10 @@ For complex command-driven extensions with panels, timers, local state, or backg
17
17
 
18
18
  | Need | Use |
19
19
  | --- | --- |
20
- | `/foo` expands to a prompt | Extension command |
21
- | `/foo` starts a complex reusable workflow | Skill + thin extension command |
22
- | Model should call the capability by itself | Extension tool |
23
- | Command needs transient UI while doing local work | Extension command + panel |
20
+ | `/foo` expands to a prompt | Mod command |
21
+ | `/foo` starts a complex reusable workflow | Skill + thin mod command |
22
+ | Model should call the capability by itself | Mod tool |
23
+ | Command needs transient UI while doing local work | Mod command + panel |
24
24
  | Command needs model output while the main agent is busy | `runWhenBusy: true` command + forked `ctx.conversation` |
25
25
 
26
26
  If the command represents a durable agent workflow (for example `/goal`), put the workflow instructions in a skill and keep the command as a small launcher/prompt.
@@ -29,8 +29,8 @@ If the command represents a durable agent workflow (for example `/goal`), put th
29
29
 
30
30
  - Do not include the leading slash. Use `id: "review"`, not `id: "/review"`.
31
31
  - Use a lowercase slug with letters, numbers, and hyphens only.
32
- - Built-in commands like `/reload`, `/model`, `/statusline`, etc. can be overridden by trusted local extensions. Do this intentionally and keep recovery in mind: start with `--no-extensions` or `LETTA_DISABLE_EXTENSIONS=1` if an override breaks command handling.
33
- - Duplicate extension command IDs fail unless `override: true` is intentional.
32
+ - Built-in commands like `/reload`, `/model`, `/statusline`, etc. can be overridden by trusted local mods. Do this intentionally and keep recovery in mind: start with `--no-mods` or `LETTA_DISABLE_MODS=1` if an override breaks command handling.
33
+ - Duplicate mod command IDs fail unless `override: true` is intentional.
34
34
 
35
35
  ## Prompt command
36
36
 
@@ -68,7 +68,7 @@ export default function activate(letta) {
68
68
 
69
69
  return letta.commands.register({
70
70
  id: "whereami",
71
- description: "Show the active extension command context",
71
+ description: "Show the active mod command context",
72
72
  run(ctx) {
73
73
  return {
74
74
  type: "output",
@@ -125,4 +125,4 @@ const stream = await forked.sendMessageStream([
125
125
 
126
126
  Do not send directly to the active conversation from a busy command; fork first unless the user explicitly asked to affect the main conversation later.
127
127
 
128
- For a worked multi-capability extension that combines commands, tools, events, permissions, and local state, see `plan-mode.md`.
128
+ For a worked multi-capability mod that combines commands, tools, events, permissions, and local state, see `plan-mode.md`.
@@ -1,6 +1,6 @@
1
- # Extension event recipes
1
+ # Mod event recipes
2
2
 
3
- Use events when trusted local code should react to app/session changes or transform outbound turns without the human explicitly invoking a command. For event-driven extensions with state, timers, panels, or background model work, also read `architecture.md`.
3
+ Use events when trusted local code should react to app/session changes or transform outbound turns without the human explicitly invoking a command. For event-driven mods with state, timers, panels, or background model work, also read `architecture.md`.
4
4
 
5
5
  ## Contents
6
6
 
@@ -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
- This is the first slice of the hooks-v2 direction. The long-term goal is for typed extension events to replace settings-based hooks. Existing hooks still own blocking decisions and model feedback injection until each event has a typed return contract.
15
+ This is the first slice of the hooks-v2 direction. The long-term goal is for typed mod events to replace settings-based hooks. Existing hooks still own blocking decisions and model feedback injection until each event has a typed return contract.
16
16
 
17
17
  ## Capabilities
18
18
 
@@ -22,7 +22,7 @@ letta.capabilities.events.tools
22
22
  letta.capabilities.events.turns
23
23
  ```
24
24
 
25
- Guard events when writing portable extensions:
25
+ Guard events when writing portable mods:
26
26
 
27
27
  ```ts
28
28
  export default function activate(letta) {
@@ -115,7 +115,7 @@ Lifecycle handlers are notification-only and should not return values. `turn_sta
115
115
  }
116
116
  ```
117
117
 
118
- `tool_start` fires immediately before a client-side tool executes. This includes built-in tools, extension tools, and external tools executed through the local tool manager. It runs after permission/approval classification and before `PreToolUse` hooks, so trusted local extensions can change the actual executed arguments after the approval UI has already classified the original request. Extension permission overlays are rechecked after `tool_start` on the final args.
118
+ `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 and before `PreToolUse` hooks, 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.
119
119
 
120
120
  Handlers can inspect `event.args`, mutate it directly, or return replacement args:
121
121
 
@@ -134,9 +134,9 @@ letta.events.on("tool_start", (event) => {
134
134
  });
135
135
  ```
136
136
 
137
- Handlers run in registration order. Later handlers see the current args after earlier mutations/returns. If a handler throws, its partial `event.args` mutation is rolled back and the error is recorded as an extension diagnostic.
137
+ Handlers run in registration order. Later handlers see the current args after earlier mutations/returns. If a handler throws, its partial `event.args` mutation is rolled back and the error is recorded as a mod diagnostic.
138
138
 
139
- `tool_start` is intentionally a trusted local extension point: it can rewrite commands, file paths, and other tool inputs before execution. Keep transforms focused and unsurprising.
139
+ `tool_start` is intentionally a trusted local mod point: it can rewrite commands, file paths, and other tool inputs before execution. Keep transforms focused and unsurprising.
140
140
 
141
141
  `turn_start` fires before outbound turns that include a user message. In the TUI this includes normal submits and prompt-style command turns. In headless it includes one-shot prompts and bidirectional user turns.
142
142
 
@@ -166,9 +166,9 @@ letta.events.on("turn_start", (event) => {
166
166
  });
167
167
  ```
168
168
 
169
- Handlers run in registration order. Later handlers see the current input after earlier mutations/returns. If a handler throws, its partial `event.input` mutation is rolled back and the error is recorded as an extension diagnostic.
169
+ Handlers run in registration order. Later handlers see the current input after earlier mutations/returns. If a handler throws, its partial `event.input` mutation is rolled back and the error is recorded as a mod diagnostic.
170
170
 
171
- `turn_start` is intentionally a trusted local extension point: it can rewrite user messages, approval results, and ordering. Keep transforms focused and unsurprising.
171
+ `turn_start` is intentionally a trusted local mod point: it can rewrite user messages, approval results, and ordering. Keep transforms focused and unsurprising.
172
172
 
173
173
  Handlers also receive:
174
174
 
@@ -221,5 +221,5 @@ export default function activate(letta) {
221
221
 
222
222
  - Do not block user flow unless the event's typed contract explicitly supports blocking.
223
223
  - Do not use lifecycle events for safety decisions yet. Existing hooks still own blocking behavior.
224
- - Catch expected local errors if the user-facing outcome matters. Uncaught errors are isolated and recorded as extension diagnostics.
224
+ - Catch expected local errors if the user-facing outcome matters. Uncaught errors are isolated and recorded as mod diagnostics.
225
225
  - Return disposers from activation for event registrations, timers, subscriptions, and status values.
@@ -1,4 +1,4 @@
1
- # Extension permission recipes
1
+ # Mod permission recipes
2
2
 
3
3
  Use permission overlays when trusted local code should participate in tool approval decisions. Prefer permissions over `tool_start` denial for policy: permissions run before approval UI and again before execution on final tool arguments.
4
4
 
@@ -8,7 +8,7 @@ Use permission overlays when trusted local code should participate in tool appro
8
8
  letta.capabilities.permissions
9
9
  ```
10
10
 
11
- Guard registrations when writing portable extensions:
11
+ Guard registrations when writing portable mods:
12
12
 
13
13
  ```ts
14
14
  export default function activate(letta) {
@@ -71,7 +71,7 @@ Composition rules across overlays:
71
71
  - then `allow`
72
72
  - `undefined` means no opinion
73
73
 
74
- User/configured hard denials still win before extension overlays. Extension overlays can override normal auto-allow/default approval behavior, including unrestricted/yolo mode.
74
+ User/configured hard denials still win before mod overlays. Mod overlays can override normal auto-allow/default approval behavior, including unrestricted/yolo mode.
75
75
 
76
76
  ## Two phases
77
77
 
@@ -1,8 +1,8 @@
1
- # Plan mode extension example
1
+ # Plan mode mod example
2
2
 
3
- Use this as the canonical multi-capability extension example. It composes a slash command, model-callable tools, turn reminders, permission overlays, and local state to recreate the old built-in plan-mode flow with extension APIs.
3
+ Use this as the canonical multi-capability mod example. It composes a slash command, model-callable tools, turn reminders, permission overlays, and local state to recreate the old built-in plan-mode flow with mod APIs.
4
4
 
5
- This is a pattern reference, not a full product implementation. Keep local extensions self-contained and avoid importing Letta Code internals.
5
+ This is a pattern reference, not a full product implementation. Keep local mods self-contained and avoid importing Letta Code internals.
6
6
 
7
7
  ## Contents
8
8
 
@@ -46,7 +46,7 @@ Do not use panels for persistent mode state. Panels are transient UI and can be
46
46
 
47
47
  ## State
48
48
 
49
- Use small local state under `~/.letta/extensions/`, keyed by conversation ID:
49
+ Use small local state under `~/.letta/mods/`, keyed by conversation ID:
50
50
 
51
51
  ```ts
52
52
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
@@ -54,7 +54,7 @@ import { homedir } from "node:os";
54
54
  import { join, relative } from "node:path";
55
55
 
56
56
  const PLANS_DIR = join(homedir(), ".letta", "plans");
57
- const STATE_PATH = join(homedir(), ".letta", "extensions", "plan-mode.state.json");
57
+ const STATE_PATH = join(homedir(), ".letta", "mods", "plan-mode.state.json");
58
58
  const GLOBAL_CONVERSATION_ID = "__global__";
59
59
 
60
60
  type PlanSession = {
@@ -81,7 +81,7 @@ function readState(): PlanState {
81
81
  }
82
82
 
83
83
  function writeState(state: PlanState): void {
84
- mkdirSync(join(homedir(), ".letta", "extensions"), { recursive: true });
84
+ mkdirSync(join(homedir(), ".letta", "mods"), { recursive: true });
85
85
  writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
86
86
  }
87
87
  ```
@@ -279,7 +279,9 @@ Shell allowlists are easy to get wrong. Start conservative: allow clearly read-o
279
279
 
280
280
  ## Exit tool
281
281
 
282
- In the extension version, `exit_plan_mode` is not the approval UI. The agent should read the plan file, present the full current plan text with `AskUserQuestion`, then call `exit_plan_mode` only after the user approves.
282
+ In the mod version, `exit_plan_mode` is not the approval UI. The agent should read the plan file, present the full current plan text with `AskUserQuestion`, then call `exit_plan_mode` only after the user approves.
283
+
284
+ Use `approvalPolicy: "alwaysAsk"` so the final state transition still pauses for human confirmation in unrestricted/yolo mode.
283
285
 
284
286
  ```ts
285
287
  if (letta.capabilities.tools) {
@@ -288,7 +290,7 @@ if (letta.capabilities.tools) {
288
290
  description:
289
291
  "Exit plan mode only after the plan file has been written, the full current plan text has been presented with AskUserQuestion, and the user has approved it.",
290
292
  parameters: { type: "object", properties: {}, additionalProperties: false },
291
- requiresApproval: false,
293
+ approvalPolicy: "alwaysAsk",
292
294
  parallelSafe: false,
293
295
  run(ctx) {
294
296
  const session = getSession(ctx.conversation.id);
@@ -1,15 +1,15 @@
1
- # Extension provider recipes
1
+ # Mod provider recipes
2
2
 
3
- Use provider extensions when the user wants a **local agent** to use a model provider that is not built into `/connect` and `/model`.
3
+ Use provider mods when the user wants a **local agent** to use a model provider that is not built into `/connect` and `/model`.
4
4
 
5
- Important: provider extensions are local-backend/local-agent only. They register local provider metadata for the TUI, headless local runtime, and desktop listener. They do not add providers for Constellation/cloud agents.
5
+ Important: provider mods are local-backend/local-agent only. They register local provider metadata for the TUI, headless local runtime, and desktop listener. They do not add providers for Constellation/cloud agents.
6
6
 
7
- For multi-capability extensions that combine a provider with commands, tools, UI, or state, also read `architecture.md`.
7
+ For multi-capability mods that combine a provider with commands, tools, UI, or state, also read `architecture.md`.
8
8
 
9
9
  ## Quick pattern
10
10
 
11
11
  ```ts
12
- // ~/.letta/extensions/kilo.ts
12
+ // ~/.letta/mods/kilo.ts
13
13
  export default function activate(letta) {
14
14
  if (!letta.capabilities.providers) return;
15
15
 
@@ -48,10 +48,10 @@ After `/reload`, the provider appears in local `/connect` and desktop Connect mo
48
48
 
49
49
  - Always guard with `letta.capabilities.providers`.
50
50
  - Prefer `letta.providers.register(...)` over legacy `letta.registerProvider(...)`.
51
- - Keep provider registration independent from commands/tools/UI/events and `letta.client`; the desktop listener loads provider-only extensions.
51
+ - Keep provider registration independent from commands/tools/UI/events and `letta.client`; the desktop listener loads provider-only mods.
52
52
  - Do not hardcode real secrets. `apiKey: "ENV_VAR"` resolves `process.env.ENV_VAR` when present, or lets `/connect` save a local key.
53
53
  - Use stable lowercase provider ids. Model ids must be unprefixed and must not contain `/`.
54
- - Set `api` at provider or model level. Common values include `"openai-completions"`, `"openai-responses"`, `"anthropic-messages"`, and `"bedrock-converse-stream"`; check `src/backend/dev/pi-provider-extension-types.ts` and pi-ai model types before using uncommon values.
54
+ - Set `api` at provider or model level. Common values include `"openai-completions"`, `"openai-responses"`, `"anthropic-messages"`, and `"bedrock-converse-stream"`; check `src/backend/dev/pi-provider-mod-types.ts` and pi-ai model types before using uncommon values.
55
55
 
56
56
  ## Model metadata
57
57
 
@@ -1,8 +1,8 @@
1
- # Extension tool recipes
1
+ # Mod tool recipes
2
2
 
3
3
  Use tools when the agent/model should call a local capability autonomously.
4
4
 
5
- For tools that are part of a larger extension with commands, UI, local state, or events, also read `architecture.md`.
5
+ For tools that are part of a larger mod with commands, UI, local state, or events, also read `architecture.md`.
6
6
 
7
7
  ## Contents
8
8
 
@@ -17,6 +17,7 @@ For tools that are part of a larger extension with commands, UI, local state, or
17
17
  - Description: explain when the model should use it.
18
18
  - Parameters: JSON Schema object. Use `additionalProperties: false` when possible.
19
19
  - `requiresApproval: false` only for read-only, low-risk local introspection.
20
+ - `approvalPolicy: "alwaysAsk"` only for tools that must pause for human approval even in unrestricted/yolo mode.
20
21
  - `parallelSafe: true` only for read-only tools with no shared mutation or long-lived exclusive resource.
21
22
  - Use `ctx.cwd` / `ctx.workingDirectory` as the workspace.
22
23
  - Use `await ctx.conversation.getHistory()` when a tool needs recent conversation context. It returns the most recent messages in chronological order by default.
@@ -124,3 +125,20 @@ letta.tools.register({
124
125
  },
125
126
  });
126
127
  ```
128
+
129
+ ## Always-ask tool
130
+
131
+ Use `approvalPolicy: "alwaysAsk"` when a tool represents a human gate rather than a risky operation. Deny rules and permission overlays still win, but unrestricted/yolo mode will not auto-approve it.
132
+
133
+ ```ts
134
+ letta.tools.register({
135
+ name: "exit_plan_mode",
136
+ description: "Exit plan mode after the user has reviewed and approved the plan.",
137
+ parameters: { type: "object", properties: {}, additionalProperties: false },
138
+ approvalPolicy: "alwaysAsk",
139
+ parallelSafe: false,
140
+ run(ctx) {
141
+ return "Plan approved. You can now start coding.";
142
+ },
143
+ });
144
+ ```
@@ -1,8 +1,8 @@
1
- # Extension UI recipes
1
+ # Mod UI recipes
2
2
 
3
- UI capabilities are optional. Always guard UI work with `letta.capabilities.ui.*` when writing portable extensions.
3
+ UI capabilities are optional. Always guard UI work with `letta.capabilities.ui.*` when writing portable mods.
4
4
 
5
- For UI that belongs to a larger command/event extension, also read `architecture.md` for cleanup and composition patterns.
5
+ For UI that belongs to a larger command/event mod, also read `architecture.md` for cleanup and composition patterns.
6
6
 
7
7
  ## Capabilities
8
8
 
@@ -21,7 +21,7 @@ letta.capabilities.ui.customStatuslineRenderer
21
21
  ```ts
22
22
  if (letta.capabilities.ui.panels) {
23
23
  const panel = letta.ui.openPanel({
24
- id: "my-extension",
24
+ id: "my-mod",
25
25
  content: ["Working…"],
26
26
  order: 100,
27
27
  });
@@ -1,36 +1,36 @@
1
1
  ---
2
2
  name: customizing-commands
3
- description: Creates, edits, and enables Letta Code extension-provided slash commands. Use when the user asks to add a custom /command, slash command, command shortcut, scoped conversation-backed command, or command-driven panel behavior.
3
+ description: Creates, edits, and enables Letta Code mod-provided slash commands. Use when the user asks to add a custom /command, slash command, command shortcut, scoped conversation-backed command, or command-driven panel behavior.
4
4
  ---
5
5
 
6
6
  # Customizing Commands
7
7
 
8
- Use this as the command-specific entrypoint for local extension slash commands. For broader extension work, recipes live in `../creating-extensions/references/commands.md`, `../creating-extensions/references/architecture.md`, `../creating-extensions/references/ui.md`, and `../creating-extensions/references/plan-mode.md`.
8
+ Use this as the command-specific entrypoint for local mod slash commands. For broader mod work, recipes live in `../creating-mods/references/commands.md`, `../creating-mods/references/architecture.md`, `../creating-mods/references/ui.md`, and `../creating-mods/references/plan-mode.md`.
9
9
 
10
- Extension files live in:
10
+ Mod files live in:
11
11
 
12
12
  ```text
13
- ~/.letta/extensions/
13
+ ~/.letta/mods/
14
14
  ```
15
15
 
16
- Use a focused file name, e.g. `~/.letta/extensions/review.ts` or `~/.letta/extensions/commands.ts`.
16
+ Use a focused file name, e.g. `~/.letta/mods/review.ts` or `~/.letta/mods/commands.ts`.
17
17
 
18
18
  ## First decide whether a command is right
19
19
 
20
20
  | User wants | Build |
21
21
  | --- | --- |
22
- | `/foo` sends a prompt or shows local output | Extension command |
23
- | `/foo` starts a reusable agent workflow | Skill + thin extension command |
24
- | Agent/model should autonomously call the capability | Extension tool, not a command |
25
- | Command shows transient progress/results | Extension command + panel |
22
+ | `/foo` sends a prompt or shows local output | Mod command |
23
+ | `/foo` starts a reusable agent workflow | Skill + thin mod command |
24
+ | Agent/model should autonomously call the capability | Mod tool, not a command |
25
+ | Command shows transient progress/results | Mod command + panel |
26
26
  | Command needs model output while the main agent is busy | `runWhenBusy: true` command + forked `ctx.conversation` |
27
27
 
28
- If the command is a durable workflow like `/goal`, put the workflow instructions in a skill and keep the extension command as a small launcher/prompt.
28
+ If the command is a durable workflow like `/goal`, put the workflow instructions in a skill and keep the mod command as a small launcher/prompt.
29
29
 
30
30
  ## Workflow
31
31
 
32
- 1. Inspect `~/.letta/extensions/` for related command files.
33
- 2. Preserve unrelated extension code; create a focused new file if merging is messy.
32
+ 1. Inspect `~/.letta/mods/` for related command files.
33
+ 2. Preserve unrelated mod code; create a focused new file if merging is messy.
34
34
  3. Register with `letta.commands.register()` and guard with `letta.capabilities.commands`.
35
35
  4. Return the unregister function, or a disposer that calls it plus any timer/panel cleanup.
36
36
  5. Tell the user the exact file path changed and to run `/reload`.
@@ -62,7 +62,7 @@ export default function activate(letta) {
62
62
  ## Command result types
63
63
 
64
64
  ```ts
65
- type ExtensionCommandResult =
65
+ type ModCommandResult =
66
66
  | { type: "prompt"; content: string; systemReminder?: boolean }
67
67
  | { type: "output"; output: string; success?: boolean }
68
68
  | { type: "handled" };
@@ -80,11 +80,11 @@ type ExtensionCommandResult =
80
80
  - `runWhenBusy: true` commands must not return `prompt` while the main agent is busy; use scoped conversation helpers/panels and return `handled`.
81
81
  - `showInTranscript: false` commands should usually return `handled`, not `prompt`.
82
82
  - Do not import Letta Code app internals.
83
- - Do not do surprising side effects on startup; extensions activate on app start and `/reload`.
83
+ - Do not do surprising side effects on startup; mods activate on app start and `/reload`.
84
84
 
85
85
  ## More recipes
86
86
 
87
- - Simple output command, panel command, busy-safe conversation command: `../creating-extensions/references/commands.md`
88
- - Complex command architecture, state, cleanup: `../creating-extensions/references/architecture.md`
89
- - Panel/status UI patterns: `../creating-extensions/references/ui.md`
90
- - Worked plan-mode command/tool composition: `../creating-extensions/references/plan-mode.md`
87
+ - Simple output command, panel command, busy-safe conversation command: `../creating-mods/references/commands.md`
88
+ - Complex command architecture, state, cleanup: `../creating-mods/references/architecture.md`
89
+ - Panel/status UI patterns: `../creating-mods/references/ui.md`
90
+ - Worked plan-mode command/tool composition: `../creating-mods/references/plan-mode.md`