mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(tui): mark disposed when the agent leaves the registry under the TUI
agent/disposed only cleared the status line, so an agent-loop-only reload that disposed the agent while the TUI stayed mounted left the local disposed flag false. Since retained agents accept deliveries after detachment, later input drove a zombie agent/session. Set disposed on agent/disposed so dispatchMessage reports it. Adds a regression that sends after disposal and asserts no delivery.
This commit is contained in:
@@ -3217,6 +3217,11 @@ export function createTuiChat(
|
||||
})
|
||||
const disposeAgent = ctx.on('agent/disposed', (subject) => {
|
||||
if (subject !== agent) return
|
||||
// The agent left the registry (e.g. an agent-loop-only reload) while the
|
||||
// TUI stays mounted. Retained agents accept deliveries after detachment, so
|
||||
// without this a later send would drive a zombie agent/session; mark
|
||||
// disposed so dispatchMessage reports it instead.
|
||||
disposed = true
|
||||
clearStatus()
|
||||
appendNotice(`Agent "${agent.id}" was disposed.`, 'warning')
|
||||
})
|
||||
|
||||
@@ -2859,6 +2859,25 @@ describe('pi-tui chat lifecycle and transcript', () => {
|
||||
expect(events.terminal.output).toContain('was disposed')
|
||||
await dispose(events)
|
||||
})
|
||||
|
||||
it('rejects input after the agent is disposed out from under the TUI', async () => {
|
||||
const result = await setup()
|
||||
|
||||
// The agent leaves the registry (e.g. an agent-loop-only reload) while the
|
||||
// TUI stays mounted. A later send must report disposal, not drive the
|
||||
// detached zombie agent.
|
||||
agentEvents(result.ctx, result.agent).emit('agent/disposed')
|
||||
await tick()
|
||||
expect(result.terminal.output).toContain('was disposed')
|
||||
|
||||
result.terminal.send('drive the zombie')
|
||||
result.terminal.send('\r')
|
||||
await tick()
|
||||
expect(result.agent.sent).toHaveLength(0)
|
||||
expect(result.agent.steered).toHaveLength(0)
|
||||
expect(result.terminal.output).toContain('is disposed')
|
||||
await dispose(result)
|
||||
})
|
||||
})
|
||||
|
||||
describe('skill slash command', () => {
|
||||
|
||||
Reference in New Issue
Block a user