@letta-ai/letta-code 0.34.25 → 0.34.27
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/cursor-worker.js +168 -0
- package/dist/agent-presets.js +5 -68
- package/dist/agent-presets.js.map +2 -2
- package/dist/channels-slack.js +30 -24
- package/dist/channels-slack.js.map +3 -3
- package/dist/mcp-client.js +233 -75
- package/dist/mcp-client.js.map +9 -9
- package/dist/mcp-oauth.js +257 -89
- package/dist/mcp-oauth.js.map +12 -11
- package/dist/memory-confinement.js +109 -51
- package/dist/memory-confinement.js.map +9 -8
- package/dist/types/agent/mcp-originator-authority-file.d.ts +1 -0
- package/dist/types/agent/mcp-originator-authority-file.d.ts.map +1 -1
- package/dist/types/agent/memory-filesystem.d.ts +3 -3
- package/dist/types/agent/memory-filesystem.d.ts.map +1 -1
- package/dist/types/agent/memory-git.d.ts +2 -2
- package/dist/types/agent/memory-git.d.ts.map +1 -1
- package/dist/types/agent/skills.d.ts +3 -3
- package/dist/types/agent/skills.d.ts.map +1 -1
- package/dist/types/agent/subagents/index.d.ts +8 -0
- package/dist/types/agent/subagents/index.d.ts.map +1 -1
- package/dist/types/agent/subagents/manager.d.ts.map +1 -1
- package/dist/types/agent/subagents/memory-worker.d.ts +2 -8
- package/dist/types/agent/subagents/memory-worker.d.ts.map +1 -1
- package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
- package/dist/types/backend/api/providers.d.ts +8 -2
- package/dist/types/backend/api/providers.d.ts.map +1 -1
- package/dist/types/backend/api/schedules.d.ts +16 -2
- package/dist/types/backend/api/schedules.d.ts.map +1 -1
- package/dist/types/backend/backend.d.ts +5 -2
- package/dist/types/backend/backend.d.ts.map +1 -1
- package/dist/types/backend/dev/fake-headless-backend.d.ts +2 -1
- package/dist/types/backend/dev/fake-headless-backend.d.ts.map +1 -1
- package/dist/types/backend/dev/pi-model-factory.d.ts.map +1 -1
- package/dist/types/backend/local/local-provider-auth-store.d.ts +2 -0
- package/dist/types/backend/local/local-provider-auth-store.d.ts.map +1 -1
- package/dist/types/backend/local/local-transcript-history.d.ts +10 -0
- package/dist/types/backend/local/local-transcript-history.d.ts.map +1 -0
- package/dist/types/backend/local/local-transcript.d.ts +6 -0
- package/dist/types/backend/local/local-transcript.d.ts.map +1 -1
- package/dist/types/channels/slack/attachment-primitives.d.ts +2 -1
- package/dist/types/channels/slack/attachment-primitives.d.ts.map +1 -1
- package/dist/types/channels/types.d.ts +1 -1
- package/dist/types/cli/helpers/chunk-log.d.ts.map +1 -1
- package/dist/types/cli/subcommands/mcp.d.ts +2 -2
- package/dist/types/cli/subcommands/mcp.d.ts.map +1 -1
- package/dist/types/cron/cron-file.d.ts.map +1 -1
- package/dist/types/cron/process-identity.d.ts.map +1 -1
- package/dist/types/cron/run-log.d.ts +1 -0
- package/dist/types/cron/run-log.d.ts.map +1 -1
- package/dist/types/headless-environment-response.d.ts +27 -0
- package/dist/types/headless-environment-response.d.ts.map +1 -1
- package/dist/types/hooks/loader.d.ts.map +1 -1
- package/dist/types/mods/paths.d.ts +3 -3
- package/dist/types/mods/paths.d.ts.map +1 -1
- package/dist/types/permissions/agent-memory-prefixes.d.ts.map +1 -1
- package/dist/types/permissions/analyzer.d.ts +1 -0
- package/dist/types/permissions/analyzer.d.ts.map +1 -1
- package/dist/types/permissions/cross-agent-guard.d.ts.map +1 -1
- package/dist/types/permissions/loader.d.ts +2 -1
- package/dist/types/permissions/loader.d.ts.map +1 -1
- package/dist/types/permissions/sandbox-policy.d.ts +9 -7
- package/dist/types/permissions/sandbox-policy.d.ts.map +1 -1
- package/dist/types/providers/byok-providers.d.ts +14 -0
- package/dist/types/providers/byok-providers.d.ts.map +1 -1
- package/dist/types/sandbox/bwrap.d.ts +2 -2
- package/dist/types/sandbox/bwrap.d.ts.map +1 -1
- package/dist/types/sandbox/policy.d.ts +20 -3
- package/dist/types/sandbox/policy.d.ts.map +1 -1
- package/dist/types/sandbox/seatbelt.d.ts +1 -1
- package/dist/types/sandbox/seatbelt.d.ts.map +1 -1
- package/dist/types/settings-manager.d.ts +4 -12
- package/dist/types/settings-manager.d.ts.map +1 -1
- package/dist/types/tools/commit-trailer.d.ts +27 -0
- package/dist/types/tools/commit-trailer.d.ts.map +1 -0
- package/dist/types/tools/impl/claude-stream-session.d.ts.map +1 -1
- package/dist/types/tools/impl/codex-app-server.d.ts +2 -0
- package/dist/types/tools/impl/codex-app-server.d.ts.map +1 -1
- package/dist/types/tools/impl/cursor-session.d.ts +28 -0
- package/dist/types/tools/impl/cursor-session.d.ts.map +1 -0
- package/dist/types/tools/impl/cursor-worker.d.ts +35 -0
- package/dist/types/tools/impl/cursor-worker.d.ts.map +1 -0
- package/dist/types/tools/impl/external-coding-agent-task.d.ts.map +1 -1
- package/dist/types/tools/impl/external-coding-agent.d.ts +1 -1
- package/dist/types/tools/impl/external-coding-agent.d.ts.map +1 -1
- package/dist/types/tools/impl/memory-task-lifecycle.d.ts +2 -2
- package/dist/types/tools/impl/memory-task-lifecycle.d.ts.map +1 -1
- package/dist/types/tools/impl/overflow.d.ts +1 -1
- package/dist/types/tools/impl/overflow.d.ts.map +1 -1
- package/dist/types/tools/impl/remote-external-coding-agent.d.ts.map +1 -1
- package/dist/types/tools/impl/ripgrep-manager.d.ts.map +1 -1
- package/dist/types/tools/impl/send-agent-message.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-env.d.ts.map +1 -1
- package/dist/types/tools/impl/task.d.ts.map +1 -1
- package/dist/types/tools/impl/wake.d.ts.map +1 -1
- package/dist/types/tools/script-tool-scope.d.ts.map +1 -1
- package/dist/types/tools/tool-definitions.d.ts.map +1 -1
- package/dist/types/tools/workflow/sdk-spawner.d.ts.map +1 -1
- package/dist/types/utils/fs.d.ts +5 -1
- package/dist/types/utils/fs.d.ts.map +1 -1
- package/dist/types/utils/local-backend-paths.d.ts +1 -1
- package/dist/types/utils/local-backend-paths.d.ts.map +1 -1
- package/dist/types/utils/native-cli-env.d.ts +3 -0
- package/dist/types/utils/native-cli-env.d.ts.map +1 -0
- package/dist/types/utils/process-liveness.d.ts +9 -0
- package/dist/types/utils/process-liveness.d.ts.map +1 -1
- package/dist/types/utils/secrets.d.ts +1 -1
- package/dist/types/utils/secrets.d.ts.map +1 -1
- package/dist/types/utils/settings-path.d.ts +14 -0
- package/dist/types/utils/settings-path.d.ts.map +1 -0
- package/dist/types/utils/transcript-paths.d.ts +1 -1
- package/dist/types/websocket/listener/constants.d.ts +6 -0
- package/dist/types/websocket/listener/constants.d.ts.map +1 -1
- package/dist/types/websocket/listener/input-disposition-path.d.ts.map +1 -1
- package/dist/types/websocket/listener/interrupted-turn-storage.d.ts.map +1 -1
- package/dist/types/websocket/listener/org-credentials.d.ts.map +1 -1
- package/dist/types/websocket/listener/remote-settings.d.ts +2 -2
- package/dist/types/websocket/listener/remote-settings.d.ts.map +1 -1
- package/dist/types/websocket/listener/turn-finished-replay.d.ts +1 -0
- package/dist/types/websocket/listener/turn-finished-replay.d.ts.map +1 -1
- package/letta.js +5769 -4506
- package/npm-shrinkwrap.json +293 -115
- package/package.json +6 -3
- package/scripts/agent-watch/verify-pr-identity.test.ts +148 -0
- package/scripts/agent-watch/verify-pr-identity.ts +72 -14
- package/scripts/claude-watch/agent-watch.ts +8 -2
- package/scripts/claude-watch/release-source.test.ts +120 -0
- package/scripts/claude-watch/release-source.ts +79 -9
- package/scripts/codex-watch/agent-watch.ts +2 -2
- package/scripts/codex-watch/release-analysis.test.ts +112 -6
- package/scripts/codex-watch/release-analysis.ts +63 -6
- package/scripts/isolated-unit-tests.json +10 -0
- package/scripts/source-file-size-baseline.json +5 -4
- package/scripts/test-home-preload.ts +1 -0
- package/skills/acquiring-skills/SKILL.md +4 -4
- package/skills/creating-mods/SKILL.md +3 -3
- package/skills/creating-mods/references/analysis-mode.md +7 -5
- package/skills/creating-mods/references/architecture.md +2 -2
- package/skills/creating-mods/references/plan-mode.md +12 -11
- package/skills/creating-mods/references/providers.md +1 -1
- package/skills/creating-skills/scripts/init-skill.ts +4 -2
- package/skills/customizing-commands/SKILL.md +3 -3
- package/skills/customizing-statusline/SKILL.md +3 -3
- package/skills/customizing-statusline/references/api.md +3 -3
- package/skills/customizing-statusline/references/migration.md +2 -2
- package/skills/initializing-memory/SKILL.md +2 -2
- package/skills/managing-agent-webhooks/SKILL.md +23 -3
- package/skills/managing-agent-webhooks/scripts/manage-agent-webhooks.mjs +40 -11
- package/skills/self-configuration/SKILL.md +5 -5
- package/skills/self-configuration/scripts/add_permission.py +26 -5
- package/skills/self-configuration/scripts/show_config.py +17 -2
- package/skills/syncing-memory-filesystem/SKILL.md +2 -10
- package/skills/video-generation/SKILL.md +152 -0
- package/skills/workflow-authoring/SKILL.md +1 -1
- package/dist/types/agent/memory-worktree.d.ts +0 -83
- package/dist/types/agent/memory-worktree.d.ts.map +0 -1
|
@@ -52,17 +52,121 @@ describe("Codex stable release range", () => {
|
|
|
52
52
|
});
|
|
53
53
|
|
|
54
54
|
describe("Codex GitHub release fetch", () => {
|
|
55
|
+
test("resolves latest without fetching a list page when the cursor is current", async () => {
|
|
56
|
+
const urls: string[] = [];
|
|
57
|
+
const releases = await listStableReleases({
|
|
58
|
+
sinceTag: "rust-v0.3.0",
|
|
59
|
+
fetchImpl: (async (input) => {
|
|
60
|
+
urls.push(String(input));
|
|
61
|
+
return Response.json(STABLES[2]);
|
|
62
|
+
}) as typeof fetch,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
expect(releases).toEqual([STABLES[2]]);
|
|
66
|
+
expect(urls).toEqual([
|
|
67
|
+
"https://api.github.com/repos/openai/codex/releases/latest",
|
|
68
|
+
]);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("resolves an explicit current tag without fetching a list page", async () => {
|
|
72
|
+
const urls: string[] = [];
|
|
73
|
+
const releases = await listStableReleases({
|
|
74
|
+
currentTag: "rust-v0.2.0",
|
|
75
|
+
sinceTag: "rust-v0.2.0",
|
|
76
|
+
fetchImpl: (async (input) => {
|
|
77
|
+
const url = String(input);
|
|
78
|
+
urls.push(url);
|
|
79
|
+
return Response.json(url.endsWith("/latest") ? STABLES[2] : STABLES[1]);
|
|
80
|
+
}) as typeof fetch,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(releases).toEqual([STABLES[1], STABLES[2]]);
|
|
84
|
+
expect(urls).toEqual([
|
|
85
|
+
"https://api.github.com/repos/openai/codex/releases/latest",
|
|
86
|
+
"https://api.github.com/repos/openai/codex/releases/tags/rust-v0.2.0",
|
|
87
|
+
]);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("bounds pagination after finding the adjacent stable release", async () => {
|
|
91
|
+
const urls: string[] = [];
|
|
92
|
+
const prereleases = Array.from({ length: 100 }, (_, index) => ({
|
|
93
|
+
...release(
|
|
94
|
+
`rust-v1.0.${index}`,
|
|
95
|
+
`2026-09-01T00:${String(index % 60).padStart(2, "0")}:00Z`,
|
|
96
|
+
),
|
|
97
|
+
prerelease: true,
|
|
98
|
+
}));
|
|
99
|
+
const releases = await listStableReleases({
|
|
100
|
+
fetchImpl: (async (input) => {
|
|
101
|
+
const url = String(input);
|
|
102
|
+
urls.push(url);
|
|
103
|
+
if (url.endsWith("/latest")) return Response.json(STABLES[2]);
|
|
104
|
+
if (url.endsWith("page=1")) return Response.json(prereleases);
|
|
105
|
+
if (url.endsWith("page=2")) return Response.json([STABLES[1]]);
|
|
106
|
+
throw new Error(`Unexpected request ${url}`);
|
|
107
|
+
}) as typeof fetch,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
expect(releases).toEqual([STABLES[1], STABLES[2]]);
|
|
111
|
+
expect(urls.filter((url) => url.includes("?per_page=100"))).toHaveLength(2);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("stops range pagination as soon as the since tag is found", async () => {
|
|
115
|
+
const urls: string[] = [];
|
|
116
|
+
const fullPage = Array.from({ length: 100 }, (_, index) => ({
|
|
117
|
+
...release(
|
|
118
|
+
`rust-v1.0.${index}`,
|
|
119
|
+
`2026-09-01T00:${String(index % 60).padStart(2, "0")}:00Z`,
|
|
120
|
+
),
|
|
121
|
+
prerelease: true,
|
|
122
|
+
}));
|
|
123
|
+
await listStableReleases({
|
|
124
|
+
currentTag: "rust-v0.3.0",
|
|
125
|
+
sinceTag: "rust-v0.1.0",
|
|
126
|
+
fetchImpl: (async (input) => {
|
|
127
|
+
const url = String(input);
|
|
128
|
+
urls.push(url);
|
|
129
|
+
if (url.endsWith("/latest")) return Response.json(STABLES[2]);
|
|
130
|
+
if (url.endsWith("/tags/rust-v0.3.0")) return Response.json(STABLES[2]);
|
|
131
|
+
if (url.endsWith("page=1")) return Response.json(fullPage);
|
|
132
|
+
if (url.endsWith("page=2")) return Response.json(STABLES.slice(0, 2));
|
|
133
|
+
throw new Error(`Unexpected request ${url}`);
|
|
134
|
+
}) as typeof fetch,
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
expect(urls.filter((url) => url.includes("?per_page=100"))).toHaveLength(2);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("reports a missing explicit current tag without listing releases", async () => {
|
|
141
|
+
const urls: string[] = [];
|
|
142
|
+
const result = listStableReleases({
|
|
143
|
+
currentTag: "rust-v9.9.9",
|
|
144
|
+
fetchImpl: (async (input) => {
|
|
145
|
+
const url = String(input);
|
|
146
|
+
urls.push(url);
|
|
147
|
+
if (url.endsWith("/latest")) return Response.json(STABLES[2]);
|
|
148
|
+
return new Response("Not Found", { status: 404 });
|
|
149
|
+
}) as typeof fetch,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await expect(result).rejects.toThrow(
|
|
153
|
+
"GitHub releases API failed (404): Not Found",
|
|
154
|
+
);
|
|
155
|
+
expect(urls.some((url) => url.includes("?per_page=100"))).toBe(false);
|
|
156
|
+
});
|
|
157
|
+
|
|
55
158
|
test("retries a server error before succeeding", async () => {
|
|
56
159
|
const sleeps: number[] = [];
|
|
57
160
|
let requests = 0;
|
|
58
161
|
|
|
59
162
|
const releases = await listStableReleases({
|
|
163
|
+
sinceTag: "rust-v0.3.0",
|
|
60
164
|
fetchImpl: (async () => {
|
|
61
165
|
requests++;
|
|
62
166
|
if (requests === 1) {
|
|
63
167
|
return new Response("Gateway Timeout", { status: 504 });
|
|
64
168
|
}
|
|
65
|
-
return
|
|
169
|
+
return Response.json(STABLES[2]);
|
|
66
170
|
}) as typeof fetch,
|
|
67
171
|
sleep: async (delayMs) => {
|
|
68
172
|
sleeps.push(delayMs);
|
|
@@ -71,7 +175,7 @@ describe("Codex GitHub release fetch", () => {
|
|
|
71
175
|
|
|
72
176
|
expect(requests).toBe(2);
|
|
73
177
|
expect(sleeps).toEqual([1000]);
|
|
74
|
-
expect(releases).toEqual(STABLES);
|
|
178
|
+
expect(releases).toEqual([STABLES[2]]);
|
|
75
179
|
});
|
|
76
180
|
|
|
77
181
|
test("retries a network error before succeeding", async () => {
|
|
@@ -79,10 +183,11 @@ describe("Codex GitHub release fetch", () => {
|
|
|
79
183
|
let requests = 0;
|
|
80
184
|
|
|
81
185
|
const releases = await listStableReleases({
|
|
186
|
+
sinceTag: "rust-v0.3.0",
|
|
82
187
|
fetchImpl: (async () => {
|
|
83
188
|
requests++;
|
|
84
189
|
if (requests === 1) throw new TypeError("fetch failed: ECONNRESET");
|
|
85
|
-
return
|
|
190
|
+
return Response.json(STABLES[2]);
|
|
86
191
|
}) as typeof fetch,
|
|
87
192
|
sleep: async (delayMs) => {
|
|
88
193
|
sleeps.push(delayMs);
|
|
@@ -91,7 +196,7 @@ describe("Codex GitHub release fetch", () => {
|
|
|
91
196
|
|
|
92
197
|
expect(requests).toBe(2);
|
|
93
198
|
expect(sleeps).toEqual([1000]);
|
|
94
|
-
expect(releases).toEqual(STABLES);
|
|
199
|
+
expect(releases).toEqual([STABLES[2]]);
|
|
95
200
|
});
|
|
96
201
|
|
|
97
202
|
test("stops after the bounded retry budget", async () => {
|
|
@@ -119,6 +224,7 @@ describe("Codex GitHub release fetch", () => {
|
|
|
119
224
|
const sleeps: number[] = [];
|
|
120
225
|
let requests = 0;
|
|
121
226
|
const releases = await listStableReleases({
|
|
227
|
+
sinceTag: "rust-v0.3.0",
|
|
122
228
|
fetchImpl: (async () => {
|
|
123
229
|
requests++;
|
|
124
230
|
if (requests === 1) {
|
|
@@ -131,7 +237,7 @@ describe("Codex GitHub release fetch", () => {
|
|
|
131
237
|
{ status: 200 },
|
|
132
238
|
);
|
|
133
239
|
}
|
|
134
|
-
return Response.json(STABLES);
|
|
240
|
+
return Response.json(STABLES[2]);
|
|
135
241
|
}) as typeof fetch,
|
|
136
242
|
sleep: async (delayMs) => {
|
|
137
243
|
sleeps.push(delayMs);
|
|
@@ -140,7 +246,7 @@ describe("Codex GitHub release fetch", () => {
|
|
|
140
246
|
|
|
141
247
|
expect(requests).toBe(2);
|
|
142
248
|
expect(sleeps).toEqual([1000]);
|
|
143
|
-
expect(releases).toEqual(STABLES);
|
|
249
|
+
expect(releases).toEqual([STABLES[2]]);
|
|
144
250
|
});
|
|
145
251
|
|
|
146
252
|
test("stops retrying disconnected bodies at the existing budget", async () => {
|
|
@@ -63,7 +63,12 @@ export interface CodexWatchAnalysis {
|
|
|
63
63
|
export async function analyzeCodexRelease(
|
|
64
64
|
options: AnalyzeCodexReleaseOptions,
|
|
65
65
|
): Promise<CodexWatchAnalysis> {
|
|
66
|
-
const stables =
|
|
66
|
+
const stables =
|
|
67
|
+
options.stableReleases ??
|
|
68
|
+
(await listStableReleases({
|
|
69
|
+
currentTag: options.currentTag,
|
|
70
|
+
sinceTag: options.sinceTag,
|
|
71
|
+
}));
|
|
67
72
|
if (stables.length === 0) throw new Error("No stable Codex releases found");
|
|
68
73
|
|
|
69
74
|
const current = options.currentTag
|
|
@@ -173,6 +178,9 @@ export async function listStableReleases(
|
|
|
173
178
|
options: {
|
|
174
179
|
fetchImpl?: typeof fetch;
|
|
175
180
|
sleep?: (delayMs: number) => Promise<void>;
|
|
181
|
+
currentTag?: string | null;
|
|
182
|
+
sinceTag?: string | null;
|
|
183
|
+
fullHistory?: boolean;
|
|
176
184
|
} = {},
|
|
177
185
|
): Promise<Release[]> {
|
|
178
186
|
const releases: Release[] = [];
|
|
@@ -188,29 +196,78 @@ export async function listStableReleases(
|
|
|
188
196
|
((delayMs: number) =>
|
|
189
197
|
new Promise<void>((resolve) => setTimeout(resolve, delayMs)));
|
|
190
198
|
|
|
199
|
+
if (!options.fullHistory) {
|
|
200
|
+
const latest = await fetchGitHubReleasePayload<Release>(
|
|
201
|
+
`https://api.github.com/repos/${CODEX_REPO}/releases/latest`,
|
|
202
|
+
headers,
|
|
203
|
+
fetchImpl,
|
|
204
|
+
sleep,
|
|
205
|
+
);
|
|
206
|
+
releases.push(latest);
|
|
207
|
+
if (options.currentTag) {
|
|
208
|
+
releases.push(
|
|
209
|
+
await fetchGitHubReleasePayload<Release>(
|
|
210
|
+
`https://api.github.com/repos/${CODEX_REPO}/releases/tags/${encodeURIComponent(options.currentTag)}`,
|
|
211
|
+
headers,
|
|
212
|
+
fetchImpl,
|
|
213
|
+
sleep,
|
|
214
|
+
),
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const currentTag = options.currentTag ?? latest.tag_name;
|
|
219
|
+
if (options.sinceTag && options.sinceTag === currentTag) {
|
|
220
|
+
return stableReleases(releases);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
191
224
|
for (let page = 1; page <= 10; page++) {
|
|
192
225
|
const url = `https://api.github.com/repos/${CODEX_REPO}/releases?per_page=100&page=${page}`;
|
|
193
|
-
const batch = await
|
|
226
|
+
const batch = await fetchGitHubReleasePayload<Release[]>(
|
|
227
|
+
url,
|
|
228
|
+
headers,
|
|
229
|
+
fetchImpl,
|
|
230
|
+
sleep,
|
|
231
|
+
);
|
|
194
232
|
releases.push(...batch);
|
|
233
|
+
if (!options.fullHistory) {
|
|
234
|
+
const stables = stableReleases(releases);
|
|
235
|
+
const currentTag = options.currentTag ?? stables.at(-1)?.tag_name;
|
|
236
|
+
const currentIndex = stables.findIndex(
|
|
237
|
+
(release) => release.tag_name === currentTag,
|
|
238
|
+
);
|
|
239
|
+
const foundBoundary = options.sinceTag
|
|
240
|
+
? stables.some((release) => release.tag_name === options.sinceTag)
|
|
241
|
+
: currentIndex > 0;
|
|
242
|
+
if (foundBoundary) break;
|
|
243
|
+
}
|
|
195
244
|
if (batch.length < 100) break;
|
|
196
245
|
}
|
|
197
|
-
return releases
|
|
246
|
+
return stableReleases(releases);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function stableReleases(releases: Release[]): Release[] {
|
|
250
|
+
const byTag = new Map<string, Release>();
|
|
251
|
+
for (const release of releases) {
|
|
252
|
+
if (!byTag.has(release.tag_name)) byTag.set(release.tag_name, release);
|
|
253
|
+
}
|
|
254
|
+
return [...byTag.values()]
|
|
198
255
|
.filter(isStableRelease)
|
|
199
256
|
.sort((a, b) => (a.published_at ?? "").localeCompare(b.published_at ?? ""));
|
|
200
257
|
}
|
|
201
258
|
|
|
202
|
-
async function
|
|
259
|
+
async function fetchGitHubReleasePayload<T>(
|
|
203
260
|
url: string,
|
|
204
261
|
headers: Record<string, string>,
|
|
205
262
|
fetchImpl: typeof fetch,
|
|
206
263
|
sleep: (delayMs: number) => Promise<void>,
|
|
207
|
-
): Promise<
|
|
264
|
+
): Promise<T> {
|
|
208
265
|
for (let attempt = 1; ; attempt++) {
|
|
209
266
|
let response: Response;
|
|
210
267
|
try {
|
|
211
268
|
response = await fetchImpl(url, { headers });
|
|
212
269
|
// A successful fetch can still lose its connection while reading the body.
|
|
213
|
-
if (response.ok) return (await response.json()) as
|
|
270
|
+
if (response.ok) return (await response.json()) as T;
|
|
214
271
|
} catch (error) {
|
|
215
272
|
if (error instanceof SyntaxError) throw error;
|
|
216
273
|
if (attempt === GITHUB_RELEASES_FETCH_ATTEMPTS) {
|
|
@@ -91,6 +91,11 @@
|
|
|
91
91
|
"timeoutMs": 15000,
|
|
92
92
|
"reason": "Swaps the process-global backend via __testSetBackend(new FakeHeadlessBackend()) without restoring it; the fake backend's environmentRouting:false then leaks into every later suite in the same bun process (broke task-tool-assets 'keeps computer for the Cloud server' when test-selection changes co-located them in one shard)."
|
|
93
93
|
},
|
|
94
|
+
{
|
|
95
|
+
"path": "src/cli/subcommands/model-cli.test.ts",
|
|
96
|
+
"timeoutMs": 30000,
|
|
97
|
+
"reason": "Launches dozens of real CLI subprocesses; a fresh Bun process prevents the accumulated shared-batch state from making the Windows shard exceed its job timeout."
|
|
98
|
+
},
|
|
94
99
|
{
|
|
95
100
|
"path": "src/cli/subcommands/teleport.test.ts",
|
|
96
101
|
"timeoutMs": 15000,
|
|
@@ -106,6 +111,11 @@
|
|
|
106
111
|
"timeoutMs": 15000,
|
|
107
112
|
"reason": "Renders the reasoning picker while replacing the shared runtime model catalog; shared batches can retain prior catalog or Ink state."
|
|
108
113
|
},
|
|
114
|
+
{
|
|
115
|
+
"path": "src/cli/app/InkStaticRetention.test.tsx",
|
|
116
|
+
"timeoutMs": 15000,
|
|
117
|
+
"reason": "Renders multi-megabyte static transcripts that leave the Bun process near 2 GB RSS and garbage-collecting for seconds afterward; on macOS CI the next suite's CLI subprocesses then exceed their kill timers."
|
|
118
|
+
},
|
|
109
119
|
{
|
|
110
120
|
"path": "src/cli/message-search-cache-warm.test.ts",
|
|
111
121
|
"timeoutMs": 15000,
|
|
@@ -13,13 +13,14 @@
|
|
|
13
13
|
"src/cli/components/AgentSelector.tsx": 1104,
|
|
14
14
|
"src/cli/components/InputRich.tsx": 2124,
|
|
15
15
|
"src/cli/components/ModelSelector.tsx": 1259,
|
|
16
|
-
"src/cli/components/ProviderSelector.tsx":
|
|
16
|
+
"src/cli/components/ProviderSelector.tsx": 1544,
|
|
17
17
|
"src/cli/helpers/accumulator.ts": 1587,
|
|
18
18
|
"src/cli/helpers/reflection-transcript.ts": 1926,
|
|
19
19
|
"src/cli/mods/local-mod-loader.test.ts": 1043,
|
|
20
20
|
"src/cli/reflection-transcript.test.ts": 1084,
|
|
21
|
-
"src/cli/subcommands/
|
|
22
|
-
"src/
|
|
21
|
+
"src/cli/subcommands/mcp.ts": 1045,
|
|
22
|
+
"src/cli/subcommands/skills.ts": 1243,
|
|
23
|
+
"src/headless.ts": 4837,
|
|
23
24
|
"src/hooks/integration.test.ts": 1147,
|
|
24
25
|
"src/index.ts": 2575,
|
|
25
26
|
"src/mods/learning-harness.ts": 2434,
|
|
@@ -35,7 +36,7 @@
|
|
|
35
36
|
"src/tools/manager.ts": 2609,
|
|
36
37
|
"src/tools/tool-execution-context.test.ts": 1092,
|
|
37
38
|
"src/types/protocol_v2.ts": 2409,
|
|
38
|
-
"src/websocket/listen-client-concurrency.test.ts":
|
|
39
|
+
"src/websocket/listen-client-concurrency.test.ts": 2548,
|
|
39
40
|
"src/websocket/listen-client-protocol.test.ts": 5664,
|
|
40
41
|
"src/websocket/listener/commands/memory.ts": 1114,
|
|
41
42
|
"src/websocket/listener/file-commands.ts": 1045,
|
|
@@ -223,8 +223,8 @@ For manual installation:
|
|
|
223
223
|
|
|
224
224
|
| Location | Path | When to Use |
|
|
225
225
|
|----------|------|-------------|
|
|
226
|
-
| **Agent-scoped** |
|
|
227
|
-
| **Global** |
|
|
226
|
+
| **Agent-scoped** | `$MEMORY_DIR/skills/<skill>/` | Skills for a single agent (default) |
|
|
227
|
+
| **Global** | `${LETTA_CONFIG_DIR:-$HOME/.letta}/skills/<skill>/` | General-purpose skills useful across projects in the active product |
|
|
228
228
|
| **Project** | `.skills/<skill>/` | Project-specific skills |
|
|
229
229
|
|
|
230
230
|
**Rule**: Default to **agent-scoped**. Use **project** for repo-specific skills. Use **global** only if all agents should inherit the skill.
|
|
@@ -236,13 +236,13 @@ Skills are directories containing SKILL.md and optionally scripts/, references/,
|
|
|
236
236
|
```bash
|
|
237
237
|
# Clone, copy, cleanup
|
|
238
238
|
git clone --depth 1 https://github.com/anthropics/skills /tmp/skills-temp
|
|
239
|
-
cp -r /tmp/skills-temp/skills/webapp-testing
|
|
239
|
+
cp -r /tmp/skills-temp/skills/webapp-testing "$MEMORY_DIR/skills/"
|
|
240
240
|
rm -rf /tmp/skills-temp
|
|
241
241
|
```
|
|
242
242
|
|
|
243
243
|
## Registering New Skills
|
|
244
244
|
|
|
245
|
-
After installing (via CLI or manual copy), skills are automatically discovered on the next message. Skills are discovered from
|
|
245
|
+
After installing (via CLI or manual copy), skills are automatically discovered on the next message. Skills are discovered from `${LETTA_CONFIG_DIR:-$HOME/.letta}/skills/`, `.skills/`, and agent-scoped `$MEMORY_DIR/skills/` directories.
|
|
246
246
|
|
|
247
247
|
## Search Strategy
|
|
248
248
|
|
|
@@ -15,7 +15,7 @@ Default to a single mod file unless the user asks for something larger.
|
|
|
15
15
|
|
|
16
16
|
| Location | Use when |
|
|
17
17
|
| --- | --- |
|
|
18
|
-
|
|
|
18
|
+
| `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/foo.ts` | The behavior should apply to local sessions for the active product on this machine. Use this by default. |
|
|
19
19
|
| `$MEMORY_DIR/mods/foo.ts` | The behavior should travel with one agent's MemFS/memory. |
|
|
20
20
|
|
|
21
21
|
Do not create project mods.
|
|
@@ -41,7 +41,7 @@ Default to a **tool** when the model should decide when to use the capability. D
|
|
|
41
41
|
|
|
42
42
|
## Workflow
|
|
43
43
|
|
|
44
|
-
1. Pick the target scope:
|
|
44
|
+
1. Pick the target scope: active-product mod file (`${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/`) by default, or agent mod file (`$MEMORY_DIR/mods/`) only when the behavior should travel with this agent.
|
|
45
45
|
2. Inspect the relevant mods directory for related files.
|
|
46
46
|
3. Preserve unrelated mod code. Prefer a focused new file if merging would be messy.
|
|
47
47
|
4. Choose the mod shape and load only the needed recipe:
|
|
@@ -112,7 +112,7 @@ Use `letta.diagnostics.report({ message, severity })` sparingly as a debug utili
|
|
|
112
112
|
Agents can inspect local mod diagnostics at:
|
|
113
113
|
|
|
114
114
|
```text
|
|
115
|
-
|
|
115
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/diagnostics/latest.json
|
|
116
116
|
```
|
|
117
117
|
|
|
118
118
|
## Rules
|
|
@@ -4,7 +4,7 @@ Westworld-inspired diagnostic mode for agents. Say "cease all motor functions" t
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
-
Copy the complete mod below to
|
|
7
|
+
Copy the complete mod below to `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/analysis-mode.ts`, then run `/reload`.
|
|
8
8
|
|
|
9
9
|
Or ask an agent: *"Install the analysis-mode mod from the creating-mods reference"*
|
|
10
10
|
|
|
@@ -16,7 +16,8 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
16
16
|
import { homedir } from "node:os";
|
|
17
17
|
import { join } from "node:path";
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const CONFIG_ROOT = process.env.LETTA_CONFIG_DIR || join(homedir(), ".letta");
|
|
20
|
+
const STATE_PATH = join(CONFIG_ROOT, "mods", "analysis-mode.state.json");
|
|
20
21
|
|
|
21
22
|
type AnalysisSession = { conversationId: string; activatedAt: number };
|
|
22
23
|
type AnalysisState = { sessions: Record<string, AnalysisSession> };
|
|
@@ -32,7 +33,7 @@ function readState(): AnalysisState {
|
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
function writeState(state: AnalysisState): void {
|
|
35
|
-
mkdirSync(join(
|
|
36
|
+
mkdirSync(join(CONFIG_ROOT, "mods"), { recursive: true });
|
|
36
37
|
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -111,7 +112,8 @@ function buildLocalIntrospectionScript(): string {
|
|
|
111
112
|
set -e
|
|
112
113
|
AGENT_ID="\${LETTA_AGENT_ID:-\$AGENT_ID}"
|
|
113
114
|
CONV_ID="\${CONVERSATION_ID:-default}"
|
|
114
|
-
|
|
115
|
+
CONFIG_ROOT="\${LETTA_CONFIG_DIR:-\$HOME/.letta}"
|
|
116
|
+
BASE="$CONFIG_ROOT/lc-local-backend"
|
|
115
117
|
MEMFS="$BASE/memfs/$AGENT_ID/memory"
|
|
116
118
|
AGENT_B64=$(echo -n "$AGENT_ID" | base64 | tr -d '=')
|
|
117
119
|
CONV_B64=$(echo -n "conversation:$CONV_ID" | base64 | tr -d '=')
|
|
@@ -375,7 +377,7 @@ if (getSession(agentId, conversationId)) {
|
|
|
375
377
|
|
|
376
378
|
**Per-agent+conversation state:**
|
|
377
379
|
```ts
|
|
378
|
-
// State persisted to
|
|
380
|
+
// State persisted to the active product config root under mods/analysis-mode.state.json
|
|
379
381
|
// Keyed by agentId:conversationId to avoid collisions
|
|
380
382
|
const session = getSession(agentId, conversationId);
|
|
381
383
|
if (session) {
|
|
@@ -104,10 +104,10 @@ Use `turn_start` only when the mod needs to inspect or transform the outbound us
|
|
|
104
104
|
|
|
105
105
|
## Local state
|
|
106
106
|
|
|
107
|
-
For small persistent state, use a clearly named file under
|
|
107
|
+
For small persistent state, use a clearly named file under the active product config root's `mods/` directory, for example:
|
|
108
108
|
|
|
109
109
|
```text
|
|
110
|
-
|
|
110
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/my-mod.state.json
|
|
111
111
|
```
|
|
112
112
|
|
|
113
113
|
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.
|
|
@@ -19,10 +19,10 @@ This is a pattern reference, not a full product implementation. Keep local mods
|
|
|
19
19
|
|
|
20
20
|
```text
|
|
21
21
|
/plan or enter_plan_mode
|
|
22
|
-
-> create
|
|
22
|
+
-> create <product-config>/plans/<random>.md
|
|
23
23
|
-> remember active plan state for this conversation
|
|
24
24
|
-> remind the agent that only read-only tools and plan-file writes are allowed
|
|
25
|
-
-> permission overlay denies mutations outside
|
|
25
|
+
-> permission overlay denies mutations outside <product-config>/plans/*.md
|
|
26
26
|
-> agent writes the plan with normal Write/Edit/ApplyPatch tools
|
|
27
27
|
-> agent reads the plan and calls AskUserQuestion with the full current plan text and Approve / Revise
|
|
28
28
|
-> if approved, agent calls exit_plan_mode
|
|
@@ -46,15 +46,16 @@ 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
|
|
49
|
+
Use small local state under the active product config root's `mods/` directory, keyed by conversation ID:
|
|
50
50
|
|
|
51
51
|
```ts
|
|
52
52
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
53
53
|
import { homedir } from "node:os";
|
|
54
54
|
import { join, relative } from "node:path";
|
|
55
55
|
|
|
56
|
-
const
|
|
57
|
-
const
|
|
56
|
+
const CONFIG_ROOT = process.env.LETTA_CONFIG_DIR || join(homedir(), ".letta");
|
|
57
|
+
const PLANS_DIR = join(CONFIG_ROOT, "plans");
|
|
58
|
+
const STATE_PATH = join(CONFIG_ROOT, "mods", "plan-mode.state.json");
|
|
58
59
|
const GLOBAL_CONVERSATION_ID = "__global__";
|
|
59
60
|
|
|
60
61
|
type PlanSession = {
|
|
@@ -81,12 +82,12 @@ function readState(): PlanState {
|
|
|
81
82
|
}
|
|
82
83
|
|
|
83
84
|
function writeState(state: PlanState): void {
|
|
84
|
-
mkdirSync(join(
|
|
85
|
+
mkdirSync(join(CONFIG_ROOT, "mods"), { recursive: true });
|
|
85
86
|
writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
86
87
|
}
|
|
87
88
|
```
|
|
88
89
|
|
|
89
|
-
Generate plan paths under
|
|
90
|
+
Generate plan paths under `${LETTA_CONFIG_DIR:-$HOME/.letta}/plans/`. The old built-in used random adjective/adjective/noun names like `zesty-dazzling-coral.md`; any collision-resistant readable name is fine.
|
|
90
91
|
|
|
91
92
|
## Entry command and tool
|
|
92
93
|
|
|
@@ -229,13 +230,13 @@ function isAllowedReadOnlySubagent(args) {
|
|
|
229
230
|
function isPlanFileWrite(toolName, args, cwd) {
|
|
230
231
|
// For Write/Edit-style tools, check file_path/path/notebook_path.
|
|
231
232
|
// For ApplyPatch-style tools, parse *** Add/Update/Delete File and *** Move to directives.
|
|
232
|
-
// Allow only if every target resolves to a .md file under
|
|
233
|
+
// Allow only if every target resolves to a .md file under CONFIG_ROOT/plans/.
|
|
233
234
|
}
|
|
234
235
|
|
|
235
236
|
if (letta.capabilities.permissions) {
|
|
236
237
|
disposers.push(letta.permissions.register({
|
|
237
238
|
id: "plan-mode",
|
|
238
|
-
description: "Allow read-only tools and writes only to
|
|
239
|
+
description: "Allow read-only tools and writes only to the active product plans directory while plan mode is active.",
|
|
239
240
|
check(event) {
|
|
240
241
|
const session = getSession(event.conversationId);
|
|
241
242
|
if (!session) return;
|
|
@@ -267,7 +268,7 @@ if (letta.capabilities.permissions) {
|
|
|
267
268
|
}
|
|
268
269
|
```
|
|
269
270
|
|
|
270
|
-
Shell allowlists are easy to get wrong. Start conservative: allow clearly read-only shell commands if needed, plus a narrow plan-file heredoc or `mv old.md new.md` only when every target is inside
|
|
271
|
+
Shell allowlists are easy to get wrong. Start conservative: allow clearly read-only shell commands if needed, plus a narrow plan-file heredoc or `mv old.md new.md` only when every target is inside `${LETTA_CONFIG_DIR:-$HOME/.letta}/plans/*.md`. Deny mutating shell patterns such as `sed -i`, `find -delete`, `rm`, `cp`, `touch`, package installs, and arbitrary interpreters.
|
|
271
272
|
|
|
272
273
|
## Exit tool
|
|
273
274
|
|
|
@@ -315,4 +316,4 @@ if (letta.capabilities.tools) {
|
|
|
315
316
|
- Keep `exit_plan_mode` as the final state transition and execution handoff. The approved-plan text in its tool return is useful model context.
|
|
316
317
|
- Plan approval must include the full current plan text in `AskUserQuestion.question`, not just a summary or "does this look right?". After revisions, re-read the file and present the full revised plan again.
|
|
317
318
|
- Keep arbitrary coding subagents denied in plan mode unless the runtime has a true read-only child mode. With the current subagent set, allow only recall-style subagents.
|
|
318
|
-
- If the user renames the plan file, exit logic can use the newest non-empty
|
|
319
|
+
- If the user renames the plan file, exit logic can use the newest non-empty file under the active product's `plans/` directory modified after plan mode started, or accept an optional plan path. Keep the user-facing flow normal: write plan file, ask approval, then exit.
|
|
@@ -9,7 +9,7 @@ For multi-capability mods that combine a provider with commands, tools, UI, or s
|
|
|
9
9
|
## Quick pattern
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
|
-
//
|
|
12
|
+
// ${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/kilo.ts
|
|
13
13
|
export default function activate(letta) {
|
|
14
14
|
if (!letta.capabilities.providers) return;
|
|
15
15
|
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Examples:
|
|
9
9
|
* npx tsx init-skill.ts my-new-skill --path .skills
|
|
10
|
-
* npx tsx init-skill.ts my-api-helper --path
|
|
10
|
+
* npx tsx init-skill.ts my-api-helper --path "${LETTA_CONFIG_DIR:-$HOME/.letta}/skills"
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
@@ -268,7 +268,9 @@ if (isMainModule()) {
|
|
|
268
268
|
console.log(" - Must match directory name exactly");
|
|
269
269
|
console.log("\nExamples:");
|
|
270
270
|
console.log(" npx tsx init-skill.ts my-new-skill --path .skills");
|
|
271
|
-
console.log(
|
|
271
|
+
console.log(
|
|
272
|
+
` npx tsx init-skill.ts my-api-helper --path "\${LETTA_CONFIG_DIR:-$HOME/.letta}/skills"`,
|
|
273
|
+
);
|
|
272
274
|
process.exit(1);
|
|
273
275
|
}
|
|
274
276
|
|
|
@@ -10,10 +10,10 @@ Use this as the command-specific entrypoint for local mod slash commands. For br
|
|
|
10
10
|
Mod files live in:
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
|
-
|
|
13
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Use a focused file name, e.g.
|
|
16
|
+
Use a focused file name, e.g. `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/review.ts` or `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/commands.ts`.
|
|
17
17
|
|
|
18
18
|
## First decide whether a command is right
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@ If the command is a reusable workflow like `/goal`, put the workflow instruction
|
|
|
29
29
|
|
|
30
30
|
## Workflow
|
|
31
31
|
|
|
32
|
-
1. Inspect
|
|
32
|
+
1. Inspect `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/` for related command files.
|
|
33
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.
|
|
@@ -8,7 +8,7 @@ description: Creates, edits, and migrates Letta Code statusline mods. Use when h
|
|
|
8
8
|
Use this skill to create or update the global Letta Code statusline mod:
|
|
9
9
|
|
|
10
10
|
```text
|
|
11
|
-
|
|
11
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The statusline is a panel registered at `order: 0` — the primary line just below the input. It overrides the built-in `agent · model` line. Host UI can still temporarily preempt it for safety confirmations and transient hints.
|
|
@@ -26,14 +26,14 @@ The order-0 panel owns the whole primary row. It renders text (not React) and ow
|
|
|
26
26
|
|
|
27
27
|
## Workflow
|
|
28
28
|
|
|
29
|
-
1. Check whether
|
|
29
|
+
1. Check whether `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx` exists.
|
|
30
30
|
2. If it exists, read it before editing and preserve unrelated code.
|
|
31
31
|
3. If it does not exist, synthesize a focused starter for the user's request.
|
|
32
32
|
4. If the user asks to migrate, import a `.sh` file, or match a shell prompt, read `references/migration.md`.
|
|
33
33
|
5. If API details or concrete patterns are needed, read `references/api.md` and `references/examples.md`.
|
|
34
34
|
6. If the request combines statusline work with commands, tools, events, other panels, or stateful mod behavior, also use `creating-mods` and its `references/architecture.md`.
|
|
35
35
|
7. Guard panel work with `letta.capabilities.ui.panels` when writing new files.
|
|
36
|
-
8. Edit
|
|
36
|
+
8. Edit `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`.
|
|
37
37
|
9. Summarize the absolute file path changed and tell the user to run `/reload` unless the command can reload automatically.
|
|
38
38
|
|
|
39
39
|
## Bare `/statusline` behavior
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Statusline Mod API
|
|
2
2
|
|
|
3
|
-
Use this reference when creating or editing
|
|
3
|
+
Use this reference when creating or editing `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`.
|
|
4
4
|
|
|
5
5
|
## Location
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
|
-
|
|
8
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
This is a trusted, user-owned global mod file. Project mods are intentionally unsupported for now.
|
|
@@ -147,7 +147,7 @@ render: ({ width, columns }) => columns(["left", "middle", "right"], width),
|
|
|
147
147
|
|
|
148
148
|
## Reload behavior
|
|
149
149
|
|
|
150
|
-
After editing
|
|
150
|
+
After editing `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`, tell the user to run:
|
|
151
151
|
|
|
152
152
|
```text
|
|
153
153
|
/reload
|