feat(agent): unify send(target × wakeup), coalesce context/message into user/message

Replace send/steer/inject with one Agent.send primitive over the
(target × wakeup) matrix; followup/steer/inject become fixed-preset
alias methods on the now-abstract Agent class. Coalesce context/message
into user/message (injected context is a non-user source). Replace
agent/queued with agent/inbox/enqueue/dequeue/discard, add cancel
keepInbox, and add a FIFO-conservation invariant.
This commit is contained in:
Turtle
2026-07-23 19:15:45 +08:00
parent 7c0c516f60
commit 44fd93fd06
117 changed files with 1249 additions and 728 deletions

View File

@@ -2,7 +2,7 @@ import { describe, expect, it } from 'vitest'
import { Inbox } from '../src/inbox.ts'
function message(text: string) {
return { content: [{ type: 'text' as const, text }], source: { kind: 'user' as const }, contexts: [] }
return { content: [{ type: 'text' as const, text }], source: { kind: 'user' as const }, contexts: [], wakeup: true }
}
function resolverPair() {
@@ -25,6 +25,32 @@ describe('Inbox', () => {
expect(inbox.dequeueQueued()).toBeUndefined()
})
it('enqueue(msg, false) queues without waking a parked waiter', async () => {
const inbox = new Inbox()
let woke = false
const waiter = inbox.waitForQueued(new Promise(() => {})).then(() => { woke = true })
inbox.enqueue(message('quiet'), false)
// The item is queued, but the parked waiter was not resolved by it.
expect(inbox.hasQueued).toBe(true)
await Promise.resolve()
expect(woke).toBe(false)
// A later waking enqueue resolves the same waiter.
inbox.enqueue(message('loud'))
await waiter
expect(woke).toBe(true)
})
it('pending() snapshots queued then steering without removing them', () => {
const inbox = new Inbox()
inbox.enqueue(message('q'))
inbox.steer(message('s'))
const pending = inbox.pending()
expect(pending.map(p => p.steering)).toEqual([false, true])
// Snapshot does not drain the FIFOs.
expect(inbox.hasQueued).toBe(true)
expect(inbox.hasSteering).toBe(true)
})
it('pushes and drains steering messages separately from queued', () => {
const inbox = new Inbox()
inbox.steer(message('steer'))