@letta-ai/letta-code 0.27.3 → 0.27.5
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/letta.js +22289 -21227
- package/package.json +2 -2
- package/skills/acquiring-skills/SKILL.md +197 -40
- package/skills/creating-extensions/SKILL.md +31 -12
- package/skills/creating-extensions/references/architecture.md +12 -0
- package/skills/creating-extensions/references/commands.md +9 -0
- package/skills/creating-extensions/references/events.md +25 -1
- package/skills/creating-extensions/references/providers.md +208 -0
- package/skills/creating-extensions/references/tools.md +7 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# Extension provider recipes
|
|
2
|
+
|
|
3
|
+
Use provider extensions when the user wants Letta Code local agents to use a model provider that is not built into `/connect` and `/model`.
|
|
4
|
+
|
|
5
|
+
**Important scope:** custom provider extensions are for **local agents only**. They register local provider metadata used by the local backend/TUI/desktop listener. They do not add providers for Constellation/cloud agents.
|
|
6
|
+
|
|
7
|
+
For multi-capability extensions that combine a provider with commands, tools, UI, or state, also read `architecture.md`.
|
|
8
|
+
|
|
9
|
+
## Contents
|
|
10
|
+
|
|
11
|
+
- When to use
|
|
12
|
+
- Surface behavior and limitations
|
|
13
|
+
- Capability guard
|
|
14
|
+
- Basic API-key provider
|
|
15
|
+
- Config fields
|
|
16
|
+
- Dynamic model discovery
|
|
17
|
+
- Connect behavior
|
|
18
|
+
- Review checklist
|
|
19
|
+
|
|
20
|
+
## When to use
|
|
21
|
+
|
|
22
|
+
Use a provider extension when:
|
|
23
|
+
|
|
24
|
+
- the provider API is supported by the local pi-ai adapter stack; OpenAI-compatible providers usually use `api: "openai-completions"` or `api: "openai-responses"`
|
|
25
|
+
- the user wants the provider to appear in local `/connect` / desktop Connect model providers
|
|
26
|
+
- local `/model` should show static or dynamically listed models from that provider
|
|
27
|
+
- local turns should resolve model handles like `kilo/kilo-code`
|
|
28
|
+
|
|
29
|
+
Do **not** use a provider extension when the user is asking to configure a Constellation/cloud provider. For cloud agents, use the product's normal provider configuration path instead.
|
|
30
|
+
|
|
31
|
+
## Surface behavior and limitations
|
|
32
|
+
|
|
33
|
+
- TUI/headless local agents can load provider extensions along with other extension capabilities.
|
|
34
|
+
- The desktop listener loads provider extensions so desktop's local provider UI can list and connect them.
|
|
35
|
+
- Listener provider loading is provider-only. Do not rely on commands, tools, UI, events, or `letta.client` while registering a provider.
|
|
36
|
+
- Provider extensions should use local data, environment variables, local provider connections, and direct `fetch`/Node APIs. They should not use Constellation-specific APIs to make a local provider work.
|
|
37
|
+
|
|
38
|
+
## Capability guard
|
|
39
|
+
|
|
40
|
+
Always guard provider registration:
|
|
41
|
+
|
|
42
|
+
```ts
|
|
43
|
+
export default function activate(letta) {
|
|
44
|
+
if (!letta.capabilities.providers) return;
|
|
45
|
+
|
|
46
|
+
return letta.providers.register("kilo", {
|
|
47
|
+
// provider config
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`letta.registerProvider(id, config)` is also supported, but prefer `letta.providers.register(...)` so the capability being used is explicit.
|
|
53
|
+
|
|
54
|
+
## Basic API-key provider
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
// ~/.letta/extensions/kilo.ts
|
|
58
|
+
export default function activate(letta) {
|
|
59
|
+
if (!letta.capabilities.providers) return;
|
|
60
|
+
|
|
61
|
+
return letta.providers.register("kilo", {
|
|
62
|
+
name: "Kilo",
|
|
63
|
+
description: "Connect to Kilo's OpenAI-compatible API",
|
|
64
|
+
api: "openai-completions",
|
|
65
|
+
baseUrl: "https://api.kilo.example/v1",
|
|
66
|
+
apiKey: "KILO_API_KEY",
|
|
67
|
+
models: [
|
|
68
|
+
{
|
|
69
|
+
id: "kilo-code",
|
|
70
|
+
name: "Kilo Code",
|
|
71
|
+
reasoning: true,
|
|
72
|
+
input: ["text"],
|
|
73
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
74
|
+
contextWindow: 128000,
|
|
75
|
+
maxTokens: 8192,
|
|
76
|
+
},
|
|
77
|
+
],
|
|
78
|
+
connect: {
|
|
79
|
+
fields: [
|
|
80
|
+
{ key: "apiKey", label: "Kilo API Key", secret: true },
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
After `/reload`, this provider appears in local `/connect` and in the desktop local provider page. The model handle is `<provider-id>/<model-id>`, for example `kilo/kilo-code`.
|
|
88
|
+
|
|
89
|
+
## Config fields
|
|
90
|
+
|
|
91
|
+
Common provider config fields:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
{
|
|
95
|
+
name?: string;
|
|
96
|
+
description?: string;
|
|
97
|
+
api?: string; // common: "openai-completions", "openai-responses", "anthropic-messages", "bedrock-converse-stream"
|
|
98
|
+
baseUrl?: string;
|
|
99
|
+
apiKey?: string; // env var name to resolve, e.g. "KILO_API_KEY"
|
|
100
|
+
headers?: Record<string, string>; // values resolve through process.env when present
|
|
101
|
+
authHeader?: boolean; // add Authorization: Bearer <apiKey>
|
|
102
|
+
models?: Array<model>;
|
|
103
|
+
listModels?: (connection) => Promise<Array<model>> | Array<model>;
|
|
104
|
+
connect?: boolean | { fields?: Array<field> };
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Check `src/backend/dev/pi-provider-extension-types.ts` and pi-ai model types before documenting or using an uncommon `api` value.
|
|
109
|
+
|
|
110
|
+
Model entries must include useful local runtime metadata:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
{
|
|
114
|
+
id: "model-id-without-provider-prefix",
|
|
115
|
+
name: "Display Name",
|
|
116
|
+
api?: "openai-completions",
|
|
117
|
+
reasoning: false,
|
|
118
|
+
input: ["text"], // or ["text", "image"]
|
|
119
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
120
|
+
contextWindow: 128000,
|
|
121
|
+
maxTokens: 8192,
|
|
122
|
+
headers?: {},
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Do not set `model.baseUrl` when all models use the provider-level URL. Model-level `baseUrl` overrides the connected provider base URL, so `/connect` base URL overrides will be ignored. Use it only when a specific model intentionally needs a different endpoint.
|
|
127
|
+
|
|
128
|
+
## Dynamic model discovery
|
|
129
|
+
|
|
130
|
+
Use `listModels(connection)` when the provider exposes a models endpoint or the model list depends on connected credentials:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
export default function activate(letta) {
|
|
134
|
+
if (!letta.capabilities.providers) return;
|
|
135
|
+
|
|
136
|
+
return letta.providers.register("kilo", {
|
|
137
|
+
name: "Kilo",
|
|
138
|
+
api: "openai-completions",
|
|
139
|
+
baseUrl: "https://api.kilo.example/v1",
|
|
140
|
+
apiKey: "KILO_API_KEY",
|
|
141
|
+
async listModels(connection) {
|
|
142
|
+
const response = await fetch(`${connection.baseUrl}/models`, {
|
|
143
|
+
headers: {
|
|
144
|
+
Authorization: `Bearer ${connection.apiKey}`,
|
|
145
|
+
...connection.headers,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
throw new Error(`Kilo model list failed: ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
const body = await response.json();
|
|
152
|
+
return body.data.map((model) => ({
|
|
153
|
+
id: model.id,
|
|
154
|
+
name: model.id,
|
|
155
|
+
reasoning: false,
|
|
156
|
+
input: ["text"],
|
|
157
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
158
|
+
contextWindow: 128000,
|
|
159
|
+
maxTokens: 8192,
|
|
160
|
+
}));
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
`connection` contains the provider id/name plus resolved local connection details:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
{
|
|
170
|
+
id: string;
|
|
171
|
+
providerName: string;
|
|
172
|
+
baseUrl?: string;
|
|
173
|
+
apiKey?: string;
|
|
174
|
+
headers?: Record<string, string>;
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Keep dynamic listing lightweight and fail with short actionable errors. If listing is flaky, provide a static `models` fallback instead.
|
|
179
|
+
|
|
180
|
+
## Connect behavior
|
|
181
|
+
|
|
182
|
+
- `connect: undefined` or `connect: true` uses default API key + base URL fields.
|
|
183
|
+
- `connect: { fields: [...] }` customizes local `/connect` / desktop fields.
|
|
184
|
+
- `connect: false` hides the provider from `/connect`; use only when credentials come entirely from environment variables or local code.
|
|
185
|
+
- `apiKey: "ENV_VAR_NAME"` resolves from `process.env.ENV_VAR_NAME` when available. Do not hardcode real secrets.
|
|
186
|
+
- Custom fields are currently required. `placeholder` is display-only, not a default value.
|
|
187
|
+
- If the provider has a normal fixed `baseUrl`, set provider-level `baseUrl` and omit `baseUrl` from `connect.fields`; the local runtime will use the provider-level URL after the API key is saved.
|
|
188
|
+
- Include `{ key: "baseUrl", ... }` only when the user must enter or override the endpoint during connect.
|
|
189
|
+
|
|
190
|
+
Default custom fields use these keys when possible because the local provider store understands them:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
{ key: "apiKey", label: "API Key", secret: true }
|
|
194
|
+
{ key: "baseUrl", label: "Base URL" }
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Review checklist
|
|
198
|
+
|
|
199
|
+
- The extension says or implies the provider is local-agent only when reporting back to the user.
|
|
200
|
+
- `letta.capabilities.providers` is checked before registration.
|
|
201
|
+
- Provider id is stable, lowercase, and suitable as the model-handle prefix.
|
|
202
|
+
- Model ids are unprefixed; Letta Code exposes them as `<provider-id>/<model-id>`.
|
|
203
|
+
- `contextWindow`, `maxTokens`, `input`, and `reasoning` metadata are accurate enough for local selection and context display.
|
|
204
|
+
- Model-level `baseUrl` is omitted unless the model intentionally needs a different endpoint from the provider/connection.
|
|
205
|
+
- `connect.fields` contains only fields the user must type; placeholders are not treated as defaults.
|
|
206
|
+
- Secrets are read from environment/local provider connections, not hardcoded.
|
|
207
|
+
- Provider registration does not depend on commands/tools/UI/events or `letta.client`.
|
|
208
|
+
- The user is told to run `/reload`, then configure the provider through local `/connect` or desktop Connect model providers.
|
|
@@ -4,6 +4,13 @@ Use tools when the agent/model should call a local capability autonomously.
|
|
|
4
4
|
|
|
5
5
|
For tools that are part of a larger extension with commands, UI, local state, or events, also read `architecture.md`.
|
|
6
6
|
|
|
7
|
+
## Contents
|
|
8
|
+
|
|
9
|
+
- Defaults
|
|
10
|
+
- Read-only shell tool
|
|
11
|
+
- Tool with arguments
|
|
12
|
+
- Mutating or risky tool
|
|
13
|
+
|
|
7
14
|
## Defaults
|
|
8
15
|
|
|
9
16
|
- Name: lowercase/underscore tool name, e.g. `branch_summary`.
|