fix(tui): match reference discard listener on content, not the unassigned id
The reference-admission discard listener matched on followup()'s returned id, but an agent/inbox/enqueue listener that synchronously cancels emits agent/inbox/discard before followup() returns to assign that id. The match then missed, leaking both the submit and discard listeners plus the attached context per referenced prompt. Match on the content reference instead — the same value send() carries onto the message, known before followup() runs, and symmetric with the submit wrapper's content check. The prior "ordinary allowed path" test passed only because the fake agent returned a fixed 'stub' id that collided with the id the test constructed; it now releases each wrapper through its own allowed admission, and a new regression drives the synchronous-discard timing directly.
This commit is contained in:
@@ -2822,17 +2822,18 @@ export function createTuiChat(
|
||||
}, { prepend: true })
|
||||
// Installed BEFORE followup(): admission runs synchronously inside it on
|
||||
// the common path, and a listener registered after cleanup() already ran
|
||||
// would never be released. The id lands before any discard can name it —
|
||||
// discard is only ever emitted by a later cancel().
|
||||
let id: AgentMessageId | undefined
|
||||
// would never be released. Match on the `content` reference, not the
|
||||
// returned id: an enqueue listener that synchronously cancels emits
|
||||
// discard before followup() returns to assign the id, and content is the
|
||||
// same reference send() carries onto the message (mirrors detachSubmit).
|
||||
const detachDiscard = ctx.on('agent/inbox/discard', (subject, messages) => {
|
||||
if (subject === agent && messages.some(message => message.id === id)) cleanup()
|
||||
if (subject === agent && messages.some(message => message.content === content)) cleanup()
|
||||
})
|
||||
// followup() accepts any typed input and contains listener failures;
|
||||
// this guards a future synchronous throw so the wrapper cannot leak.
|
||||
/* v8 ignore start -- future-proofing guard, see above */
|
||||
try {
|
||||
id = agent.followup({ content, source: { kind: 'user' } })
|
||||
agent.followup({ content, source: { kind: 'user' } })
|
||||
} catch (error: unknown) {
|
||||
cleanup()
|
||||
throw error
|
||||
|
||||
Reference in New Issue
Block a user