





















@@ -18,6 +18,13 @@ const hoisted = vi.hoisted(() => ({
1818let spawnSubagentDirect: typeof import("./subagent-spawn.js").spawnSubagentDirect;
1919let persistedStore: Record<string, Record<string, unknown>> | undefined;
202021+type SpawnResult = Awaited<ReturnType<typeof spawnSubagentDirect>>;
22+type AcceptedSpawnResult = SpawnResult & {
23+childSessionKey: string;
24+runId: string;
25+status: "accepted";
26+};
27+2128function createDepthLimitConfig(subagents?: Record<string, unknown>) {
2229return createSubagentSpawnTestConfig("/tmp/workspace-main", {
2330agents: {
@@ -45,6 +52,24 @@ async function spawnFrom(sessionKey: string, params?: Record<string, unknown>) {
4552);
4653}
475455+function expectForbidden(result: SpawnResult, error: string) {
56+expect(result.status).toBe("forbidden");
57+if (result.status !== "forbidden") {
58+throw new Error(`Expected forbidden spawn result, received ${result.status}`);
59+}
60+expect(result.error).toBe(error);
61+}
62+63+function expectAccepted(result: SpawnResult, runId: string): AcceptedSpawnResult {
64+expect(result.status).toBe("accepted");
65+if (result.status !== "accepted") {
66+throw new Error(`Expected accepted spawn result, received ${result.status}`);
67+}
68+expect(result.runId).toBe(runId);
69+expect(typeof result.childSessionKey).toBe("string");
70+return result as AcceptedSpawnResult;
71+}
72+4873describe("subagent spawn depth + child limits", () => {
4974beforeAll(async () => {
5075({ spawnSubagentDirect } = await loadSubagentSpawnModuleForTest({
@@ -80,10 +105,10 @@ describe("subagent spawn depth + child limits", () => {
8010581106const result = await spawnFrom("agent:main:subagent:parent");
8210783-expect(result).toMatchObject({
84-status: "forbidden",
85-error: "sessions_spawn is not allowed at this depth (current depth: 1, max: 1)",
86-});
108+expectForbidden(
109+result,
110+"sessions_spawn is not allowed at this depth (current depth: 1, max: 1)",
111+);
87112});
8811389114it("allows depth-1 callers when maxSpawnDepth is 2 and patches child capabilities", async () => {
@@ -92,19 +117,18 @@ describe("subagent spawn depth + child limits", () => {
9211793118const result = await spawnFrom("agent:main:subagent:parent");
9411995-expect(result).toMatchObject({
96-status: "accepted",
97-childSessionKey: expect.stringMatching(/^agent:main:subagent:/),
98-runId: "run-1",
99-});
100-101-const childSession = persistedStore?.[result.childSessionKey as string];
102-expect(childSession).toMatchObject({
103-spawnedBy: "agent:main:subagent:parent",
104-spawnDepth: 2,
105-subagentRole: "leaf",
106-subagentControlScope: "none",
107-});
120+const accepted = expectAccepted(result, "run-1");
121+expect(accepted.childSessionKey).toMatch(/^agent:main:subagent:/);
122+123+const childSession = persistedStore?.[accepted.childSessionKey];
124+expect(childSession).toBeDefined();
125+if (!childSession) {
126+throw new Error("Expected persisted child session");
127+}
128+expect(childSession.spawnedBy).toBe("agent:main:subagent:parent");
129+expect(childSession.spawnDepth).toBe(2);
130+expect(childSession.subagentRole).toBe("leaf");
131+expect(childSession.subagentControlScope).toBe("none");
108132expect(typeof childSession?.spawnedWorkspaceDir).toBe("string");
109133});
110134@@ -114,10 +138,10 @@ describe("subagent spawn depth + child limits", () => {
114138115139const result = await spawnFrom("agent:main:subagent:flat-depth-2");
116140117-expect(result).toMatchObject({
118-status: "forbidden",
119-error: "sessions_spawn is not allowed at this depth (current depth: 2, max: 2)",
120-});
141+expectForbidden(
142+result,
143+"sessions_spawn is not allowed at this depth (current depth: 2, max: 2)",
144+);
121145});
122146123147it("rejects when active children for requester session reached maxChildrenPerAgent", async () => {
@@ -130,10 +154,10 @@ describe("subagent spawn depth + child limits", () => {
130154131155const result = await spawnFrom("agent:main:subagent:parent");
132156133-expect(result).toMatchObject({
134-status: "forbidden",
135-error: "sessions_spawn has reached max active children for this session (1/1)",
136-});
157+expectForbidden(
158+result,
159+"sessions_spawn has reached max active children for this session (1/1)",
160+);
137161});
138162139163it("does not use subagent maxConcurrent as a per-parent spawn gate", async () => {
@@ -147,10 +171,7 @@ describe("subagent spawn depth + child limits", () => {
147171148172const result = await spawnFrom("agent:main:subagent:parent");
149173150-expect(result).toMatchObject({
151-status: "accepted",
152-runId: "run-1",
153-});
174+expectAccepted(result, "run-1");
154175});
155176156177it("fails spawn when the initial child session patch rejects the model", async () => {
@@ -167,9 +188,7 @@ describe("subagent spawn depth + child limits", () => {
167188168189const result = await spawnFrom("main", { model: "bad-model" });
169190170-expect(result).toMatchObject({
171-status: "error",
172-});
191+expect(result.status).toBe("error");
173192expect(result.error ?? "").toContain("invalid model");
174193expect(
175194hoisted.callGatewayMock.mock.calls.some(
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。