fix(tasks): bind cleanup and notices to exact owners
Task records previously retained only ownerSession. If an old agent scope unwound after another agent reused the same agent and session ids, cleanup selected both records and could cancel replacement work. The completion surface also re-resolved the session at settlement, which could inject an old task notice into the replacement agent. Retain the exact Agent instance for lifecycle work, select owner cleanup by object identity, and pass that exact owner to completion listeners. Keep read, list, kill, and wait authorization session-based as the runtime RFC intends. Add regressions for cleanup and notice routing under id reuse, then update the public docs and generated API catalogs.
This commit is contained in:
@@ -506,6 +506,39 @@ describe('TaskService owner cleanup', () => {
|
||||
expect(ctx.tasks.list(owner)).toEqual([])
|
||||
})
|
||||
|
||||
it('does not let an old scope cleanup cancel a same-id/session replacement task', async () => {
|
||||
const ctx = await harness()
|
||||
const oldOwner = stubAgent(ctx, 'owner', 'shared-session')
|
||||
const detachOld = ctx.agents.register(oldOwner)
|
||||
const cancels: string[] = []
|
||||
|
||||
function start(owner: Agent, label: string): TaskId {
|
||||
let settle!: (outcome: TaskOutcome) => void
|
||||
return ctx.tasks.start({
|
||||
kind: 'bash',
|
||||
label,
|
||||
owner,
|
||||
run: () => ({
|
||||
cancel() { cancels.push(label); settle({ status: 'killed' }) },
|
||||
done: new Promise<TaskOutcome>((resolve) => { settle = resolve }),
|
||||
}),
|
||||
})
|
||||
}
|
||||
|
||||
start(oldOwner, 'old task')
|
||||
detachOld()
|
||||
const replacement = stubAgent(ctx, 'owner', 'shared-session')
|
||||
ctx.agents.register(replacement)
|
||||
const replacementId = start(replacement, 'replacement task')
|
||||
|
||||
await disposeAgentScope(oldOwner)
|
||||
expect(cancels).toEqual(['old task'])
|
||||
expect(ctx.tasks.list(replacement).map(task => task.id)).toEqual([replacementId])
|
||||
|
||||
await disposeAgentScope(replacement)
|
||||
expect(cancels).toEqual(['old task', 'replacement task'])
|
||||
})
|
||||
|
||||
it('registers owner cleanup on the agent scope rather than the tasks fiber', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(AgentRegistry)
|
||||
|
||||
Reference in New Issue
Block a user