File size: 3,252 Bytes
4c76b0d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | import { afterEach, describe, expect, it, vi } from "vitest";
import { withPluginRuntimeGatewayRequestScope } from "../../../plugins/runtime/gateway-request-scope.js";
import { withGatewayToolCallerIdentity } from "../../tools/gateway-caller-context.js";
import { setSubagentSpawnDepsForTest } from "./subagent-spawn-deps.js";
import { callNativeSubagentGateway } from "./subagent-spawn-gateway.js";
vi.mock("./subagent-spawn.runtime.js", async () => {
const dispatch = await import("../../../gateway/server-plugin-in-process-dispatch.js");
const scopes = await import("../../../gateway/method-scopes.js");
return {
...scopes,
dispatchGatewayMethodInProcess: dispatch.dispatchGatewayMethodInProcess,
hasInProcessGatewayContext: () => Boolean(dispatch.getInProcessGatewayRequestContext()),
callGateway: vi.fn(),
ensureContextEnginesInitialized: vi.fn(),
forkSessionEntryFromParent: vi.fn(),
getGlobalHookRunner: vi.fn(),
getRuntimeConfig: vi.fn(),
readPreparedModelCatalog: vi.fn(),
resolveProviderRefOwnership: vi.fn(),
resolveContextEngine: vi.fn(),
};
});
vi.mock("../../tools/gateway.js", () => ({ callGatewayTool: vi.fn() }));
afterEach(() => setSubagentSpawnDepsForTest());
describe("native subagent Gateway transport ownership", () => {
it.each(["caller", "captured", "scoped"])(
"rejects a retired %s binding without opening a socket",
async (binding) => {
const callGateway = vi.fn<() => void>();
setSubagentSpawnDepsForTest({
callGateway: async <T>() => {
callGateway();
return { runId: "wrong-gateway", status: "accepted" } as T;
},
});
const resolver = () => undefined;
const launch = () =>
callNativeSubagentGateway(
{
method: "agent",
params: { message: "must not launch", idempotencyKey: "closed-owner" },
},
undefined,
binding === "captured" ? resolver : undefined,
);
const result =
binding === "caller"
? withGatewayToolCallerIdentity(
{ agentId: "main", sessionKey: "agent:main:main", gatewayContextResolver: resolver },
launch,
)
: binding === "scoped"
? withPluginRuntimeGatewayRequestScope(
{ resolveGatewayContext: resolver, isWebchatConnect: () => false },
launch,
)
: launch();
await expect(result).rejects.toThrow("instance binding");
expect(callGateway).not.toHaveBeenCalled();
},
);
it("keeps socket dispatch available when no Gateway owner was bound", async () => {
const callGateway = vi.fn<() => void>();
setSubagentSpawnDepsForTest({
callGateway: async <T>() => {
callGateway();
return { runId: "remote-run", status: "accepted" } as T;
},
});
await expect(
callNativeSubagentGateway({
method: "agent",
params: { message: "standalone", idempotencyKey: "remote-run" },
}),
).resolves.toEqual({
response: { runId: "remote-run", status: "accepted" },
taskRowOwnership: "gateway_best_effort",
});
expect(callGateway).toHaveBeenCalledOnce();
});
});
|