Agents: require explicit subagent delivery origins

This commit is contained in:
Gustavo Madeira Santana
2026-04-17 01:59:31 -04:00
parent a04b8c16d1
commit bc177bec6e
2 changed files with 17 additions and 36 deletions

View File

@@ -60,11 +60,6 @@ describe("spawnSubagentDirect thread binding delivery", () => {
registerSubagentRunMock: hoisted.registerSubagentRunMock,
emitSessionLifecycleEventMock: hoisted.emitSessionLifecycleEventMock,
hookRunner: hoisted.hookRunner,
getSessionBindingService: () => ({ listBySession: () => [] }),
resolveConversationDeliveryTarget: () => ({
to: "room:!room:example",
threadId: "$thread-root",
}),
resolveSubagentSpawnModelSelection: () => "openai-codex/gpt-5.4",
resolveSandboxRuntimeStatus: () => ({ sandboxed: false }),
});
@@ -108,7 +103,7 @@ describe("spawnSubagentDirect thread binding delivery", () => {
);
});
it("keeps completion announcements when no bound delivery origin is resolved", async () => {
it("keeps completion announcements when only a generic binding is available", async () => {
hoisted.hookRunner.hasHooks.mockImplementation(
(hookName?: string) => hookName === "subagent_spawning",
);
@@ -131,7 +126,21 @@ describe("spawnSubagentDirect thread binding delivery", () => {
registerSubagentRunMock: hoisted.registerSubagentRunMock,
emitSessionLifecycleEventMock: hoisted.emitSessionLifecycleEventMock,
hookRunner: hoisted.hookRunner,
getSessionBindingService: () => ({ listBySession: () => [] }),
getSessionBindingService: () => ({
listBySession: () => [
{
status: "active",
conversation: {
channel: "feishu",
accountId: "work",
conversationId: "oc_dm_chat_1",
},
},
],
}),
resolveConversationDeliveryTarget: () => ({
to: "channel:oc_dm_chat_1",
}),
resolveSubagentSpawnModelSelection: () => "openai-codex/gpt-5.4",
resolveSandboxRuntimeStatus: () => ({ sandboxed: false }),
});

View File

@@ -41,13 +41,11 @@ import {
emitSessionLifecycleEvent,
getGlobalHookRunner,
loadConfig,
getSessionBindingService,
mergeSessionEntry,
mergeDeliveryContext,
normalizeDeliveryContext,
pruneLegacyStoreKeys,
resolveAgentConfig,
resolveConversationDeliveryTarget,
resolveDisplaySessionKey,
resolveGatewaySessionStoreTarget,
resolveInternalSessionKey,
@@ -340,9 +338,7 @@ async function ensureThreadBindingForSubagentSpawn(params: {
"Unable to create or bind a thread for this subagent session. Session mode is unavailable for this target.",
};
}
const deliveryOrigin =
normalizeDeliveryContext(result.deliveryOrigin) ??
resolveThreadBindingDeliveryOrigin(params.childSessionKey);
const deliveryOrigin = normalizeDeliveryContext(result.deliveryOrigin);
return {
status: "ok",
...(deliveryOrigin ? { deliveryOrigin } : {}),
@@ -355,30 +351,6 @@ async function ensureThreadBindingForSubagentSpawn(params: {
}
}
function resolveThreadBindingDeliveryOrigin(childSessionKey: string): DeliveryContext | undefined {
const activeBindings = getSessionBindingService()
.listBySession(childSessionKey)
.filter((binding) => binding.status === "active");
if (activeBindings.length !== 1) {
return undefined;
}
const binding = activeBindings[0];
if (!binding) {
return undefined;
}
const target = resolveConversationDeliveryTarget({
channel: binding.conversation.channel,
conversationId: binding.conversation.conversationId,
parentConversationId: binding.conversation.parentConversationId,
});
return normalizeDeliveryContext({
channel: binding.conversation.channel,
accountId: binding.conversation.accountId,
to: target.to,
threadId: target.threadId,
});
}
function hasRoutableDeliveryOrigin(
origin?: DeliveryContext,
): origin is DeliveryContext & { channel: string; to: string } {