Skip to content

Commit 10eea22

Browse files
heartbeart while claude execution
1 parent 9638cca commit 10eea22

File tree

1 file changed

+12
-4
lines changed

1 file changed

+12
-4
lines changed

packages/agent/src/agent/ClaudeSDKAgent.ts

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -104,15 +104,17 @@ export class ClaudeSDKAgent extends BaseAgent {
104104
/**
105105
* Wrapper around iterator.next() that yields heartbeat events while waiting
106106
* @param iterator - The async iterator
107-
* @yields Heartbeat events (FormattedEvent) and the final iterator result (IteratorResult)
107+
* @yields Heartbeat events (FormattedEvent) while waiting, then the final iterator result (IteratorResult)
108108
*/
109109
private async *nextWithHeartbeat(iterator: AsyncIterator<any>): AsyncGenerator<any> {
110110
const heartbeatInterval = 20000 // 20 seconds
111111
let heartbeatTimer: NodeJS.Timeout | null = null
112112
let abortHandler: (() => void) | null = null
113-
let iteratorPromise = iterator.next()
114113

115-
// Create abort promise ONCE outside loop to avoid accumulating event listeners
114+
// Call iterator.next() once - this generator wraps a single next() call
115+
const iteratorPromise = iterator.next()
116+
117+
// Create abort promise
116118
const abortPromise = new Promise<never>((_, reject) => {
117119
if (this.abortController) {
118120
abortHandler = () => {
@@ -123,19 +125,22 @@ export class ClaudeSDKAgent extends BaseAgent {
123125
})
124126

125127
try {
128+
// Loop until the iterator promise resolves, yielding heartbeats while waiting
126129
while (true) {
127130
// Check if execution was aborted
128131
if (this.abortController?.signal.aborted) {
129132
logger.info('⚠️ Agent execution aborted during heartbeat wait')
130133
return
131134
}
132135

136+
// Create timeout promise for this iteration
133137
const timeoutPromise = new Promise(resolve => {
134138
heartbeatTimer = setTimeout(() => resolve({ type: 'heartbeat' }), heartbeatInterval)
135139
})
136140

137141
type RaceResult = { type: 'result'; result: any } | { type: 'heartbeat' }
138142
let race: RaceResult
143+
139144
try {
140145
race = await Promise.race([
141146
iteratorPromise.then(result => ({ type: 'result' as const, result })),
@@ -152,15 +157,18 @@ export class ClaudeSDKAgent extends BaseAgent {
152157
return
153158
}
154159

160+
// Clear the timeout if it was set
155161
if (heartbeatTimer) {
156162
clearTimeout(heartbeatTimer)
157163
heartbeatTimer = null
158164
}
159165

160166
if (race.type === 'heartbeat') {
167+
// Heartbeat timeout occurred - yield processing event and continue waiting
161168
yield EventFormatter.createProcessingEvent()
169+
// Loop continues - will race the same iteratorPromise (still pending) vs new timeout
162170
} else {
163-
// Yield the iterator result (not return!) so the consumer receives it
171+
// Iterator result arrived - yield it and exit this generator
164172
yield race.result
165173
return
166174
}

0 commit comments

Comments
 (0)