Files
letta-code-sdk/src/transport.test.ts

128 lines
4.6 KiB
TypeScript

import { describe, expect, test } from "bun:test";
import { createRequire } from "node:module";
import { existsSync } from "node:fs";
import { SubprocessTransport } from "./transport.js";
describe("CLI resolution", () => {
test("resolves @letta-ai/letta-code via package main export", () => {
// This is the resolution strategy used by findCli().
// Previously we resolved "@letta-ai/letta-code/letta.js" which fails
// with ERR_PACKAGE_PATH_NOT_EXPORTED because the subpath isn't in the
// package.json exports field. The main export "." maps to "./letta.js".
const require = createRequire(import.meta.url);
const resolved = require.resolve("@letta-ai/letta-code");
expect(resolved).toBeDefined();
expect(resolved.endsWith("letta.js")).toBe(true);
expect(existsSync(resolved)).toBe(true);
});
test("subpath resolution fails without explicit export", () => {
// This documents why we can't use the subpath directly.
const require = createRequire(import.meta.url);
expect(() => {
require.resolve("@letta-ai/letta-code/letta.js");
}).toThrow();
});
});
describe("transport args", () => {
function buildArgsFor(options: {
permissionMode?: "default" | "acceptEdits" | "plan" | "bypassPermissions";
allowedTools?: string[];
disallowedTools?: string[];
memfs?: boolean;
skillSources?: Array<"bundled" | "global" | "agent" | "project">;
systemInfoReminder?: boolean;
sleeptime?: {
trigger?: "off" | "step-count" | "compaction-event";
behavior?: "reminder" | "auto-launch";
stepCount?: number;
};
} = {}): string[] {
const transport = new SubprocessTransport(options);
// Access private helper for deterministic argument testing.
// biome-ignore lint/suspicious/noExplicitAny: test access to private method
return (transport as any).buildArgs();
}
test("acceptEdits uses --permission-mode acceptEdits", () => {
const args = buildArgsFor({ permissionMode: "acceptEdits" });
expect(args).toContain("--permission-mode");
expect(args).toContain("acceptEdits");
expect(args).not.toContain("--accept-edits");
});
test("plan mode uses --permission-mode plan", () => {
const args = buildArgsFor({ permissionMode: "plan" });
expect(args).toContain("--permission-mode");
expect(args).toContain("plan");
});
test("bypassPermissions still uses --yolo alias", () => {
const args = buildArgsFor({ permissionMode: "bypassPermissions" });
expect(args).toContain("--yolo");
expect(args).not.toContain("--permission-mode");
});
test("allowedTools and disallowedTools are forwarded to CLI flags", () => {
const args = buildArgsFor({
allowedTools: ["Read", "Bash"],
disallowedTools: ["EnterPlanMode", "ExitPlanMode"],
});
expect(args).toContain("--allowedTools");
expect(args).toContain("Read,Bash");
expect(args).toContain("--disallowedTools");
expect(args).toContain("EnterPlanMode,ExitPlanMode");
});
test("memfs true forwards --memfs", () => {
const args = buildArgsFor({ memfs: true });
expect(args).toContain("--memfs");
});
test("memfs false forwards --no-memfs", () => {
const args = buildArgsFor({ memfs: false });
expect(args).toContain("--no-memfs");
expect(args).not.toContain("--memfs");
});
test("memfs undefined does not forward memfs flags", () => {
expect(buildArgsFor({})).not.toContain("--memfs");
expect(buildArgsFor({})).not.toContain("--no-memfs");
});
test("empty skillSources forwards --no-skills", () => {
const args = buildArgsFor({ skillSources: [] });
expect(args).toContain("--no-skills");
expect(args).not.toContain("--skill-sources");
});
test("skillSources list forwards --skill-sources csv", () => {
const args = buildArgsFor({ skillSources: ["project", "global"] });
expect(args).toContain("--skill-sources");
expect(args).toContain("project,global");
expect(args).not.toContain("--no-skills");
});
test("systemInfoReminder false forwards --no-system-info-reminder", () => {
const args = buildArgsFor({ systemInfoReminder: false });
expect(args).toContain("--no-system-info-reminder");
});
test("sleeptime options forward reflection flags", () => {
const args = buildArgsFor({
sleeptime: {
trigger: "step-count",
behavior: "reminder",
stepCount: 12,
},
});
expect(args).toContain("--reflection-trigger");
expect(args).toContain("step-count");
expect(args).toContain("--reflection-behavior");
expect(args).toContain("reminder");
expect(args).toContain("--reflection-step-count");
expect(args).toContain("12");
});
});