fix: initialize plugins before killed subagent hooks

This commit is contained in:
Tak Hoffman
2026-03-24 08:27:50 -05:00
parent b72d0c8459
commit e6e2407cee
2 changed files with 61 additions and 0 deletions

View File

@@ -346,4 +346,58 @@ describe("subagent registry seam flow", () => {
});
});
});
it("loads runtime plugins before emitting killed subagent ended hooks", async () => {
const endedHookRunner = {
hasHooks: (hookName: string) => hookName === "subagent_ended",
runSubagentEnded: mocks.runSubagentEnded,
};
mocks.getGlobalHookRunner.mockReturnValue(null);
mocks.ensureRuntimePluginsLoaded.mockImplementation(() => {
mocks.getGlobalHookRunner.mockReturnValue(endedHookRunner as never);
});
mod.registerSubagentRun({
runId: "run-killed-init",
childSessionKey: "agent:main:subagent:killed",
requesterSessionKey: "agent:main:main",
requesterDisplayKey: "main",
requesterOrigin: { channel: "discord", accountId: "acct-1" },
task: "kill after init",
cleanup: "keep",
workspaceDir: "/tmp/killed-workspace",
});
const updated = mod.markSubagentRunTerminated({
runId: "run-killed-init",
reason: "manual kill",
});
expect(updated).toBe(1);
await vi.waitFor(() => {
expect(mocks.ensureRuntimePluginsLoaded).toHaveBeenCalledWith({
config: {
agents: { defaults: { subagents: { archiveAfterMinutes: 0 } } },
session: { mainKey: "main", scope: "per-sender" },
},
workspaceDir: "/tmp/killed-workspace",
allowGatewaySubagentBinding: true,
});
});
expect(mocks.runSubagentEnded).toHaveBeenCalledWith(
expect.objectContaining({
targetSessionKey: "agent:main:subagent:killed",
reason: "subagent-killed",
accountId: "acct-1",
runId: "run-killed-init",
outcome: "killed",
error: "manual kill",
}),
expect.objectContaining({
runId: "run-killed-init",
childSessionKey: "agent:main:subagent:killed",
requesterSessionKey: "agent:main:main",
}),
);
});
});

View File

@@ -1654,10 +1654,17 @@ export function markSubagentRunTerminated(params: {
childSessionKey: entry.childSessionKey,
});
});
const cfg = loadConfig();
ensureRuntimePluginsLoaded({
config: cfg,
workspaceDir: entry.workspaceDir,
allowGatewaySubagentBinding: true,
});
void emitSubagentEndedHookOnce({
entry,
reason: SUBAGENT_ENDED_REASON_KILLED,
sendFarewell: true,
accountId: entry.requesterOrigin?.accountId,
outcome: SUBAGENT_ENDED_OUTCOME_KILLED,
error: reason,
inFlightRunIds: endedHookInFlightRunIds,