@@ -17,6 +17,7 @@ import {
1717 mergeDeliveryContext ,
1818 normalizeDeliveryContext ,
1919} from "../utils/delivery-context.js" ;
20+ import { isDeliverableMessageChannel } from "../utils/message-channel.js" ;
2021import {
2122 buildAnnounceIdFromChildRun ,
2223 buildAnnounceIdempotencyKey ,
@@ -44,6 +45,19 @@ type SubagentAnnounceDeliveryResult = {
4445 error ?: string ;
4546} ;
4647
48+ function buildCompletionDeliveryMessage ( params : {
49+ findings : string ;
50+ subagentName : string ;
51+ } ) : string {
52+ const findingsText = params . findings . trim ( ) ;
53+ const hasFindings = findingsText . length > 0 && findingsText !== "(no output)" ;
54+ const header = `✅ Subagent ${ params . subagentName } finished` ;
55+ if ( ! hasFindings ) {
56+ return header ;
57+ }
58+ return `${ header } \n\n${ findingsText } ` ;
59+ }
60+
4761function summarizeDeliveryError ( error : unknown ) : string {
4862 if ( error instanceof Error ) {
4963 return error . message || "error" ;
@@ -256,10 +270,23 @@ function resolveAnnounceOrigin(
256270 entry ?: DeliveryContextSource ,
257271 requesterOrigin ?: DeliveryContext ,
258272) : DeliveryContext | undefined {
273+ const normalizedRequester = normalizeDeliveryContext ( requesterOrigin ) ;
274+ const normalizedEntry = deliveryContextFromSession ( entry ) ;
275+ if ( normalizedRequester ?. channel && ! isDeliverableMessageChannel ( normalizedRequester . channel ) ) {
276+ // Ignore internal/non-deliverable channel hints (for example webchat)
277+ // so a valid persisted route can still be used for outbound delivery.
278+ return mergeDeliveryContext (
279+ {
280+ accountId : normalizedRequester . accountId ,
281+ threadId : normalizedRequester . threadId ,
282+ } ,
283+ normalizedEntry ,
284+ ) ;
285+ }
259286 // requesterOrigin (captured at spawn time) reflects the channel the user is
260287 // actually on and must take priority over the session entry, which may carry
261288 // stale lastChannel / lastTo values from a previous channel interaction.
262- return mergeDeliveryContext ( requesterOrigin , deliveryContextFromSession ( entry ) ) ;
289+ return mergeDeliveryContext ( normalizedRequester , normalizedEntry ) ;
263290}
264291
265292async function sendAnnounce ( item : AnnounceQueueItem ) {
@@ -411,24 +438,29 @@ async function sendSubagentAnnounceDirectly(params: {
411438 directOrigin ?: DeliveryContext ;
412439 requesterIsSubagent : boolean ;
413440} ) : Promise < SubagentAnnounceDeliveryResult > {
441+ const cfg = loadConfig ( ) ;
442+ const canonicalRequesterSessionKey = resolveRequesterStoreKey (
443+ cfg ,
444+ params . targetRequesterSessionKey ,
445+ ) ;
414446 try {
415447 const completionDirectOrigin = normalizeDeliveryContext ( params . completionDirectOrigin ) ;
416- const completionChannel =
448+ const completionChannelRaw =
417449 typeof completionDirectOrigin ?. channel === "string"
418450 ? completionDirectOrigin . channel . trim ( )
419451 : "" ;
452+ const completionChannel =
453+ completionChannelRaw && isDeliverableMessageChannel ( completionChannelRaw )
454+ ? completionChannelRaw
455+ : "" ;
420456 const completionTo =
421457 typeof completionDirectOrigin ?. to === "string" ? completionDirectOrigin . to . trim ( ) : "" ;
422- const completionHasThreadHint =
423- completionDirectOrigin ?. threadId != null &&
424- String ( completionDirectOrigin . threadId ) . trim ( ) !== "" ;
425458 const hasCompletionDirectTarget =
426459 ! params . requesterIsSubagent && Boolean ( completionChannel ) && Boolean ( completionTo ) ;
427460
428461 if (
429462 params . expectsCompletionMessage &&
430463 hasCompletionDirectTarget &&
431- ! completionHasThreadHint &&
432464 params . completionMessage ?. trim ( )
433465 ) {
434466 await callGateway ( {
@@ -437,7 +469,7 @@ async function sendSubagentAnnounceDirectly(params: {
437469 channel : completionChannel ,
438470 to : completionTo ,
439471 accountId : completionDirectOrigin ?. accountId ,
440- sessionKey : params . targetRequesterSessionKey ,
472+ sessionKey : canonicalRequesterSessionKey ,
441473 message : params . completionMessage ,
442474 idempotencyKey : params . directIdempotencyKey ,
443475 } ,
@@ -455,11 +487,10 @@ async function sendSubagentAnnounceDirectly(params: {
455487 directOrigin ?. threadId != null && directOrigin . threadId !== ""
456488 ? String ( directOrigin . threadId )
457489 : undefined ;
458-
459490 await callGateway ( {
460491 method : "agent" ,
461492 params : {
462- sessionKey : params . targetRequesterSessionKey ,
493+ sessionKey : canonicalRequesterSessionKey ,
463494 message : params . triggerMessage ,
464495 deliver : ! params . requesterIsSubagent ,
465496 channel : params . requesterIsSubagent ? undefined : directOrigin ?. channel ,
@@ -521,11 +552,11 @@ async function deliverSubagentAnnouncement(params: {
521552 targetRequesterSessionKey : params . targetRequesterSessionKey ,
522553 triggerMessage : params . triggerMessage ,
523554 completionMessage : params . completionMessage ,
524- expectsCompletionMessage : params . expectsCompletionMessage ,
525555 directIdempotencyKey : params . directIdempotencyKey ,
526556 completionDirectOrigin : params . completionDirectOrigin ,
527557 directOrigin : params . directOrigin ,
528558 requesterIsSubagent : params . requesterIsSubagent ,
559+ expectsCompletionMessage : params . expectsCompletionMessage ,
529560 } ) ;
530561 if ( direct . delivered || ! params . expectsCompletionMessage ) {
531562 return direct ;
@@ -806,6 +837,7 @@ export async function runSubagentAnnounceFlow(params: {
806837 // Build instructional message for main agent
807838 const announceType = params . announceType ?? "subagent task" ;
808839 const taskLabel = params . label || params . task || "task" ;
840+ const subagentName = resolveAgentIdFromSessionKey ( params . childSessionKey ) ;
809841 const announceSessionId = childSessionId || "unknown" ;
810842 const findings = reply || "(no output)" ;
811843 let completionMessage = "" ;
@@ -872,15 +904,19 @@ export async function runSubagentAnnounceFlow(params: {
872904 startedAt : params . startedAt ,
873905 endedAt : params . endedAt ,
874906 } ) ;
875- completionMessage = [
907+ completionMessage = buildCompletionDeliveryMessage ( {
908+ findings,
909+ subagentName,
910+ } ) ;
911+ const internalSummaryMessage = [
876912 `[System Message] [sessionId: ${ announceSessionId } ] A ${ announceType } "${ taskLabel } " just ${ statusLabel } .` ,
877913 "" ,
878914 "Result:" ,
879915 findings ,
880916 "" ,
881917 statsLine ,
882918 ] . join ( "\n" ) ;
883- triggerMessage = [ completionMessage , "" , replyInstruction ] . join ( "\n" ) ;
919+ triggerMessage = [ internalSummaryMessage , "" , replyInstruction ] . join ( "\n" ) ;
884920
885921 const announceId = buildAnnounceIdFromChildRun ( {
886922 childSessionKey : params . childSessionKey ,
0 commit comments