@letta-ai/letta-code 0.30.13 → 0.30.14

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 (58) hide show
  1. package/dist/channels-slack.js +106 -26
  2. package/dist/channels-slack.js.map +5 -4
  3. package/dist/gateway-core.js +12 -2
  4. package/dist/gateway-core.js.map +3 -3
  5. package/dist/mcp-client.js +2 -2
  6. package/dist/mcp-client.js.map +1 -1
  7. package/dist/types/channels/message-channel-executor.d.ts +4 -0
  8. package/dist/types/channels/message-channel-executor.d.ts.map +1 -1
  9. package/dist/types/channels/slack/bot-policy.d.ts +25 -0
  10. package/dist/types/channels/slack/bot-policy.d.ts.map +1 -0
  11. package/dist/types/channels/slack/ingress-policy.d.ts +32 -1
  12. package/dist/types/channels/slack/ingress-policy.d.ts.map +1 -1
  13. package/dist/types/channels-slack.d.ts +3 -2
  14. package/dist/types/channels-slack.d.ts.map +1 -1
  15. package/dist/types/gateway-core.d.ts +1 -1
  16. package/dist/types/gateway-core.d.ts.map +1 -1
  17. package/dist/types/permissions/analyzer.d.ts.map +1 -1
  18. package/dist/types/permissions/canonical.d.ts +1 -0
  19. package/dist/types/permissions/canonical.d.ts.map +1 -1
  20. package/dist/types/permissions/checker.d.ts.map +1 -1
  21. package/dist/types/tools/impl/bash.d.ts +7 -0
  22. package/dist/types/tools/impl/bash.d.ts.map +1 -1
  23. package/dist/types/tools/impl/enter-worktree.d.ts +16 -1
  24. package/dist/types/tools/impl/enter-worktree.d.ts.map +1 -1
  25. package/dist/types/tools/impl/exit-worktree.d.ts +21 -0
  26. package/dist/types/tools/impl/exit-worktree.d.ts.map +1 -0
  27. package/dist/types/tools/impl/kill-bash.d.ts.map +1 -1
  28. package/dist/types/tools/impl/monitor-event-stream.d.ts +23 -0
  29. package/dist/types/tools/impl/monitor-event-stream.d.ts.map +1 -0
  30. package/dist/types/tools/impl/monitor.d.ts +30 -0
  31. package/dist/types/tools/impl/monitor.d.ts.map +1 -0
  32. package/dist/types/tools/impl/process_manager.d.ts +8 -0
  33. package/dist/types/tools/impl/process_manager.d.ts.map +1 -1
  34. package/dist/types/tools/impl/shell-runner.d.ts +1 -0
  35. package/dist/types/tools/impl/shell-runner.d.ts.map +1 -1
  36. package/dist/types/tools/impl/worktree-git.d.ts +26 -0
  37. package/dist/types/tools/impl/worktree-git.d.ts.map +1 -0
  38. package/dist/types/tools/manager.d.ts.map +1 -1
  39. package/dist/types/tools/tool-definitions.d.ts +2 -0
  40. package/dist/types/tools/tool-definitions.d.ts.map +1 -1
  41. package/dist/types/tools/tool-permissions.d.ts +7 -0
  42. package/dist/types/tools/tool-permissions.d.ts.map +1 -0
  43. package/dist/types/types/background-process-protocol.d.ts +29 -0
  44. package/dist/types/types/background-process-protocol.d.ts.map +1 -0
  45. package/dist/types/types/protocol_v2.d.ts +2 -19
  46. package/dist/types/types/protocol_v2.d.ts.map +1 -1
  47. package/dist/types/utils/task-notifications.d.ts +5 -0
  48. package/dist/types/utils/task-notifications.d.ts.map +1 -1
  49. package/dist/types/websocket/listener/background-process-snapshot.d.ts.map +1 -1
  50. package/dist/types/websocket/listener/types.d.ts +2 -0
  51. package/dist/types/websocket/listener/types.d.ts.map +1 -1
  52. package/letta.js +3682 -2358
  53. package/package.json +1 -1
  54. package/scripts/isolated-unit-tests.json +5 -0
  55. package/scripts/run-unit-tests.cjs +83 -2
  56. package/scripts/source-file-size-baseline.json +3 -4
  57. package/scripts/unit-test-impact.cjs +578 -0
  58. package/scripts/unit-test-impact.test.cjs +328 -0
