@letta-ai/letta-code 0.30.31 → 0.30.32
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/gateway-core.js +11 -4
- package/dist/gateway-core.js.map +3 -3
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/backend/api/ephemeral-conversations.d.ts +15 -0
- package/dist/types/backend/api/ephemeral-conversations.d.ts.map +1 -0
- package/dist/types/channels/gateway-core.d.ts.map +1 -1
- package/dist/types/providers/byok-providers.d.ts.map +1 -1
- package/dist/types/tools/impl/exec-command.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-runner.d.ts +1 -0
- package/dist/types/tools/impl/shell-runner.d.ts.map +1 -1
- package/dist/types/tools/toolset.d.ts +1 -1
- package/dist/types/tools/toolset.d.ts.map +1 -1
- package/dist/types/types/external-tool-protocol.d.ts +2 -2
- package/dist/types/types/external-tool-protocol.d.ts.map +1 -1
- package/dist/types/types/protocol_v2.d.ts +27 -46
- package/dist/types/types/protocol_v2.d.ts.map +1 -1
- package/dist/types/types/runtime-scope.d.ts +4 -2
- package/dist/types/types/runtime-scope.d.ts.map +1 -1
- package/dist/types/types/runtime-start-protocol.d.ts +21 -0
- package/dist/types/types/runtime-start-protocol.d.ts.map +1 -0
- package/dist/types/websocket/listener/protocol-outbound.d.ts +1 -1
- package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
- package/dist/types/websocket/listener/scope.d.ts +1 -1
- package/dist/types/websocket/listener/scope.d.ts.map +1 -1
- package/letta.js +995 -428
- package/package.json +2 -2
- package/scripts/codex-watch/release-analysis.test.ts +8 -16
- package/scripts/codex-watch/release-analysis.ts +1 -3
- package/scripts/codex-watch/tracker.test.ts +7 -28
- package/scripts/codex-watch/tracker.ts +26 -17
- package/scripts/source-file-size-baseline.json +3 -3
- package/skills/self-configuration/SKILL.md +20 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@letta-ai/letta-code",
|
|
3
|
-
"version": "0.30.
|
|
3
|
+
"version": "0.30.32",
|
|
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.10",
|
|
@@ -119,7 +119,7 @@
|
|
|
119
119
|
"access": "public"
|
|
120
120
|
},
|
|
121
121
|
"dependencies": {
|
|
122
|
-
"@earendil-works/pi-ai": "0.84.
|
|
122
|
+
"@earendil-works/pi-ai": "0.84.3",
|
|
123
123
|
"@janhapke/sharp-electron": "0.35.3-electron.1",
|
|
124
124
|
"@letta-ai/letta-client": "^1.10.2",
|
|
125
125
|
"@letta-ai/trajectory": "0.2.0",
|
|
@@ -23,25 +23,17 @@ const STABLES = [
|
|
|
23
23
|
|
|
24
24
|
describe("Codex stable release backlog", () => {
|
|
25
25
|
test("selects the first release after the durable cursor", () => {
|
|
26
|
-
expect(
|
|
27
|
-
|
|
28
|
-
)
|
|
26
|
+
expect(findNextStableRelease(STABLES, "rust-v0.1.0")?.tag_name).toBe(
|
|
27
|
+
"rust-v0.2.0",
|
|
28
|
+
);
|
|
29
29
|
});
|
|
30
30
|
|
|
31
31
|
test("identifies only adjacent stable release pairs", () => {
|
|
32
32
|
expect(
|
|
33
|
-
areAdjacentStableReleases(
|
|
34
|
-
STABLES,
|
|
35
|
-
"rust-v0.1.0",
|
|
36
|
-
"rust-v0.2.0",
|
|
37
|
-
),
|
|
33
|
+
areAdjacentStableReleases(STABLES, "rust-v0.1.0", "rust-v0.2.0"),
|
|
38
34
|
).toBe(true);
|
|
39
35
|
expect(
|
|
40
|
-
areAdjacentStableReleases(
|
|
41
|
-
STABLES,
|
|
42
|
-
"rust-v0.1.0",
|
|
43
|
-
"rust-v0.3.0",
|
|
44
|
-
),
|
|
36
|
+
areAdjacentStableReleases(STABLES, "rust-v0.1.0", "rust-v0.3.0"),
|
|
45
37
|
).toBe(false);
|
|
46
38
|
});
|
|
47
39
|
|
|
@@ -50,8 +42,8 @@ describe("Codex stable release backlog", () => {
|
|
|
50
42
|
});
|
|
51
43
|
|
|
52
44
|
test("fails instead of skipping an unavailable cursor", () => {
|
|
53
|
-
expect(() =>
|
|
54
|
-
|
|
55
|
-
)
|
|
45
|
+
expect(() => findNextStableRelease(STABLES, "rust-v0.0.9")).toThrow(
|
|
46
|
+
"Could not find terminal release rust-v0.0.9",
|
|
47
|
+
);
|
|
56
48
|
});
|
|
57
49
|
});
|
|
@@ -212,9 +212,7 @@ export function areAdjacentStableReleases(
|
|
|
212
212
|
previousTag: string,
|
|
213
213
|
currentTag: string,
|
|
214
214
|
): boolean {
|
|
215
|
-
return (
|
|
216
|
-
findPreviousStable(stables, currentTag)?.tag_name === previousTag
|
|
217
|
-
);
|
|
215
|
+
return findPreviousStable(stables, currentTag)?.tag_name === previousTag;
|
|
218
216
|
}
|
|
219
217
|
|
|
220
218
|
export function findNextStableRelease(
|
|
@@ -37,7 +37,7 @@ function analysis(
|
|
|
37
37
|
|
|
38
38
|
function withoutAuditCursor(body: string): string {
|
|
39
39
|
return body
|
|
40
|
-
.replace(/
|
|
40
|
+
.replace(/ {2}"audit_cursor_tag": (?:null|"[^"]+"),\n/, "")
|
|
41
41
|
.replace(' "audit_cursor_validated": true,\n', "");
|
|
42
42
|
}
|
|
43
43
|
|
|
@@ -198,11 +198,7 @@ describe("tracker state", () => {
|
|
|
198
198
|
|
|
199
199
|
const body = renderTrackerBody(state);
|
|
200
200
|
expect(
|
|
201
|
-
hasProcessedRange(
|
|
202
|
-
parseTrackerState(body),
|
|
203
|
-
"rust-v0.1.0",
|
|
204
|
-
"rust-v0.2.0",
|
|
205
|
-
),
|
|
201
|
+
hasProcessedRange(parseTrackerState(body), "rust-v0.1.0", "rust-v0.2.0"),
|
|
206
202
|
).toBe(true);
|
|
207
203
|
expect(body).toContain("_No actionable releases recorded yet._");
|
|
208
204
|
expect(body).toContain("no watched changes");
|
|
@@ -215,23 +211,16 @@ describe("tracker state", () => {
|
|
|
215
211
|
);
|
|
216
212
|
state = upsertTrackerEntry(state, entry(2, "error"));
|
|
217
213
|
|
|
218
|
-
expect(
|
|
219
|
-
hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0"),
|
|
220
|
-
).toBe(false);
|
|
214
|
+
expect(hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0")).toBe(false);
|
|
221
215
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
222
216
|
|
|
223
217
|
state = upsertTrackerEntry(state, entry(2, "no_local_impact"));
|
|
224
|
-
expect(
|
|
225
|
-
hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0"),
|
|
226
|
-
).toBe(true);
|
|
218
|
+
expect(hasProcessedRange(state, "rust-v0.1.0", "rust-v0.2.0")).toBe(true);
|
|
227
219
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.2.0");
|
|
228
220
|
});
|
|
229
221
|
|
|
230
222
|
test("uses an errored release baseline when no terminal entry exists", () => {
|
|
231
|
-
const state = upsertTrackerEntry(
|
|
232
|
-
emptyTrackerState(),
|
|
233
|
-
entry(2, "error"),
|
|
234
|
-
);
|
|
223
|
+
const state = upsertTrackerEntry(emptyTrackerState(), entry(2, "error"));
|
|
235
224
|
|
|
236
225
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
237
226
|
});
|
|
@@ -279,12 +268,7 @@ describe("tracker state", () => {
|
|
|
279
268
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.1.0");
|
|
280
269
|
expect(
|
|
281
270
|
getCodexAuditCursorTag(
|
|
282
|
-
advanceCodexAuditCursor(
|
|
283
|
-
state,
|
|
284
|
-
"rust-v0.1.0",
|
|
285
|
-
"rust-v0.5.0",
|
|
286
|
-
false,
|
|
287
|
-
),
|
|
271
|
+
advanceCodexAuditCursor(state, "rust-v0.1.0", "rust-v0.5.0", false),
|
|
288
272
|
),
|
|
289
273
|
).toBe("rust-v0.1.0");
|
|
290
274
|
});
|
|
@@ -298,12 +282,7 @@ describe("tracker state", () => {
|
|
|
298
282
|
state = upsertTrackerEntry(state, entry(2, "no_local_impact"));
|
|
299
283
|
|
|
300
284
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.2.0");
|
|
301
|
-
state = advanceCodexAuditCursor(
|
|
302
|
-
state,
|
|
303
|
-
"rust-v0.2.0",
|
|
304
|
-
"rust-v0.3.0",
|
|
305
|
-
true,
|
|
306
|
-
);
|
|
285
|
+
state = advanceCodexAuditCursor(state, "rust-v0.2.0", "rust-v0.3.0", true);
|
|
307
286
|
expect(getCodexAuditCursorTag(state)).toBe("rust-v0.3.0");
|
|
308
287
|
});
|
|
309
288
|
|
|
@@ -121,7 +121,8 @@ export function advanceCodexAuditCursor(
|
|
|
121
121
|
currentTag: string,
|
|
122
122
|
isAdjacentRelease: boolean,
|
|
123
123
|
): TrackerState {
|
|
124
|
-
if (!isAdjacentRelease || state.audit_cursor_tag !== previousTag)
|
|
124
|
+
if (!isAdjacentRelease || state.audit_cursor_tag !== previousTag)
|
|
125
|
+
return state;
|
|
125
126
|
if (!hasProcessedRange(state, previousTag, currentTag)) {
|
|
126
127
|
throw new Error(
|
|
127
128
|
`Cannot advance Codex audit cursor without terminal ${previousTag}...${currentTag}`,
|
|
@@ -135,17 +136,21 @@ export function recordAnalysis(
|
|
|
135
136
|
options: RecordAnalysisOptions,
|
|
136
137
|
): TrackerState {
|
|
137
138
|
const processedAt = options.processedAt ?? new Date().toISOString();
|
|
138
|
-
return upsertTrackerEntry(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
139
|
+
return upsertTrackerEntry(
|
|
140
|
+
state,
|
|
141
|
+
{
|
|
142
|
+
tag: options.analysis.current_tag,
|
|
143
|
+
previous_tag: options.analysis.previous_tag,
|
|
144
|
+
verdict: options.analysis.verdict,
|
|
145
|
+
outcome: options.outcome,
|
|
146
|
+
pr_url: options.prUrl ?? null,
|
|
147
|
+
notes: options.notes,
|
|
148
|
+
processed_at: processedAt,
|
|
149
|
+
compare_url: options.analysis.compare_url,
|
|
150
|
+
workflow_run_url: options.analysis.workflow_run_url,
|
|
151
|
+
},
|
|
152
|
+
options.analysis.is_adjacent_release,
|
|
153
|
+
);
|
|
149
154
|
}
|
|
150
155
|
|
|
151
156
|
export function upsertTrackerEntry(
|
|
@@ -179,7 +184,10 @@ export function upsertTrackerEntry(
|
|
|
179
184
|
),
|
|
180
185
|
];
|
|
181
186
|
let processed = candidates.slice(0, HIDDEN_STATE_LIMIT);
|
|
182
|
-
if (
|
|
187
|
+
if (
|
|
188
|
+
auditCursorTag !== null &&
|
|
189
|
+
!isSupportedAuditCursor(processed, auditCursorTag)
|
|
190
|
+
) {
|
|
183
191
|
const support = candidates.find((candidate) =>
|
|
184
192
|
isAuditCursorSupportEntry(candidate, auditCursorTag),
|
|
185
193
|
);
|
|
@@ -276,7 +284,10 @@ function escapeTable(value: string): string {
|
|
|
276
284
|
|
|
277
285
|
function normalizeState(value: unknown): TrackerState {
|
|
278
286
|
if (!isRecord(value)) throw new TypeError("tracker state must be an object");
|
|
279
|
-
if (
|
|
287
|
+
if (
|
|
288
|
+
!Array.isArray(value.processed) ||
|
|
289
|
+
!value.processed.every(isTrackerEntry)
|
|
290
|
+
) {
|
|
280
291
|
throw new TypeError("tracker processed entries are invalid");
|
|
281
292
|
}
|
|
282
293
|
if (
|
|
@@ -346,9 +357,7 @@ function isSupportedAuditCursor(
|
|
|
346
357
|
processed: TrackerEntry[],
|
|
347
358
|
cursorTag: string,
|
|
348
359
|
): boolean {
|
|
349
|
-
return processed.some((entry) =>
|
|
350
|
-
isAuditCursorSupportEntry(entry, cursorTag),
|
|
351
|
-
);
|
|
360
|
+
return processed.some((entry) => isAuditCursorSupportEntry(entry, cursorTag));
|
|
352
361
|
}
|
|
353
362
|
|
|
354
363
|
function isAuditCursorSupportEntry(
|
|
@@ -37,13 +37,13 @@
|
|
|
37
37
|
"src/settings-manager.ts": 2102,
|
|
38
38
|
"src/tools/manager.ts": 2993,
|
|
39
39
|
"src/tools/tool-execution-context.test.ts": 1138,
|
|
40
|
-
"src/types/protocol_v2.ts":
|
|
40
|
+
"src/types/protocol_v2.ts": 2834,
|
|
41
41
|
"src/websocket/listen-client-concurrency.test.ts": 2686,
|
|
42
42
|
"src/websocket/listen-client-protocol.test.ts": 5819,
|
|
43
43
|
"src/websocket/listener/commands/memory.ts": 1114,
|
|
44
44
|
"src/websocket/listener/file-commands.ts": 1053,
|
|
45
45
|
"src/websocket/listener/lifecycle.ts": 1048,
|
|
46
|
-
"src/websocket/listener/protocol-inbound.ts":
|
|
46
|
+
"src/websocket/listener/protocol-inbound.ts": 2237,
|
|
47
47
|
"src/websocket/listener/protocol-outbound.ts": 1047,
|
|
48
|
-
"src/websocket/listener/turn.ts":
|
|
48
|
+
"src/websocket/listener/turn.ts": 1058
|
|
49
49
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: self-configuration
|
|
3
|
-
description: Inspect or 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 how this agent or conversation is configured, or asks you to change how you behave or how the harness runs you.
|
|
3
|
+
description: Inspect or modify Letta Code's own memory, model, context window, system prompt, compaction, permissions, toolsets, mods, skills, channels, schedules, agent secrets, and local runtime settings. Use when the user asks how this agent or conversation is configured, or asks you to change how you behave or how the harness runs you.
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -22,6 +22,7 @@ The important part is choosing the right layer. Do not smear a preference into d
|
|
|
22
22
|
| Skills | Reusable procedural knowledge or bundled scripts | Load `creating-skills` or `acquiring-skills` |
|
|
23
23
|
| Channels | Slack/Discord/Telegram/WhatsApp/Signal accounts, pairing, routing, listener state | Use `letta channels` or channel commands |
|
|
24
24
|
| Schedules | Reminders and recurring prompts | Load `scheduling-tasks` and use `letta cron` |
|
|
25
|
+
| Agent secrets | Per-agent `$NAME` credential values for shell commands | Use `letta secret` (or `/secret` in a session) |
|
|
25
26
|
|
|
26
27
|
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
|
|
|
@@ -370,6 +371,24 @@ Never print provider keys. Shell expansion such as `--api-key "$OPENAI_API_KEY"`
|
|
|
370
371
|
|
|
371
372
|
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.
|
|
372
373
|
|
|
374
|
+
## Agent secrets
|
|
375
|
+
|
|
376
|
+
Agent-scoped secrets hold credential values that are referenced as `$NAME` in shell commands. Cloud agents store them server-side on the agent; local agents use OS secure storage. The harness substitutes `$NAME` at exec time and scrubs values from tool output, so values never enter agent context.
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
letta secret list # names only, never values
|
|
380
|
+
letta secret set GITHUB_TOKEN --env GITHUB_TOKEN # ingest from the environment
|
|
381
|
+
openssl rand -hex 32 | letta secret set WEBHOOK_TOKEN --stdin # generate without seeing the value
|
|
382
|
+
letta secret unset GITHUB_TOKEN # aliases: delete | remove | rm
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Rules:
|
|
386
|
+
|
|
387
|
+
- Pass the source variable *name* to `--env`, not `$NAME`. `--env $GITHUB_TOKEN` triggers harness substitution and places the resolved value in process arguments; `--env GITHUB_TOKEN` reads it from the CLI process environment without exposure.
|
|
388
|
+
- Never echo secret values into tool output. Pipe generated credentials straight into `--stdin`.
|
|
389
|
+
- Inside a session, `AGENT_ID`/`LETTA_AGENT_ID` resolves the target automatically; pass `--agent <agent-id>` otherwise.
|
|
390
|
+
- A running session loads its secret cache at startup; CLI-side changes apply to new sessions. The `/secret` slash command manages the same store interactively and refreshes the live cache.
|
|
391
|
+
|
|
373
392
|
## Channels
|
|
374
393
|
|
|
375
394
|
Use channels when the user wants to talk through Slack, Discord, Telegram, WhatsApp, or Signal.
|