@letta-ai/letta-code 0.32.7 → 0.32.8
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/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/types/agent/message-attribution.d.ts +10 -0
- package/dist/types/agent/message-attribution.d.ts.map +1 -0
- package/dist/types/agent/subagents/manager.d.ts.map +1 -1
- package/dist/types/agent/subagents/names.d.ts +7 -0
- package/dist/types/agent/subagents/names.d.ts.map +1 -0
- package/dist/types/agent/subagents/subagent-launcher.d.ts +2 -0
- package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
- package/dist/types/backend/api/agent-message.d.ts +5 -0
- package/dist/types/backend/api/agent-message.d.ts.map +1 -1
- package/dist/types/backend/backend.d.ts.map +1 -1
- package/dist/types/queue/queue-runtime.d.ts.map +1 -1
- package/dist/types/tools/impl/send-agent-message.d.ts.map +1 -1
- package/dist/types/utils/subagent-launch-marker.d.ts +1 -0
- package/dist/types/utils/subagent-launch-marker.d.ts.map +1 -1
- package/dist/types/websocket/listener/protocol-outbound.d.ts.map +1 -1
- package/dist/types/websocket/listener/runtime.d.ts.map +1 -1
- package/dist/types/websocket/listener/types.d.ts +2 -1
- package/dist/types/websocket/listener/types.d.ts.map +1 -1
- package/letta.js +407 -242
- package/package.json +1 -1
- package/scripts/run-unit-tests.cjs +11 -1
- package/scripts/source-file-size-baseline.json +2 -2
- package/scripts/test-sharding.cjs +16 -0
- package/scripts/test-sharding.test.cjs +37 -0
- package/scripts/unit-test-impact.cjs +14 -6
- package/scripts/unit-test-impact.test.cjs +41 -9
- package/skills/working-across-computers/SKILL.md +5 -1
package/package.json
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
const { execFileSync } = require("node:child_process");
|
|
3
3
|
const { readFileSync, readdirSync } = require("node:fs");
|
|
4
4
|
const path = require("node:path");
|
|
5
|
+
const { shardTestFiles } = require("./test-sharding.cjs");
|
|
5
6
|
const {
|
|
6
7
|
buildFamilyImpactIndex,
|
|
7
8
|
getGitChangedFiles,
|
|
@@ -77,6 +78,7 @@ const allTestFiles = [
|
|
|
77
78
|
...findTestFiles("scripts/codex-watch"),
|
|
78
79
|
...findTestFiles("scripts/claude-watch"),
|
|
79
80
|
"scripts/unit-test-impact.test.cjs",
|
|
81
|
+
"scripts/test-sharding.test.cjs",
|
|
80
82
|
].sort();
|
|
81
83
|
const discoveredPaths = new Set(allTestFiles);
|
|
82
84
|
|
|
@@ -192,7 +194,15 @@ function selectTestFiles() {
|
|
|
192
194
|
}
|
|
193
195
|
|
|
194
196
|
let exitCode = 0;
|
|
195
|
-
const
|
|
197
|
+
const shardIndex = process.argv.indexOf("--shard");
|
|
198
|
+
const shard = shardIndex === -1 ? undefined : (process.argv[shardIndex + 1] ?? "");
|
|
199
|
+
// Validate outside selection's full-suite fallback: an invalid shard must fail,
|
|
200
|
+
// not make several jobs duplicate the entire suite or silently run nothing.
|
|
201
|
+
shardTestFiles([], shard);
|
|
202
|
+
const selectedTestFiles = shardTestFiles(selectTestFiles(), shard);
|
|
203
|
+
if (shard) {
|
|
204
|
+
console.log(`unit-test shard ${shard}: ${selectedTestFiles.length} files`);
|
|
205
|
+
}
|
|
196
206
|
const selectedTestPaths = new Set(selectedTestFiles);
|
|
197
207
|
|
|
198
208
|
// Bun module mocks and process-global state are shared within one test process.
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"src/tools/manager.ts": 2897,
|
|
39
39
|
"src/tools/tool-execution-context.test.ts": 1136,
|
|
40
40
|
"src/types/protocol_v2.ts": 2595,
|
|
41
|
-
"src/websocket/listen-client-concurrency.test.ts":
|
|
41
|
+
"src/websocket/listen-client-concurrency.test.ts": 2677,
|
|
42
42
|
"src/websocket/listen-client-protocol.test.ts": 5762,
|
|
43
43
|
"src/websocket/listener/commands/memory.ts": 1114,
|
|
44
44
|
"src/websocket/listener/file-commands.ts": 1053,
|
|
45
45
|
"src/websocket/listener/protocol-inbound.ts": 2143,
|
|
46
|
-
"src/websocket/listener/protocol-outbound.ts":
|
|
46
|
+
"src/websocket/listener/protocol-outbound.ts": 1022
|
|
47
47
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// Assign files before launching Bun, including suites that need their own
|
|
2
|
+
// process. Sharding each Bun invocation separately would duplicate or omit
|
|
3
|
+
// isolated suites. Keep file order within each shard unchanged.
|
|
4
|
+
function shardTestFiles(files, shard) {
|
|
5
|
+
if (shard === undefined) return files;
|
|
6
|
+
if (!/^[1-9]\d*\/[1-9]\d*$/u.test(shard)) {
|
|
7
|
+
throw new Error("Expected --shard INDEX/COUNT, with 1 <= INDEX <= COUNT");
|
|
8
|
+
}
|
|
9
|
+
const [index, count] = shard.split("/").map(Number);
|
|
10
|
+
if (!Number.isSafeInteger(count) || index > count) {
|
|
11
|
+
throw new Error("Expected --shard INDEX/COUNT, with 1 <= INDEX <= COUNT");
|
|
12
|
+
}
|
|
13
|
+
return files.filter((_, position) => position % count === index - 1);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
module.exports = { shardTestFiles };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
const { describe, expect, test } = require("bun:test");
|
|
2
|
+
const { shardTestFiles } = require("./test-sharding.cjs");
|
|
3
|
+
const { tests: isolated } = require("./isolated-unit-tests.json");
|
|
4
|
+
|
|
5
|
+
describe("unit test sharding", () => {
|
|
6
|
+
const files = [
|
|
7
|
+
...isolated.map(({ path }) => path),
|
|
8
|
+
...Array.from({ length: 103 }, (_, i) => `src/tools/file-${i}.test.ts`),
|
|
9
|
+
].sort();
|
|
10
|
+
|
|
11
|
+
test.each([1, 4, 8, 200])("%i shards cover every file exactly once", (count) => {
|
|
12
|
+
const shards = Array.from({ length: count }, (_, i) =>
|
|
13
|
+
shardTestFiles(files, `${i + 1}/${count}`),
|
|
14
|
+
);
|
|
15
|
+
expect(shards.flat().sort()).toEqual(files);
|
|
16
|
+
expect(new Set(shards.flat()).size).toBe(files.length);
|
|
17
|
+
for (const shard of shards) {
|
|
18
|
+
expect(shard).toEqual(files.filter((file) => shard.includes(file)));
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("selection stays empty rather than expanding into all tests", () => {
|
|
23
|
+
expect(shardTestFiles([], "1/8")).toEqual([]);
|
|
24
|
+
expect(shardTestFiles([files[0]], "8/8")).toEqual([]);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("local runs without a shard preserve the full selection", () => {
|
|
28
|
+
expect(shardTestFiles(files)).toEqual(files);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test.each(["", "0/4", "1/0", "5/4", "-1/4", "1.5/4", "1/2/3", "1/Infinity"])(
|
|
32
|
+
"rejects invalid shard %s rather than silently dropping coverage",
|
|
33
|
+
(shard) => {
|
|
34
|
+
expect(() => shardTestFiles(files, shard)).toThrow("Expected --shard");
|
|
35
|
+
},
|
|
36
|
+
);
|
|
37
|
+
});
|
|
@@ -527,14 +527,22 @@ function getGitChangedFiles(baseSha, headSha, rootDir = process.cwd()) {
|
|
|
527
527
|
});
|
|
528
528
|
}
|
|
529
529
|
|
|
530
|
-
function readPullRequestShas(eventPath) {
|
|
530
|
+
function readPullRequestShas(eventPath, rootDir = process.cwd()) {
|
|
531
531
|
const event = JSON.parse(readFileSync(eventPath, "utf8"));
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
event.pull_request?.merge_commit_sha ?? event.pull_request?.head?.sha;
|
|
535
|
-
if (typeof baseSha !== "string" || typeof headSha !== "string") {
|
|
536
|
-
throw new Error("Pull request event is missing base/head SHAs");
|
|
532
|
+
if (!event.pull_request) {
|
|
533
|
+
throw new Error("Expected a pull request event");
|
|
537
534
|
}
|
|
535
|
+
// GitHub can enqueue an event whose merge_commit_sha differs from the merge
|
|
536
|
+
// actions/checkout fetches. Compare the tested tree with its own base parent;
|
|
537
|
+
// both are present at fetch-depth: 2. Requiring HEAD^2 also rejects a checkout
|
|
538
|
+
// of a branch head, so the caller safely runs everything in that case.
|
|
539
|
+
const [baseSha, , headSha] = execFileSync(
|
|
540
|
+
"git",
|
|
541
|
+
["rev-parse", "HEAD^1", "HEAD^2", "HEAD"],
|
|
542
|
+
{ cwd: rootDir, encoding: "utf8" },
|
|
543
|
+
)
|
|
544
|
+
.trim()
|
|
545
|
+
.split(/\r?\n/u);
|
|
538
546
|
return { baseSha, headSha };
|
|
539
547
|
}
|
|
540
548
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
const { describe, expect, test } = require("bun:test");
|
|
2
|
+
const { execFileSync } = require("node:child_process");
|
|
2
3
|
const { mkdtempSync, rmSync, writeFileSync } = require("node:fs");
|
|
3
4
|
const { tmpdir } = require("node:os");
|
|
4
5
|
const { join } = require("node:path");
|
|
6
|
+
const { pathToFileURL } = require("node:url");
|
|
5
7
|
const {
|
|
6
8
|
ROOT_FAMILY,
|
|
7
9
|
buildFamilyImpactIndex,
|
|
10
|
+
getGitChangedFiles,
|
|
8
11
|
planUnitTests,
|
|
9
12
|
readPullRequestShas,
|
|
10
13
|
} = require("./unit-test-impact.cjs");
|
|
@@ -233,29 +236,58 @@ describe("unit-test impact planning", () => {
|
|
|
233
236
|
expect(result.reason).toBe("no changed files were provided");
|
|
234
237
|
});
|
|
235
238
|
|
|
236
|
-
test("
|
|
239
|
+
test("selection uses the shallow checkout when the event names a stale merge", () => {
|
|
237
240
|
const directory = mkdtempSync(join(tmpdir(), "unit-impact-event-"));
|
|
238
241
|
const eventPath = join(directory, "event.json");
|
|
242
|
+
const checkout = join(directory, "checkout");
|
|
243
|
+
const git = (...args) =>
|
|
244
|
+
execFileSync("git", args, {
|
|
245
|
+
cwd: directory,
|
|
246
|
+
encoding: "utf8",
|
|
247
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
248
|
+
env: {
|
|
249
|
+
...process.env,
|
|
250
|
+
GIT_AUTHOR_NAME: "Test",
|
|
251
|
+
GIT_AUTHOR_EMAIL: "test@example.com",
|
|
252
|
+
GIT_COMMITTER_NAME: "Test",
|
|
253
|
+
GIT_COMMITTER_EMAIL: "test@example.com",
|
|
254
|
+
},
|
|
255
|
+
}).trim();
|
|
239
256
|
try {
|
|
257
|
+
git("init", "-b", "main");
|
|
258
|
+
writeFileSync(join(directory, "base.txt"), "base");
|
|
259
|
+
git("add", "base.txt");
|
|
260
|
+
git("-c", "core.hooksPath=", "commit", "-m", "base");
|
|
261
|
+
const baseSha = git("rev-parse", "HEAD");
|
|
262
|
+
git("checkout", "-b", "feature");
|
|
263
|
+
writeFileSync(join(directory, "changed.txt"), "feature");
|
|
264
|
+
git("add", "changed.txt");
|
|
265
|
+
git("-c", "core.hooksPath=", "commit", "-m", "feature");
|
|
266
|
+
const branchHead = git("rev-parse", "HEAD");
|
|
267
|
+
git("checkout", "main");
|
|
268
|
+
git("-c", "core.hooksPath=", "merge", "--no-ff", "feature", "-m", "merge");
|
|
269
|
+
const headSha = git("rev-parse", "HEAD");
|
|
270
|
+
git("clone", "--depth=2", pathToFileURL(directory).href, checkout);
|
|
240
271
|
writeFileSync(
|
|
241
272
|
eventPath,
|
|
242
273
|
JSON.stringify({
|
|
243
274
|
pull_request: {
|
|
244
|
-
base: { sha:
|
|
245
|
-
head: { sha:
|
|
246
|
-
merge_commit_sha: "
|
|
275
|
+
base: { sha: baseSha },
|
|
276
|
+
head: { sha: branchHead },
|
|
277
|
+
merge_commit_sha: "700fce0cf421cd048f810e40822d608d7c81a2bc",
|
|
247
278
|
},
|
|
248
279
|
}),
|
|
249
280
|
);
|
|
250
281
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
282
|
+
const shas = readPullRequestShas(eventPath, checkout);
|
|
283
|
+
expect(shas).toEqual({ baseSha, headSha });
|
|
284
|
+
expect(getGitChangedFiles(shas.baseSha, shas.headSha, checkout)).toEqual([
|
|
285
|
+
{ status: "A", path: "changed.txt", previousPath: undefined },
|
|
286
|
+
]);
|
|
255
287
|
} finally {
|
|
256
288
|
rmSync(directory, { recursive: true, force: true });
|
|
257
289
|
}
|
|
258
|
-
});
|
|
290
|
+
}, 30000);
|
|
259
291
|
});
|
|
260
292
|
|
|
261
293
|
describe("current repository impact graph", () => {
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: working-across-computers
|
|
3
|
-
description: Guides work across Cloud, local, and other available computers, including teleporting
|
|
3
|
+
description: Guides work across Cloud, local, and other available computers, including teleporting conversations, running remote subagents, and transferring files. Load when a request needs the user's laptop or another machine's files, apps, or tools; when connecting a computer; or when moving or delegating work between local and Cloud in either direction, including running Cloud subagents from local.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Working Across Computers
|
|
7
7
|
|
|
8
|
+
## Discover access before declaring a limitation
|
|
9
|
+
|
|
10
|
+
The current filesystem is not the full set of computers you can use. When a request needs another computer, run `letta teleport list`. If a suitable computer is available, delegate there; otherwise follow the [connection instructions](references/connect-a-computer-for-remote-access.md). Do not infer that another computer is inaccessible merely because you are running in Cloud.
|
|
11
|
+
|
|
8
12
|
## Choose where to work
|
|
9
13
|
|
|
10
14
|
- When running in Cloud, prefer staying there and delegating local work to subagents on the relevant computer.
|