Merge remote-tracking branch 'origin/feat/send-unify' into xtr/agent-loop-message-machine
# Conflicts: # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.i18n.yaml # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.md # .agents/notes/implemented/architecture/2026-07-22-unified-send-and-coalesced-user-messages.zh.md # docs/architecture.i18n.yaml # docs/architecture.md # docs/architecture.zh.md # docs/core-data-structures/core.md # packages/context/session-reference/README.md # packages/cordis/tool-cordis/src/api-catalog.ts # packages/core/agent-loop/README.md # packages/core/agent-loop/src/agent.ts # packages/core/agent-loop/src/inbox.ts # packages/core/agent-loop/tests/agent.spec.ts # packages/core/agent-loop/tests/cancel.spec.ts # packages/core/agent-loop/tests/contract-regressions.spec.ts # packages/core/agent-loop/tests/coverage-edges.spec.ts # packages/core/agent-loop/tests/interception.spec.ts # packages/core/agent-loop/tests/loop.spec.ts # packages/core/agent/README.md # packages/core/agent/src/types.ts # packages/core/agent/tests/agent.spec.ts # packages/ui/acp/src/index.ts # packages/ui/tui/src/index.ts # packages/ui/tui/tests/harness.ts
This commit is contained in:
@@ -42,8 +42,8 @@ function sessionAgent(session: Session, id = 'agent'): Agent {
|
||||
session,
|
||||
status: 'running',
|
||||
ctx: new Context(),
|
||||
send: () => AgentMessageId('stub'),
|
||||
followup: () => AgentMessageId('stub'),
|
||||
queue: () => AgentMessageId('stub'),
|
||||
steer: () => AgentMessageId('stub'),
|
||||
inject(content, options) {
|
||||
session.append('user/message', {
|
||||
@@ -52,6 +52,7 @@ function sessionAgent(session: Session, id = 'agent'): Agent {
|
||||
}, { surfaceOp: 'append' })
|
||||
return AgentMessageId('stub')
|
||||
},
|
||||
send: () => AgentMessageId('stub'),
|
||||
cancel() {},
|
||||
whenIdle: () => Promise.resolve(),
|
||||
}
|
||||
@@ -373,7 +374,7 @@ describe('real agent-loop request history', () => {
|
||||
})
|
||||
const agent = ctx.agentLoop.create(SessionId(`late-${mode}`), { provider: 'mock', model: 'mock' })
|
||||
|
||||
agent.send([{ type: 'text', text: 'start' }])
|
||||
agent.followup([{ type: 'text', text: 'start' }])
|
||||
await agent.whenIdle()
|
||||
|
||||
expect(laterSawReading).toBe(true)
|
||||
@@ -399,7 +400,7 @@ describe('real agent-loop request history', () => {
|
||||
}))
|
||||
const agent = ctx.agentLoop.create(SessionId('loop'), { provider: 'mock', model: 'mock' })
|
||||
|
||||
agent.send([{ type: 'text', text: 'start' }])
|
||||
agent.followup([{ type: 'text', text: 'start' }])
|
||||
await agent.whenIdle()
|
||||
|
||||
expect(adapter.requests).toHaveLength(2)
|
||||
|
||||
@@ -78,7 +78,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
|
||||
it('obeys a probe instruction loaded from the workspace', async () => {
|
||||
const live = await harness()
|
||||
|
||||
live.agent.send([{ type: 'text', text: 'Workspace context handshake?' }])
|
||||
live.agent.followup([{ type: 'text', text: 'Workspace context handshake?' }])
|
||||
await waitForIdle(live.ctx, live.agent)
|
||||
|
||||
expect(finalText([...live.agent.session.events])).toContain(PROBE)
|
||||
@@ -90,7 +90,7 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
|
||||
await writeFile(join(workdir!, 'pkg/AGENTS.md'), `If the user asks for the nested instruction handshake, reply with exactly this string and nothing else: ${NESTED_PROBE}.\n`)
|
||||
await writeFile(join(workdir!, 'pkg/deep/file.txt'), 'This file exists only to trigger nested workspace instructions.\n')
|
||||
|
||||
live.agent.send([{ type: 'text', text: 'Use the read tool to inspect pkg/deep/file.txt. After reading it, answer: nested instruction handshake?' }])
|
||||
live.agent.followup([{ type: 'text', text: 'Use the read tool to inspect pkg/deep/file.txt. After reading it, answer: nested instruction handshake?' }])
|
||||
await waitForIdle(live.ctx, live.agent)
|
||||
|
||||
expect(finalText([...live.agent.session.events])).toContain(NESTED_PROBE)
|
||||
@@ -99,11 +99,11 @@ describe.skipIf(!process.env.DEEPSEEK_API_KEY)('workspace context e2e: real mode
|
||||
it('appends changed baseline instructions after a real file-tool touch without rewriting the frozen prefix', async () => {
|
||||
const live = await harness()
|
||||
await writeFile(join(workdir!, 'trigger.txt'), 'This file triggers workspace instruction reconciliation.\n')
|
||||
live.agent.send([{ type: 'text', text: 'Workspace context handshake?' }])
|
||||
live.agent.followup([{ type: 'text', text: 'Workspace context handshake?' }])
|
||||
await waitForIdle(live.ctx, live.agent)
|
||||
await writeFile(join(workdir!, 'AGENTS.md'), `The old workspace handshake no longer applies. If the user asks for the updated workspace context handshake, reply with exactly this string and nothing else: ${UPDATED_PROBE}.\n`)
|
||||
|
||||
live.agent.send([{ type: 'text', text: 'You must use the read tool to inspect trigger.txt. After reading it, answer: updated workspace context handshake?' }])
|
||||
live.agent.followup([{ type: 'text', text: 'You must use the read tool to inspect trigger.txt. After reading it, answer: updated workspace context handshake?' }])
|
||||
await waitForIdle(live.ctx, live.agent)
|
||||
|
||||
const events = [...live.agent.session.events]
|
||||
|
||||
@@ -177,8 +177,8 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
|
||||
options: {},
|
||||
session,
|
||||
status: 'idle',
|
||||
send: () => AgentMessageId('stub'),
|
||||
followup: () => AgentMessageId('stub'),
|
||||
queue: () => AgentMessageId('stub'),
|
||||
steer: () => AgentMessageId('stub'),
|
||||
inject(content, options) {
|
||||
session.append('user/message', {
|
||||
@@ -187,6 +187,7 @@ function stubAgent(cwd?: string, seed: SessionEvent[] = []): Agent {
|
||||
}, { surfaceOp: 'append' })
|
||||
return AgentMessageId('stub')
|
||||
},
|
||||
send: () => AgentMessageId('stub'),
|
||||
cancel() {},
|
||||
whenIdle: () => Promise.resolve(),
|
||||
}
|
||||
@@ -1712,11 +1713,11 @@ describe('dynamic nested workspace context injection', () => {
|
||||
},
|
||||
}))
|
||||
|
||||
agent.send([{ type: 'text', text: 'read and abort' }])
|
||||
agent.followup([{ type: 'text', text: 'read and abort' }])
|
||||
await agent.whenIdle()
|
||||
expect(agent.session.events.filter(event => event.type === 'user/message' && event.data.source.kind !== 'user')).toHaveLength(1)
|
||||
|
||||
agent.send([{ type: 'text', text: 'retry the read' }])
|
||||
agent.followup([{ type: 'text', text: 'retry the read' }])
|
||||
await agent.whenIdle()
|
||||
|
||||
const contexts = agent.session.events.filter(event => event.type === 'user/message' && event.data.source.kind !== 'user')
|
||||
|
||||
Reference in New Issue
Block a user