
























@@ -78,6 +78,38 @@ function countMatching<T>(items: readonly T[], predicate: (item: T) => boolean)
7878return count;
7979}
808081+function requireRecord(value: unknown, label: string): Record<string, unknown> {
82+if (!value || typeof value !== "object" || Array.isArray(value)) {
83+throw new Error(`expected ${label}`);
84+}
85+return value as Record<string, unknown>;
86+}
87+88+function requireSubagentEndedHookCall(runId: string): {
89+event: Record<string, unknown>;
90+ctx: Record<string, unknown>;
91+} {
92+const call = runSubagentEndedHookMock.mock.calls.find((candidate) => {
93+const ctx = candidate[1] as { runId?: string } | undefined;
94+return ctx?.runId === runId;
95+});
96+if (!call) {
97+throw new Error(`expected subagent_ended hook call for ${runId}`);
98+}
99+return {
100+event: requireRecord(call[0], `${runId} subagent_ended event`),
101+ctx: requireRecord(call[1], `${runId} subagent_ended context`),
102+};
103+}
104+105+function requireSessionLifecycleEventCall(label: string): Record<string, unknown> {
106+const call = emitSessionLifecycleEventMock.mock.calls[0];
107+if (!call) {
108+throw new Error(`expected ${label}`);
109+}
110+return requireRecord(call[0], label);
111+}
112+81113const noopContextEngine = {
82114info: { id: "test-context-engine", name: "Test context engine" },
83115ingest: async () => ({ ingested: false }),
@@ -296,14 +328,9 @@ describe("subagent registry steer restarts", () => {
296328});
297329expect(matchingCalls).toHaveLength(1);
298330});
299-expect(runSubagentEndedHookMock).toHaveBeenCalledWith(
300-expect.objectContaining({
301-runId: "run-new",
302-}),
303-expect.objectContaining({
304-runId: "run-new",
305-}),
306-);
331+const hookCall = requireSubagentEndedHookCall("run-new");
332+expect(hookCall.event.runId).toBe("run-new");
333+expect(hookCall.ctx.runId).toBe("run-new");
307334308335const announce = (announceSpy.mock.calls[0]?.[0] ?? {}) as { childRunId?: string };
309336expect(announce.childRunId).toBe("run-new");
@@ -330,17 +357,12 @@ describe("subagent registry steer restarts", () => {
330357await waitForRegistrySideEffect(() => {
331358expect(runSubagentEndedHookMock).toHaveBeenCalledTimes(1);
332359});
333-expect(runSubagentEndedHookMock).toHaveBeenCalledWith(
334-expect.objectContaining({
335-targetSessionKey: "agent:main:subagent:completion-delayed",
336-reason: "subagent-complete",
337-sendFarewell: true,
338-}),
339-expect.objectContaining({
340-runId: "run-completion-delayed",
341-requesterSessionKey: MAIN_REQUESTER_SESSION_KEY,
342-}),
343-);
360+const hookCall = requireSubagentEndedHookCall("run-completion-delayed");
361+expect(hookCall.event.targetSessionKey).toBe("agent:main:subagent:completion-delayed");
362+expect(hookCall.event.reason).toBe("subagent-complete");
363+expect(hookCall.event.sendFarewell).toBe(true);
364+expect(hookCall.ctx.runId).toBe("run-completion-delayed");
365+expect(hookCall.ctx.requesterSessionKey).toBe(MAIN_REQUESTER_SESSION_KEY);
344366}
345367});
346368@@ -423,21 +445,13 @@ describe("subagent registry steer restarts", () => {
423445emitLifecycleEnd("run-terminal-state-new");
424446425447await waitForRegistrySideEffect(() => {
426-expect(runSubagentEndedHookMock).toHaveBeenCalledWith(
427-expect.objectContaining({
428-runId: "run-terminal-state-new",
429-}),
430-expect.objectContaining({
431-runId: "run-terminal-state-new",
432-}),
433-);
448+const hookCall = requireSubagentEndedHookCall("run-terminal-state-new");
449+expect(hookCall.event.runId).toBe("run-terminal-state-new");
450+expect(hookCall.ctx.runId).toBe("run-terminal-state-new");
434451});
435-expect(emitSessionLifecycleEventMock).toHaveBeenCalledWith(
436-expect.objectContaining({
437-sessionKey: "agent:main:subagent:terminal-state",
438-reason: "subagent-status",
439-}),
440-);
452+const lifecycleEvent = requireSessionLifecycleEventCall("terminal-state lifecycle event");
453+expect(lifecycleEvent.sessionKey).toBe("agent:main:subagent:terminal-state");
454+expect(lifecycleEvent.reason).toBe("subagent-status");
441455}
442456});
443457@@ -532,11 +546,12 @@ describe("subagent registry steer restarts", () => {
532546expect(replaced).toBe(true);
533547534548const run = listMainRuns().find((entry) => entry.runId === "run-wake-new");
535-expect(run).toMatchObject({
536-frozenResultText: undefined,
537-fallbackFrozenResultText: "final summary before wake",
538-fallbackFrozenResultCapturedAt: 1234,
539-});
549+if (!run) {
550+throw new Error("expected wake replacement run");
551+}
552+expect(run.frozenResultText).toBeUndefined();
553+expect(run.fallbackFrozenResultText).toBe("final summary before wake");
554+expect(run.fallbackFrozenResultCapturedAt).toBe(1234);
540555});
541556542557it("restores announce for a finished run when steer replacement dispatch fails", async () => {
@@ -579,7 +594,8 @@ describe("subagent registry steer restarts", () => {
579594expect(mod.isSubagentSessionRunActive(childSessionKey)).toBe(false);
580595581596const run = listMainRuns()[0];
582-expect(run?.outcome).toMatchObject({ status: "error", error: "manual kill" });
597+expect(run?.outcome?.status).toBe("error");
598+expect(run?.outcome?.error).toBe("manual kill");
583599expect(run?.outcome?.startedAt).toBeTypeOf("number");
584600expect(run?.outcome?.endedAt).toBeTypeOf("number");
585601expect(run?.outcome?.elapsedMs).toBeTypeOf("number");
@@ -588,24 +604,19 @@ describe("subagent registry steer restarts", () => {
588604expect(run?.cleanupHandled).toBe(true);
589605expect(typeof run?.cleanupCompletedAt).toBe("number");
590606await flushAnnounce();
591-expect(runSubagentEndedHookMock).toHaveBeenCalledWith(
592-{
593-targetSessionKey: childSessionKey,
594-targetKind: "subagent",
595-reason: "subagent-killed",
596-sendFarewell: true,
597-accountId: undefined,
598-runId: "run-killed",
599-endedAt: expect.any(Number),
600-outcome: "killed",
601-error: "manual kill",
602-},
603-{
604-runId: "run-killed",
605- childSessionKey,
606-requesterSessionKey: MAIN_REQUESTER_SESSION_KEY,
607-},
608-);
607+const hookCall = requireSubagentEndedHookCall("run-killed");
608+expect(hookCall.event.targetSessionKey).toBe(childSessionKey);
609+expect(hookCall.event.targetKind).toBe("subagent");
610+expect(hookCall.event.reason).toBe("subagent-killed");
611+expect(hookCall.event.sendFarewell).toBe(true);
612+expect(hookCall.event.accountId).toBeUndefined();
613+expect(hookCall.event.runId).toBe("run-killed");
614+expect(typeof hookCall.event.endedAt).toBe("number");
615+expect(hookCall.event.outcome).toBe("killed");
616+expect(hookCall.event.error).toBe("manual kill");
617+expect(hookCall.ctx.runId).toBe("run-killed");
618+expect(hookCall.ctx.childSessionKey).toBe(childSessionKey);
619+expect(hookCall.ctx.requesterSessionKey).toBe(MAIN_REQUESTER_SESSION_KEY);
609620});
610621611622it("treats a child session as inactive when only a stale older row is still unended", () => {
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。