What
In the Telegram/iMessage/Siri-style SDK modules, a timeout AbortController is
created but never handed to query(), so it can't actually cancel a running
turn.
Where
LifeOS/install/LIFEOS/PULSE/modules/imessage.ts (~line 209)
LifeOS/install/LIFEOS/PULSE/modules/siri.ts (~line 141)
Both do:
const conversation = query({ prompt, options: sdkOptions as any })
let fullText = ""
const timeoutController = new AbortController()
const timeout = setTimeout(() => timeoutController.abort(), sdkTimeoutMs)
try {
for await (const message of conversation) {
if (timeoutController.signal.aborted) break
...
Why it matters
timeoutController is never added to sdkOptions, so query() never receives
it. abort() only sets a flag that the for await loop checks between
yielded messages. A turn that hangs mid-tool (a stalled fetch, an unreachable
host) yields no further messages, so the loop parks at await, the
signal.aborted check never runs, and nothing cancels the query until the SDK
itself gives up. Observed downstream: an ~11-minute turn that produced no
output and then shipped only a canned fallback string.
Fix
Pass the controller through options.abortController (the SDK's supported
cancellation path):
const timeoutController = new AbortController()
const timeout = setTimeout(() => timeoutController.abort(), sdkTimeoutMs)
sdkOptions.abortController = timeoutController // <-- add
const conversation = query({ prompt, options: sdkOptions as any })
Keep the existing if (timeoutController.signal.aborted) break as a backstop.
Optionally wrap the loop so an abort flushes whatever text streamed so far
instead of propagating as an error.
Found while working on a module derived from imessage.ts.
What
In the Telegram/iMessage/Siri-style SDK modules, a timeout
AbortControlleriscreated but never handed to
query(), so it can't actually cancel a runningturn.
Where
LifeOS/install/LIFEOS/PULSE/modules/imessage.ts(~line 209)LifeOS/install/LIFEOS/PULSE/modules/siri.ts(~line 141)Both do: