fix(subagents): run bundled JS launcher via runtime on Windows (#975)
Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -1,5 +1,122 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { resolveSubagentModel } from "../../agent/subagents/manager";
|
||||
import {
|
||||
resolveSubagentLauncher,
|
||||
resolveSubagentModel,
|
||||
} from "../../agent/subagents/manager";
|
||||
|
||||
describe("resolveSubagentLauncher", () => {
|
||||
test("explicit launcher takes precedence over .ts script autodetection", () => {
|
||||
const launcher = resolveSubagentLauncher(["-p", "hi"], {
|
||||
env: {
|
||||
LETTA_CODE_BIN: "custom-bun",
|
||||
LETTA_CODE_BIN_ARGS_JSON: JSON.stringify(["run", "src/index.ts"]),
|
||||
} as NodeJS.ProcessEnv,
|
||||
argv: ["bun", "/tmp/dev-entry.ts"],
|
||||
execPath: "/opt/homebrew/bin/bun",
|
||||
platform: "darwin",
|
||||
});
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "custom-bun",
|
||||
args: ["run", "src/index.ts", "-p", "hi"],
|
||||
});
|
||||
});
|
||||
|
||||
test("explicit launcher takes precedence over .js script autodetection", () => {
|
||||
const launcher = resolveSubagentLauncher(["-p", "hi"], {
|
||||
env: {
|
||||
LETTA_CODE_BIN: "custom-node",
|
||||
} as NodeJS.ProcessEnv,
|
||||
argv: ["node", "/tmp/letta.js"],
|
||||
execPath: "/usr/local/bin/node",
|
||||
platform: "win32",
|
||||
});
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "custom-node",
|
||||
args: ["-p", "hi"],
|
||||
});
|
||||
});
|
||||
|
||||
test("preserves existing .ts dev behavior for any ts entrypoint", () => {
|
||||
const launcher = resolveSubagentLauncher(
|
||||
["--output-format", "stream-json"],
|
||||
{
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
argv: ["bun", "/tmp/custom-runner.ts"],
|
||||
execPath: "/opt/homebrew/bin/bun",
|
||||
platform: "darwin",
|
||||
},
|
||||
);
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "/opt/homebrew/bin/bun",
|
||||
args: ["/tmp/custom-runner.ts", "--output-format", "stream-json"],
|
||||
});
|
||||
});
|
||||
|
||||
test("uses node runtime for bundled js on win32", () => {
|
||||
const launcher = resolveSubagentLauncher(["-p", "prompt"], {
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
argv: ["node", "C:\\Program Files\\Letta\\letta.js"],
|
||||
execPath: "C:\\Program Files\\nodejs\\node.exe",
|
||||
platform: "win32",
|
||||
});
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "C:\\Program Files\\nodejs\\node.exe",
|
||||
args: ["C:\\Program Files\\Letta\\letta.js", "-p", "prompt"],
|
||||
});
|
||||
});
|
||||
|
||||
test("keeps direct js spawn behavior on non-win32", () => {
|
||||
const launcher = resolveSubagentLauncher(["-p", "prompt"], {
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
argv: ["node", "/usr/local/lib/letta.js"],
|
||||
execPath: "/usr/local/bin/node",
|
||||
platform: "linux",
|
||||
});
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "/usr/local/lib/letta.js",
|
||||
args: ["-p", "prompt"],
|
||||
});
|
||||
});
|
||||
|
||||
test("falls back to global letta when no launcher hints available", () => {
|
||||
const launcher = resolveSubagentLauncher(["-p", "prompt"], {
|
||||
env: {} as NodeJS.ProcessEnv,
|
||||
argv: ["node", ""],
|
||||
execPath: "/usr/local/bin/node",
|
||||
platform: "linux",
|
||||
});
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "letta",
|
||||
args: ["-p", "prompt"],
|
||||
});
|
||||
});
|
||||
|
||||
test("keeps explicit launcher with spaces as a single command token", () => {
|
||||
const launcher = resolveSubagentLauncher(
|
||||
["--output-format", "stream-json"],
|
||||
{
|
||||
env: {
|
||||
LETTA_CODE_BIN:
|
||||
'"C:\\Users\\Example User\\AppData\\Roaming\\npm\\letta.cmd"',
|
||||
} as NodeJS.ProcessEnv,
|
||||
argv: ["node", "C:\\Program Files\\Letta\\letta.js"],
|
||||
execPath: "C:\\Program Files\\nodejs\\node.exe",
|
||||
platform: "win32",
|
||||
},
|
||||
);
|
||||
|
||||
expect(launcher).toEqual({
|
||||
command: "C:\\Users\\Example User\\AppData\\Roaming\\npm\\letta.cmd",
|
||||
args: ["--output-format", "stream-json"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveSubagentModel", () => {
|
||||
test("prefers BYOK-swapped handle when available", async () => {
|
||||
|
||||
@@ -24,6 +24,22 @@ describe("shellEnv letta shim", () => {
|
||||
});
|
||||
});
|
||||
|
||||
test("resolveLettaInvocation strips accidental wrapping quotes in LETTA_CODE_BIN", () => {
|
||||
const invocation = resolveLettaInvocation(
|
||||
{
|
||||
LETTA_CODE_BIN:
|
||||
'"C:\\Users\\Example User\\AppData\\Roaming\\npm\\letta.cmd"',
|
||||
},
|
||||
["node", "/irrelevant/script.js"],
|
||||
"/opt/homebrew/bin/bun",
|
||||
);
|
||||
|
||||
expect(invocation).toEqual({
|
||||
command: "C:\\Users\\Example User\\AppData\\Roaming\\npm\\letta.cmd",
|
||||
args: [],
|
||||
});
|
||||
});
|
||||
|
||||
test("resolveLettaInvocation infers dev entrypoint launcher", () => {
|
||||
const invocation = resolveLettaInvocation(
|
||||
{},
|
||||
|
||||
Reference in New Issue
Block a user