@@ -0,0 +1,328 @@
1
+ const { describe, expect, test } = require("bun:test");
2
+ const { mkdtempSync, rmSync, writeFileSync } = require("node:fs");
3
+ const { tmpdir } = require("node:os");
4
+ const { join } = require("node:path");
5
+ const {
6
+ ROOT_FAMILY,
7
+ buildFamilyImpactIndex,
8
+ planUnitTests,
9
+ readPullRequestShas,
10
+ } = require("./unit-test-impact.cjs");
11
+
12
+ const allTestFiles = [
13
+ "src/agent/agent.test.ts",
14
+ "src/channels/channel.test.ts",
15
+ "src/cli/cli.test.tsx",
16
+ "src/integration-tests/api.test.ts",
17
+ "src/tools/tool.test.ts",
18
+ "src/websocket/socket.test.ts",
19
+ "src/startup-flow.test.ts",
20
+ "scripts/unit-test-impact.test.cjs",
21
+ ];
22
+
23
+ function impact(
24
+ dependencies = {},
25
+ unresolvedImports = [],
26
+ fileDependencies = {},
27
+ ) {
28
+ return {
29
+ familyDependencies: new Map(
30
+ Object.entries(dependencies).map(([family, paths]) => [
31
+ family,
32
+ new Set(paths),
33
+ ]),
34
+ ),
35
+ fileDependencies: new Map(
36
+ Object.entries(fileDependencies).map(([file, paths]) => [
37
+ file,
38
+ new Set(paths),
39
+ ]),
40
+ ),
41
+ unresolvedImports,
42
+ };
43
+ }
44
+
45
+ function plan(changedFiles, impactIndex = impact()) {
46
+ return planUnitTests({ changedFiles, allTestFiles, impactIndex });
47
+ }
48
+
49
+ describe("unit-test impact planning", () => {
50
+ test("runs the family that owns a changed source file", () => {
51
+ const result = plan([{ status: "M", path: "src/tools/impl/bash.ts" }]);
52
+
53
+ expect(result.mode).toBe("selected");
54
+ expect(result.selectedTests).toEqual(["src/tools/tool.test.ts"]);
55
+ expect(result.omittedTests).toContain("src/channels/channel.test.ts");
56
+ });
57
+
58
+ test("runs a family when it directly imports the changed file", () => {
59
+ const result = plan(
60
+ [{ status: "M", path: "src/tools/interactive-policy.ts" }],
61
+ impact({ channels: ["src/tools/interactive-policy.ts"] }),
62
+ );
63
+
64
+ expect(result.selectedTests).toEqual([
65
+ "src/channels/channel.test.ts",
66
+ "src/tools/tool.test.ts",
67
+ ]);
68
+ expect(result.selectedFamilies).toEqual([
69
+ {
70
+ family: "channels",
71
+ reasons: ["channels imports src/tools/interactive-policy.ts directly"],
72
+ },
73
+ {
74
+ family: "tools",
75
+ reasons: ["src/tools/interactive-policy.ts is owned by tools"],
76
+ },
77
+ ]);
78
+ });
79
+
80
+ test("channel package entrypoints select channel tests", () => {
81
+ const result = plan(
82
+ [{ status: "M", path: "src/channels-public.ts" }],
83
+ impact({}, [], {
84
+ "src/channels-public.ts": ["src/channels/gateway-core.ts"],
85
+ }),
86
+ );
87
+
88
+ expect(result.selectedTests).toEqual([
89
+ "src/channels/channel.test.ts",
90
+ "src/startup-flow.test.ts",
91
+ ]);
92
+ });
93
+
94
+ test("CLI app changes include root source-level contract tests", () => {
95
+ const result = plan([
96
+ { status: "M", path: "src/cli/app/use-submit-handler.ts" },
97
+ ]);
98
+
99
+ expect(result.selectedTests).toEqual([
100
+ "src/cli/cli.test.tsx",
101
+ "src/startup-flow.test.ts",
102
+ ]);
103
+ });
104
+
105
+ test("documentation-only changes select no unit tests", () => {
106
+ const result = plan([
107
+ { status: "M", path: "README.md" },
108
+ { status: "M", path: "docs/commands.md" },
109
+ { status: "M", path: "assets/letta-code-demo.gif" },
110
+ ]);
111
+
112
+ expect(result.mode).toBe("selected");
113
+ expect(result.selectedTests).toEqual([]);
114
+ expect(result.omittedTests).toEqual(allTestFiles);
115
+ });
116
+
117
+ test("documentation examples imported by source select their consumer", () => {
118
+ const result = plan(
119
+ [{ status: "M", path: "docs/examples/mods/example.ts" }],
120
+ impact({ cli: ["docs/examples/mods/example.ts"] }),
121
+ );
122
+
123
+ expect(result.mode).toBe("selected");
124
+ expect(result.selectedTests).toEqual(["src/cli/cli.test.tsx"]);
125
+ });
126
+
127
+ test("the bundled Tutor profile selects its agent tests", () => {
128
+ const result = plan([{ status: "M", path: "assets/tutor-profile.png" }]);
129
+
130
+ expect(result.mode).toBe("selected");
131
+ expect(result.selectedTests).toEqual(["src/agent/agent.test.ts"]);
132
+ });
133
+
134
+ test.each(["assets/runtime.bin", "assets/tutor-profile.png.bak"])(
135
+ "unknown asset %s widens to the full unit suite",
136
+ (filePath) => {
137
+ const result = plan([{ status: "A", path: filePath }]);
138
+
139
+ expect(result.mode).toBe("full");
140
+ },
141
+ );
142
+
143
+ test("the source-size baseline does not widen unit tests", () => {
144
+ const result = plan([
145
+ { status: "M", path: "scripts/source-file-size-baseline.json" },
146
+ { status: "M", path: "src/tools/impl/bash.ts" },
147
+ ]);
148
+
149
+ expect(result.mode).toBe("selected");
150
+ expect(result.selectedTests).toEqual(["src/tools/tool.test.ts"]);
151
+ });
152
+
153
+ test("integration-only changes leave unit tests to the integration step", () => {
154
+ const result = plan([
155
+ { status: "M", path: "src/integration-tests/api.test.ts" },
156
+ ]);
157
+
158
+ expect(result.mode).toBe("selected");
159
+ expect(result.selectedTests).toEqual([]);
160
+ });
161
+
162
+ test.each([
163
+ "package.json",
164
+ "bun.lock",
165
+ "tsconfig.json",
166
+ "build.js",
167
+ ".github/workflows/ci.yml",
168
+ "scripts/run-unit-tests.cjs",
169
+ "scripts/isolated-unit-tests.json",
170
+ ])("%s widens to the full unit suite", (filePath) => {
171
+ const result = plan([{ status: "M", path: filePath }]);
172
+
173
+ expect(result.mode).toBe("full");
174
+ expect(result.selectedTests).toEqual(allTestFiles);
175
+ });
176
+
177
+ test("unknown files widen to the full unit suite", () => {
178
+ const result = plan([{ status: "M", path: "config/runtime.yaml" }]);
179
+
180
+ expect(result.mode).toBe("full");
181
+ expect(result.reason).toContain("not classified");
182
+ });
183
+
184
+ test("deleted source files widen to the full unit suite", () => {
185
+ const result = plan([{ status: "D", path: "src/tools/removed.ts" }]);
186
+
187
+ expect(result.mode).toBe("full");
188
+ expect(result.reason).toContain("was deleted");
189
+ });
190
+
191
+ test("source files renamed into documentation still widen", () => {
192
+ const result = plan([
193
+ {
194
+ status: "R100",
195
+ path: "docs/old-tool.md",
196
+ previousPath: "src/tools/old-tool.ts",
197
+ },
198
+ ]);
199
+
200
+ expect(result.mode).toBe("full");
201
+ expect(result.reason).toContain("was renamed");
202
+ });
203
+
204
+ test("documentation-only renames select no unit tests", () => {
205
+ const result = plan([
206
+ {
207
+ status: "R100",
208
+ path: "docs/new-name.md",
209
+ previousPath: "docs/old-name.md",
210
+ },
211
+ ]);
212
+
213
+ expect(result.mode).toBe("selected");
214
+ expect(result.selectedTests).toEqual([]);
215
+ });
216
+
217
+ test("unresolved local imports widen to the full unit suite", () => {
218
+ const result = plan(
219
+ [{ status: "M", path: "src/tools/impl/bash.ts" }],
220
+ impact({}, [
221
+ { file: "src/channels/broken.ts", moduleSpecifier: "@/missing" },
222
+ ]),
223
+ );
224
+
225
+ expect(result.mode).toBe("full");
226
+ expect(result.reason).toContain("could not be resolved");
227
+ });
228
+
229
+ test("an empty change set widens to the full unit suite", () => {
230
+ const result = plan([]);
231
+
232
+ expect(result.mode).toBe("full");
233
+ expect(result.reason).toBe("no changed files were provided");
234
+ });
235
+
236
+ test("pull request selection compares the base with the tested merge tree", () => {
237
+ const directory = mkdtempSync(join(tmpdir(), "unit-impact-event-"));
238
+ const eventPath = join(directory, "event.json");
239
+ try {
240
+ writeFileSync(
241
+ eventPath,
242
+ JSON.stringify({
243
+ pull_request: {
244
+ base: { sha: "base-sha" },
245
+ head: { sha: "branch-head-sha" },
246
+ merge_commit_sha: "tested-merge-sha",
247
+ },
248
+ }),
249
+ );
250
+
251
+ expect(readPullRequestShas(eventPath)).toEqual({
252
+ baseSha: "base-sha",
253
+ headSha: "tested-merge-sha",
254
+ });
255
+ } finally {
256
+ rmSync(directory, { recursive: true, force: true });
257
+ }
258
+ });
259
+ });
260
+
261
+ describe("current repository impact graph", () => {
262
+ const impactIndex = buildFamilyImpactIndex(process.cwd());
263
+
264
+ test("resolves every literal local import", () => {
265
+ expect(impactIndex.unresolvedImports).toEqual([]);
266
+ });
267
+
268
+ test("the shell process refactor does not select channel tests", () => {
269
+ const currentTests = [
270
+ "src/channels/discord-service.test.ts",
271
+ "src/cli/bash-mode.test.ts",
272
+ "src/tools/bash-background.test.ts",
273
+ "src/websocket/listen-client-protocol.test.ts",
274
+ ];
275
+ const result = planUnitTests({
276
+ changedFiles: [
277
+ { status: "M", path: "src/tools/impl/bash.ts" },
278
+ { status: "M", path: "src/tools/impl/exec-command.ts" },
279
+ { status: "M", path: "src/tools/impl/shell-runner.ts" },
280
+ ],
281
+ allTestFiles: currentTests,
282
+ impactIndex,
283
+ });
284
+
285
+ expect(result.mode).toBe("selected");
286
+ expect(result.selectedTests).not.toContain(
287
+ "src/channels/discord-service.test.ts",
288
+ );
289
+ expect(result.selectedTests).toContain("src/tools/bash-background.test.ts");
290
+ });
291
+
292
+ test("a direct channel dependency still selects channel tests", () => {
293
+ const result = planUnitTests({
294
+ changedFiles: [{ status: "M", path: "src/tools/interactive-policy.ts" }],
295
+ allTestFiles: [
296
+ "src/channels/gateway-core.test.ts",
297
+ "src/tools/client-toolset.test.ts",
298
+ ],
299
+ impactIndex,
300
+ });
301
+
302
+ expect(result.selectedTests).toContain("src/channels/gateway-core.test.ts");
303
+ });
304
+
305
+ test("source files read through literal file URLs are direct dependencies", () => {
306
+ expect(impactIndex.familyDependencies.get("cli")).toContain(
307
+ "src/utils/image-resize.ts",
308
+ );
309
+ });
310
+
311
+ test("tracked documentation imports are direct dependencies", () => {
312
+ expect(impactIndex.familyDependencies.get("cli")).toContain(
313
+ "docs/examples/mods/learning/memory-citations.env.json",
314
+ );
315
+ });
316
+
317
+ test("literal calls through source-reader helpers are direct dependencies", () => {
318
+ expect(impactIndex.familyDependencies.get("cli")).toContain(
319
+ "src/auth/setup-ui.tsx",
320
+ );
321
+ expect(impactIndex.familyDependencies.get("tools")).toContain(
322
+ "src/websocket/listener/turn.ts",
323
+ );
324
+ expect(impactIndex.familyDependencies.get(ROOT_FAMILY)).toContain(
325
+ "src/cli/profile-selection.tsx",
326
+ );
327
+ });
328
+ });