@@ -60,6 +60,25 @@ export async function resolveAgentHarnessBeforePromptBuildResult(params: {
6060 messages : params . messages ,
6161 } ;
6262
63+ // Match the embedded runner's lifecycle order: heartbeat contributions are
64+ // collected before prompt-build hooks so hook side effects stay deterministic.
65+ const heartbeatResult =
66+ hasHeartbeatContribution && hookRunner
67+ ? await hookRunner
68+ . runHeartbeatPromptContribution (
69+ {
70+ sessionKey : params . ctx . sessionKey ,
71+ agentId : params . ctx . agentId ,
72+ heartbeatName : "heartbeat" ,
73+ } ,
74+ hookCtx ,
75+ )
76+ . catch ( ( error : unknown ) => {
77+ log . warn ( `heartbeat_prompt_contribution hook failed: ${ String ( error ) } ` ) ;
78+ return undefined ;
79+ } )
80+ : undefined ;
81+
6382 // Support the newer before_prompt_build hook plus the deprecated
6483 // before_agent_start hook during the prompt-build migration window.
6584 const promptBuildResult = hookRunner ?. hasHooks ( "before_prompt_build" )
@@ -81,26 +100,6 @@ export async function resolveAgentHarnessBeforePromptBuildResult(params: {
81100 } )
82101 : undefined ;
83102
84- // heartbeat_prompt_contribution runs only on heartbeat turns. Mirrors the
85- // embedded runner (resolvePromptBuildHookResult): the contribution is merged
86- // ahead of the before_prompt_build / before_agent_start contributions.
87- const heartbeatResult =
88- hasHeartbeatContribution && hookRunner
89- ? await hookRunner
90- . runHeartbeatPromptContribution (
91- {
92- sessionKey : params . ctx . sessionKey ,
93- agentId : params . ctx . agentId ,
94- heartbeatName : "heartbeat" ,
95- } ,
96- hookCtx ,
97- )
98- . catch ( ( error : unknown ) => {
99- log . warn ( `heartbeat_prompt_contribution hook failed: ${ String ( error ) } ` ) ;
100- return undefined ;
101- } )
102- : undefined ;
103-
104103 const systemPrompt = resolvePromptBuildSystemPrompt ( {
105104 developerInstructions : params . developerInstructions ,
106105 promptBuildResult,
0 commit comments