























@@ -38,6 +38,22 @@ function createConfigOverride(overrides?: Record<string, unknown>) {
3838});
3939}
404041+function requireRecord(value: unknown): Record<string, unknown> {
42+expect(value).toBeTruthy();
43+expect(typeof value).toBe("object");
44+expect(Array.isArray(value)).toBe(false);
45+return value as Record<string, unknown>;
46+}
47+48+function gatewayRequestRecords(): Record<string, unknown>[] {
49+return hoisted.callGatewayMock.mock.calls.map((call) => requireRecord(call[0]));
50+}
51+52+function gatewayRequest(method: string): Record<string, unknown> {
53+const request = gatewayRequestRecords().find((entry) => entry.method === method);
54+return requireRecord(request);
55+}
56+4157describe("spawnSubagentDirect seam flow", () => {
4258beforeAll(async () => {
4359({ resetSubagentRegistryForTests, spawnSubagentDirect } = await loadSubagentSpawnModuleForTest({
@@ -114,13 +130,9 @@ describe("spawnSubagentDirect seam flow", () => {
114130},
115131);
116132117-expect(result).toMatchObject({
118-status: "forbidden",
119-error: "agentId is not allowed for sessions_spawn (allowed: planner)",
120-});
121-expect(hoisted.callGatewayMock).not.toHaveBeenCalledWith(
122-expect.objectContaining({ method: "agent" }),
123-);
133+expect(result.status).toBe("forbidden");
134+expect(result.error).toBe("agentId is not allowed for sessions_spawn (allowed: planner)");
135+expect(gatewayRequestRecords().some((request) => request.method === "agent")).toBe(false);
124136});
125137126138it("allows omitted agentId to default to requester even when allowAgents excludes requester", async () => {
@@ -154,10 +166,8 @@ describe("spawnSubagentDirect seam flow", () => {
154166},
155167);
156168157-expect(result).toMatchObject({
158-status: "accepted",
159-childSessionKey: expect.stringMatching(/^agent:task-manager:subagent:/),
160-});
169+expect(result.status).toBe("accepted");
170+expect(result.childSessionKey).toMatch(/^agent:task-manager:subagent:/);
161171});
162172163173it("accepts a spawned run across session patching, runtime-model persistence, registry registration, and lifecycle emission", async () => {
@@ -196,37 +206,31 @@ describe("spawnSubagentDirect seam flow", () => {
196206},
197207);
198208199-expect(result).toMatchObject({
200-status: "accepted",
201-runId: "run-1",
202-mode: "run",
203-modelApplied: true,
204-});
209+expect(result.status).toBe("accepted");
210+expect(result.runId).toBe("run-1");
211+expect(result.mode).toBe("run");
212+expect(result.modelApplied).toBe(true);
205213expect(result.childSessionKey).toMatch(/^agent:main:subagent:/);
206214207215const childSessionKey = result.childSessionKey as string;
208216expect(hoisted.pruneLegacyStoreKeysMock).toHaveBeenCalledTimes(3);
209217expect(hoisted.updateSessionStoreMock).toHaveBeenCalledTimes(3);
210-expect(hoisted.registerSubagentRunMock).toHaveBeenCalledWith(
211-expect.objectContaining({
212-runId: "run-1",
213- childSessionKey,
214-requesterSessionKey: "agent:main:main",
215-requesterDisplayKey: "agent:main:main",
216-requesterOrigin: {
217-channel: "discord",
218-accountId: "acct-1",
219-to: "user-1",
220-threadId: 42,
221-},
222-task: "inspect the spawn seam",
223-cleanup: "keep",
224-model: "openai-codex/gpt-5.4",
225-workspaceDir: "/tmp/requester-workspace",
226-expectsCompletionMessage: true,
227-spawnMode: "run",
228-}),
229-);
218+const registerInput = requireRecord(hoisted.registerSubagentRunMock.mock.calls[0]?.[0]);
219+const requesterOrigin = requireRecord(registerInput.requesterOrigin);
220+expect(registerInput.runId).toBe("run-1");
221+expect(registerInput.childSessionKey).toBe(childSessionKey);
222+expect(registerInput.requesterSessionKey).toBe("agent:main:main");
223+expect(registerInput.requesterDisplayKey).toBe("agent:main:main");
224+expect(requesterOrigin.channel).toBe("discord");
225+expect(requesterOrigin.accountId).toBe("acct-1");
226+expect(requesterOrigin.to).toBe("user-1");
227+expect(requesterOrigin.threadId).toBe(42);
228+expect(registerInput.task).toBe("inspect the spawn seam");
229+expect(registerInput.cleanup).toBe("keep");
230+expect(registerInput.model).toBe("openai-codex/gpt-5.4");
231+expect(registerInput.workspaceDir).toBe("/tmp/requester-workspace");
232+expect(registerInput.expectsCompletionMessage).toBe(true);
233+expect(registerInput.spawnMode).toBe("run");
230234expect(hoisted.emitSessionLifecycleEventMock).toHaveBeenCalledWith({
231235sessionKey: childSessionKey,
232236reason: "create",
@@ -245,15 +249,10 @@ describe("spawnSubagentDirect seam flow", () => {
245249expect(operations.indexOf("gateway:agent")).toBeGreaterThan(
246250operations.lastIndexOf("store:update"),
247251);
248-expect(hoisted.callGatewayMock).toHaveBeenCalledWith(
249-expect.objectContaining({
250-method: "agent",
251-params: expect.objectContaining({
252-sessionKey: childSessionKey,
253-cleanupBundleMcpOnRunEnd: true,
254-}),
255-}),
256-);
252+const agentRequest = gatewayRequest("agent");
253+const agentParams = requireRecord(agentRequest.params);
254+expect(agentParams.sessionKey).toBe(childSessionKey);
255+expect(agentParams.cleanupBundleMcpOnRunEnd).toBe(true);
257256});
258257259258it("omits requesterOrigin threadId when no requester thread is provided", async () => {
@@ -282,13 +281,12 @@ describe("spawnSubagentDirect seam flow", () => {
282281);
283282284283expect(result.status).toBe("accepted");
285-const registerInput = hoisted.registerSubagentRunMock.mock.calls[0]?.[0];
286-expect(registerInput?.requesterOrigin).toMatchObject({
287-channel: "discord",
288-accountId: "acct-1",
289-to: "user-1",
290-});
291-expect(registerInput?.requesterOrigin).not.toHaveProperty("threadId");
284+const registerInput = requireRecord(hoisted.registerSubagentRunMock.mock.calls[0]?.[0]);
285+const requesterOrigin = requireRecord(registerInput.requesterOrigin);
286+expect(requesterOrigin.channel).toBe("discord");
287+expect(requesterOrigin.accountId).toBe("acct-1");
288+expect(requesterOrigin.to).toBe("user-1");
289+expect(requesterOrigin).not.toHaveProperty("threadId");
292290});
293291294292it("pins admin-only methods to operator.admin and preserves least-privilege for others (#59428)", async () => {
@@ -364,13 +362,10 @@ describe("spawnSubagentDirect seam flow", () => {
364362},
365363);
366364367-expect(result).toMatchObject({
368-status: "accepted",
369-});
365+expect(result.status).toBe("accepted");
370366const agentCall = calls.find((call) => call.method === "agent");
371-expect(agentCall?.params).toMatchObject({
372-thinking: "high",
373-});
367+const params = requireRecord(agentCall?.params);
368+expect(params.thinking).toBe("high");
374369});
375370376371it("does not duplicate long subagent task text in the initial user message (#72019)", async () => {
@@ -434,10 +429,8 @@ describe("spawnSubagentDirect seam flow", () => {
434429},
435430);
436431437-expect(result).toMatchObject({
438-status: "error",
439-childSessionKey: expect.stringMatching(/^agent:main:subagent:/),
440-});
432+expect(result.status).toBe("error");
433+expect(result.childSessionKey).toMatch(/^agent:main:subagent:/);
441434expect(result.error ?? "").toContain("invalid model");
442435expect(
443436hoisted.callGatewayMock.mock.calls.some(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。