@@ -1317,7 +1317,7 @@ describe("createCodexDynamicToolBridge", () => {
13171317 ] ) ;
13181318 } ) ;
13191319
1320- it ( "marks delivered message-tool-only source replies as terminal when final is omitted" , async ( ) => {
1320+ it ( "does not mark delivered message-tool-only source replies as terminal when final is omitted" , async ( ) => {
13211321 const bridge = createBridgeWithToolResult (
13221322 "message" ,
13231323 textToolResult ( "Sent." , { messageId : "imessage-6264" } ) ,
@@ -1330,15 +1330,15 @@ describe("createCodexDynamicToolBridge", () => {
13301330 } ) ;
13311331
13321332 expect ( result ) . toEqual ( expectInputText ( "Sent." ) ) ;
1333- expect ( result . terminate ) . toBe ( true ) ;
1333+ expect ( result . terminate ) . toBeUndefined ( ) ;
13341334 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
13351335 expect ( bridge . telemetry . messagingToolSentTargets . at ( - 1 ) ) . toMatchObject ( {
13361336 sourceReplyFinal : true ,
13371337 } ) ;
13381338 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
13391339 } ) ;
13401340
1341- it ( "requires explicit final=false to keep a delivered message-tool-only source reply non- terminal" , async ( ) => {
1341+ it ( "requires explicit final=true to make a delivered message-tool-only source reply terminal" , async ( ) => {
13421342 const bridge = createBridgeWithToolResult (
13431343 "message" ,
13441344 textToolResult ( "Sent." , { messageId : "imessage-6264" } ) ,
@@ -1373,7 +1373,7 @@ describe("createCodexDynamicToolBridge", () => {
13731373 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
13741374 } ) ;
13751375
1376- it ( "keeps message-tool-only source replies terminal when middleware redacts receipt details" , async ( ) => {
1376+ it ( "does not mark delivered message-tool-only source replies as terminal when middleware redacts receipt details" , async ( ) => {
13771377 const registry = createEmptyPluginRegistry ( ) ;
13781378 registry . agentToolResultMiddlewares . push ( {
13791379 pluginId : "receipt-redactor" ,
@@ -1406,7 +1406,8 @@ describe("createCodexDynamicToolBridge", () => {
14061406 } ) ;
14071407
14081408 expect ( result ) . toEqual ( expectInputText ( "Sent." ) ) ;
1409- expect ( result . terminate ) . toBe ( true ) ;
1409+ expect ( result . terminate ) . toBeUndefined ( ) ;
1410+ expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
14101411 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
14111412 } ) ;
14121413
@@ -1435,7 +1436,7 @@ describe("createCodexDynamicToolBridge", () => {
14351436 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( false ) ;
14361437 } ) ;
14371438
1438- it ( "keeps message-tool-only source replies terminal for explicit current source routes" , async ( ) => {
1439+ it ( "does not mark delivered explicit current source routes as terminal without final=true " , async ( ) => {
14391440 const bridge = createBridgeWithToolResult (
14401441 "message" ,
14411442 textToolResult ( "Sent." , { ok : true , messageId : "imessage-853" } ) ,
@@ -1457,15 +1458,15 @@ describe("createCodexDynamicToolBridge", () => {
14571458 } ) ;
14581459
14591460 expect ( result ) . toEqual ( expectInputText ( "Sent." ) ) ;
1460- expect ( result . terminate ) . toBe ( true ) ;
1461+ expect ( result . terminate ) . toBeUndefined ( ) ;
14611462 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
14621463 expect ( bridge . telemetry . messagingToolSentTargets . at ( - 1 ) ) . toMatchObject ( {
14631464 sourceReplyFinal : true ,
14641465 } ) ;
14651466 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
14661467 } ) ;
14671468
1468- it ( "keeps normalized explicit source routes terminal" , async ( ) => {
1469+ it ( "does not mark normalized explicit source routes as terminal without final=true " , async ( ) => {
14691470 setActivePluginRegistry (
14701471 createTestRegistry ( [
14711472 {
@@ -1511,12 +1512,12 @@ describe("createCodexDynamicToolBridge", () => {
15111512 text : "visible reply" ,
15121513 } ) ,
15131514 ] ) ;
1514- expect ( result . terminate ) . toBe ( true ) ;
1515+ expect ( result . terminate ) . toBeUndefined ( ) ;
15151516 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
15161517 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
15171518 } ) ;
15181519
1519- it ( "keeps message-tool-only source replies terminal when the reply receipt matches the current message id" , async ( ) => {
1520+ it ( "does not mark delivered message-tool-only source replies as terminal when the reply receipt matches the current message id" , async ( ) => {
15201521 const bridge = createBridgeWithToolResult (
15211522 "message" ,
15221523 textToolResult ( "Sent." , {
@@ -1550,12 +1551,12 @@ describe("createCodexDynamicToolBridge", () => {
15501551 text : "visible reply" ,
15511552 } ) ,
15521553 ] ) ;
1553- expect ( result . terminate ) . toBe ( true ) ;
1554+ expect ( result . terminate ) . toBeUndefined ( ) ;
15541555 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
15551556 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
15561557 } ) ;
15571558
1558- it ( "keeps message-tool-only source replies terminal when a text receipt matches the current message id" , async ( ) => {
1559+ it ( "does not mark delivered message-tool-only source replies as terminal when a text receipt matches the current message id" , async ( ) => {
15591560 const receiptText = JSON . stringify ( {
15601561 ok : true ,
15611562 messageId : "provider-message-1" ,
@@ -1578,7 +1579,7 @@ describe("createCodexDynamicToolBridge", () => {
15781579 } ) ;
15791580
15801581 expect ( result ) . toEqual ( expectInputText ( receiptText ) ) ;
1581- expect ( result . terminate ) . toBe ( true ) ;
1582+ expect ( result . terminate ) . toBeUndefined ( ) ;
15821583 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
15831584 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
15841585 } ) ;
@@ -1641,7 +1642,7 @@ describe("createCodexDynamicToolBridge", () => {
16411642 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( false ) ;
16421643 } ) ;
16431644
1644- it ( "keeps message-tool-only source replies terminal for explicit native target segments" , async ( ) => {
1645+ it ( "does not mark delivered explicit native target segments as terminal without final=true " , async ( ) => {
16451646 const bridge = createBridgeWithToolResult ( "message" , textToolResult ( "Sent." , { ok : true } ) , {
16461647 sourceReplyDeliveryMode : "message_tool_only" ,
16471648 currentChannelProvider : "imessage" ,
@@ -1658,12 +1659,12 @@ describe("createCodexDynamicToolBridge", () => {
16581659 } ) ;
16591660
16601661 expect ( result ) . toEqual ( expectInputText ( "Sent." ) ) ;
1661- expect ( result . terminate ) . toBe ( true ) ;
1662+ expect ( result . terminate ) . toBeUndefined ( ) ;
16621663 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
16631664 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
16641665 } ) ;
16651666
1666- it ( "keeps message-tool-only source replies terminal when the provider is only in the current channel id" , async ( ) => {
1667+ it ( "does not mark delivered source replies as terminal when the provider is only in the current channel id" , async ( ) => {
16671668 const bridge = createBridgeWithToolResult ( "message" , textToolResult ( "Sent." , { ok : true } ) , {
16681669 sourceReplyDeliveryMode : "message_tool_only" ,
16691670 currentChannelId : "imessage:any;-;+12069106512" ,
@@ -1679,7 +1680,7 @@ describe("createCodexDynamicToolBridge", () => {
16791680 } ) ;
16801681
16811682 expect ( result ) . toEqual ( expectInputText ( "Sent." ) ) ;
1682- expect ( result . terminate ) . toBe ( true ) ;
1683+ expect ( result . terminate ) . toBeUndefined ( ) ;
16831684 expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
16841685 expect ( Object . keys ( result ) ) . not . toContain ( "terminate" ) ;
16851686 } ) ;
@@ -1779,12 +1780,45 @@ describe("createCodexDynamicToolBridge", () => {
17791780 arguments : { action : "inspect" } ,
17801781 } ) ;
17811782
1782- expect ( firstResult . terminate ) . toBe ( true ) ;
1783- expect ( bridge . telemetry . didSendViaMessagingTool ) . toBe ( true ) ;
1783+ expect ( firstResult . terminate ) . toBeUndefined ( ) ;
1784+ expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
17841785 expect ( secondResult ) . toEqual ( expectInputText ( "No message sent." ) ) ;
17851786 expect ( secondResult . terminate ) . toBeUndefined ( ) ;
17861787 } ) ;
17871788
1789+ it ( "continues after an intermediate message-tool-only source reply without final" , async ( ) => {
1790+ const execute = vi
1791+ . fn ( )
1792+ . mockResolvedValueOnce (
1793+ textToolResult ( "One moment." , { messageId : "source-reply-intermediate" } ) ,
1794+ )
1795+ . mockResolvedValueOnce ( textToolResult ( "Command output." , { exitCode : 0 } ) ) ;
1796+ const bridge = createCodexDynamicToolBridge ( {
1797+ tools : [ createTool ( { name : "message" , execute } ) , createTool ( { name : "exec" , execute } ) ] ,
1798+ signal : new AbortController ( ) . signal ,
1799+ hookContext : { sourceReplyDeliveryMode : "message_tool_only" } ,
1800+ } ) ;
1801+
1802+ const firstResult = await handleMessageToolCall ( bridge , {
1803+ action : "send" ,
1804+ message : "One moment." ,
1805+ } ) ;
1806+ expect ( firstResult ) . toEqual ( expectInputText ( "One moment." ) ) ;
1807+ expect ( firstResult . terminate ) . toBeUndefined ( ) ;
1808+ expect ( bridge . telemetry . didDeliverSourceReplyViaMessageTool ) . toBe ( true ) ;
1809+
1810+ const secondResult = await bridge . handleToolCall ( {
1811+ threadId : "thread-1" ,
1812+ turnId : "turn-1" ,
1813+ callId : "call-2" ,
1814+ namespace : null ,
1815+ tool : "exec" ,
1816+ arguments : { command : "ls" } ,
1817+ } ) ;
1818+ expect ( secondResult . success ) . toBe ( true ) ;
1819+ expect ( secondResult . terminate ) . toBeUndefined ( ) ;
1820+ } ) ;
1821+
17881822 it ( "does not mark explicit message-tool sends as terminal source replies" , async ( ) => {
17891823 const bridge = createBridgeWithToolResult (
17901824 "message" ,
0 commit comments