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:
@@ -332,7 +332,7 @@ export class PlanModeService extends Service {
|
||||
const text = target
|
||||
? 'The user switched this session to plan mode.'
|
||||
: 'The user switched this session back to the default mode.'
|
||||
session.append('context/message', {
|
||||
session.append('user/message', {
|
||||
content: [{ type: 'text', text }],
|
||||
source: { kind: 'plugin', plugin: 'plan-mode' },
|
||||
}, { surfaceOp: 'append' })
|
||||
|
||||
@@ -92,7 +92,7 @@ describe('plan mode through the agent loop', () => {
|
||||
const result = findEvent(log, 'tool/result')
|
||||
expect(result.data.isError).toBe(false)
|
||||
expect(foldPlanMode(log)).toBe(true)
|
||||
expect(log.some(event => event.type === 'context/message')).toBe(false)
|
||||
expect(log.some(event => event.type === 'user/message' && event.data.source.kind === 'plugin')).toBe(false)
|
||||
})
|
||||
|
||||
it('a user flip between turns lands at the boundary: one notice and a changed header with stable tool schemas', async () => {
|
||||
@@ -115,9 +115,9 @@ describe('plan mode through the agent loop', () => {
|
||||
|
||||
const log = agent.session.events
|
||||
expect(foldPlanMode(log)).toBe(true)
|
||||
const notices = log.filter(event => event.type === 'context/message')
|
||||
const notices = log.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin')
|
||||
expect(notices).toHaveLength(1)
|
||||
expect(findEvent(log, 'context/message').data.content).toEqual([
|
||||
expect(notices[0]?.type === 'user/message' && notices[0].data.content).toEqual([
|
||||
{ type: 'text', text: 'The user switched this session to plan mode.' },
|
||||
])
|
||||
// The changed request is logged as a complete snapshot.
|
||||
@@ -163,7 +163,8 @@ describe('plan mode through the agent loop', () => {
|
||||
expect(firstEnd?.seq).toBeLessThan(planMode.seq)
|
||||
expect(planMode.seq).toBeLessThan(retryStart?.seq ?? 0)
|
||||
expect(findEvent(log, 'request/header', 'last').data.header.system).toContain(PLAN_CONFIG.section)
|
||||
expect(findEvent(log, 'context/message').data.content).toEqual([
|
||||
const notice = log.find(event => event.type === 'user/message' && event.data.source.kind === 'plugin')
|
||||
expect(notice?.type === 'user/message' && notice.data.content).toEqual([
|
||||
{ type: 'text', text: 'The user switched this session to plan mode.' },
|
||||
])
|
||||
})
|
||||
|
||||
@@ -95,7 +95,7 @@ function header(session: Session): void {
|
||||
|
||||
function noticeTexts(session: Session): string[] {
|
||||
return session.events
|
||||
.filter(event => event.type === 'context/message')
|
||||
.filter(event => event.type === 'user/message' && event.data.source.kind === 'plugin')
|
||||
.map(event => (event.data as { content: { type: string; text?: string }[] }).content.map(block => block.text ?? '').join(''))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user