refactor(agent): align delivery method names
This commit is contained in:
@@ -48,11 +48,11 @@ function stubAgent(id: string): { agent: Agent; session: Session } {
|
||||
session,
|
||||
ctx: new Context(),
|
||||
get status() { return status },
|
||||
send: () => AgentMessageId('stub'),
|
||||
followup: () => AgentMessageId('stub'),
|
||||
queue: () => AgentMessageId('stub'),
|
||||
steer: () => AgentMessageId('stub'),
|
||||
inject(content, options) { appendInjection(session, content, options); return AgentMessageId('stub') },
|
||||
acceptInput: () => AgentMessageId('stub'),
|
||||
send: () => AgentMessageId('stub'),
|
||||
cancel() { status = 'idle' },
|
||||
whenIdle() { return Promise.resolve() },
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ export function apply(ctx: Context): void {
|
||||
}
|
||||
state.attempt = reservation
|
||||
try {
|
||||
agent.send(content, {
|
||||
agent.followup(content, {
|
||||
source: { kind: 'goal', goalId: goal.id, revision: goal.revision, round },
|
||||
})
|
||||
} catch (error: unknown) {
|
||||
|
||||
@@ -7,7 +7,7 @@ import type { GoalView } from '@deepseek-ai/dsh-goal'
|
||||
* Render the complete goal-round instruction retained in session history.
|
||||
* @param goal - exact active goal revision being admitted.
|
||||
* @param round - next positive round number.
|
||||
* @returns a fresh one-block prompt for `Agent.send()`.
|
||||
* @returns a fresh one-block prompt for `Agent.followup()`.
|
||||
*/
|
||||
export function renderGoalRoundPrompt(goal: GoalView, round: number): ContentBlock[] {
|
||||
return [{
|
||||
|
||||
@@ -276,7 +276,7 @@ describe('same-session goal driving', () => {
|
||||
? Promise.resolve({ kind: 'block', reason: 'stop this round' })
|
||||
: next())
|
||||
test.ctx.on('goal/changed', (agent, change) => {
|
||||
if (change.operation === 'block') agent.send([{ type: 'text', text: 'inspect the blocker' }])
|
||||
if (change.operation === 'block') agent.followup([{ type: 'text', text: 'inspect the blocker' }])
|
||||
})
|
||||
test.ctx.goals.create(test.agent, { objective: 'stop and inspect' })
|
||||
|
||||
@@ -323,7 +323,7 @@ describe('same-session goal driving', () => {
|
||||
it('lets already-queued human work finish before reserving the next round', async () => {
|
||||
const test = await harness([textResponse('human answer'), textResponse('goal answer')])
|
||||
test.ctx.goals.create(test.agent, { objective: 'continue after the human', maxGoalRounds: 1 })
|
||||
test.agent.send([{ type: 'text', text: 'human goes first' }])
|
||||
test.agent.followup([{ type: 'text', text: 'human goes first' }])
|
||||
|
||||
await waitForGoal(test.ctx, test.agent, goal => goal?.phase === 'blocked')
|
||||
|
||||
@@ -364,7 +364,7 @@ describe('same-session goal driving', () => {
|
||||
test.ctx.on('agent/inbox/enqueue', (agent, info) => {
|
||||
if (agent !== test.agent || info.source.kind !== 'goal' || inserted) return
|
||||
inserted = true
|
||||
agent.send([{ type: 'text', text: 'human joined the pending batch' }])
|
||||
agent.followup([{ type: 'text', text: 'human joined the pending batch' }])
|
||||
})
|
||||
test.ctx.goals.create(test.agent, { objective: 'yield to nested human input', maxGoalRounds: 1 })
|
||||
|
||||
@@ -479,16 +479,16 @@ describe('same-session goal driving', () => {
|
||||
expect(injectedTurn).toBeGreaterThan(goalTurn)
|
||||
})
|
||||
|
||||
it('blocks the goal when a custom agent rejects the otherwise valid send', async () => {
|
||||
it('blocks the goal when a custom agent rejects the otherwise valid follow-up', async () => {
|
||||
const test = await harness([])
|
||||
// Reject only the goal-sourced round send, not the state-change injection
|
||||
// Reject only the goal-sourced round follow-up, not the state-change injection
|
||||
// that precedes it.
|
||||
const realSend = test.agent.send.bind(test.agent)
|
||||
vi.spyOn(test.agent, 'send').mockImplementation((content, options) => {
|
||||
const realFollowup = test.agent.followup.bind(test.agent)
|
||||
vi.spyOn(test.agent, 'followup').mockImplementation((content, options) => {
|
||||
if (options?.source?.kind === 'goal') {
|
||||
throw new Error('queue rejected')
|
||||
}
|
||||
return realSend(content, options)
|
||||
return realFollowup(content, options)
|
||||
})
|
||||
test.ctx.goals.create(test.agent, { objective: 'handle queue failure' })
|
||||
|
||||
@@ -502,15 +502,15 @@ describe('same-session goal driving', () => {
|
||||
expect(test.adapter.requests).toHaveLength(0)
|
||||
})
|
||||
|
||||
it('preserves a custom agent side effect when send disarms before throwing', async () => {
|
||||
it('preserves a custom agent side effect when followup disarms before throwing', async () => {
|
||||
const test = await harness([])
|
||||
const realSend = test.agent.send.bind(test.agent)
|
||||
vi.spyOn(test.agent, 'send').mockImplementation((content, options) => {
|
||||
const realFollowup = test.agent.followup.bind(test.agent)
|
||||
vi.spyOn(test.agent, 'followup').mockImplementation((content, options) => {
|
||||
if (options?.source?.kind === 'goal') {
|
||||
test.ctx.goals.disarm(test.agent)
|
||||
throw new Error('queue rejected after disarm')
|
||||
}
|
||||
return realSend(content, options)
|
||||
return realFollowup(content, options)
|
||||
})
|
||||
test.ctx.goals.create(test.agent, { objective: 'preserve the newer activation state' })
|
||||
|
||||
@@ -609,7 +609,7 @@ describe('same-session goal driving', () => {
|
||||
|
||||
it('blocks forged goal attribution without touching an absent reservation', async () => {
|
||||
const test = await harness([])
|
||||
test.agent.send([{ type: 'text', text: 'forged automatic work' }], {
|
||||
test.agent.followup([{ type: 'text', text: 'forged automatic work' }], {
|
||||
source: { kind: 'goal', goalId: GoalId('forged-goal'), revision: 1, round: 1 },
|
||||
})
|
||||
await test.agent.whenIdle()
|
||||
@@ -621,7 +621,7 @@ describe('same-session goal driving', () => {
|
||||
|
||||
it('does not invent goal state when ordinary queued work is cancelled', async () => {
|
||||
const test = await harness([])
|
||||
test.agent.send([{ type: 'text', text: 'cancel ordinary work' }])
|
||||
test.agent.followup([{ type: 'text', text: 'cancel ordinary work' }])
|
||||
test.agent.cancel({ kind: 'user' })
|
||||
await test.agent.whenIdle()
|
||||
|
||||
@@ -631,7 +631,7 @@ describe('same-session goal driving', () => {
|
||||
|
||||
it('disarms without durably pausing when cancellation belongs to unrelated human work', async () => {
|
||||
const test = await harness(['hang'])
|
||||
test.agent.send([{ type: 'text', text: 'inspect something first' }])
|
||||
test.agent.followup([{ type: 'text', text: 'inspect something first' }])
|
||||
await waitForRequests(test.adapter, 1)
|
||||
const created = test.ctx.goals.create(test.agent, { objective: 'continue after inspection' })
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ function stubAgentForSession(session: Session): StubAgent {
|
||||
session,
|
||||
ctx: new Context(),
|
||||
get status() { return status },
|
||||
send: () => AgentMessageId('stub'),
|
||||
followup: () => AgentMessageId('stub'),
|
||||
queue: () => AgentMessageId('stub'),
|
||||
steer: () => AgentMessageId('stub'),
|
||||
inject(content, options) {
|
||||
@@ -72,7 +72,7 @@ function stubAgentForSession(session: Session): StubAgent {
|
||||
else appendInjection(session, content, options)
|
||||
return AgentMessageId('stub')
|
||||
},
|
||||
acceptInput: () => AgentMessageId('stub'),
|
||||
send: () => AgentMessageId('stub'),
|
||||
cancel() {},
|
||||
whenIdle() { return Promise.resolve() },
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ An autonomous goal round that successfully reports `complete` or `blocked` contr
|
||||
|
||||
Execution requires the exact live `exec.agent`, its inherited `AgentRegistry` initiator, running status, and an open turn. Create, edit, pause, and resume additionally require an accepted `{ kind: 'user' }` message or steering event in a runtime-root agent's current turn. Durable fork lineage does not demote a resumed root; live subagent ownership does.
|
||||
|
||||
`{ kind: 'user' }` is a host attestation. `Agent.send()` and `steer()` assign it when their caller omits a source, so plugins, schedulers, and other non-human producers must pass their own source rather than inheriting human authority.
|
||||
`{ kind: 'user' }` is a host attestation. `Agent.followup()` and `steer()` assign it when their caller omits a source, so plugins, schedulers, and other non-human producers must pass their own source rather than inheriting human authority.
|
||||
|
||||
Complete and blocked also accept the exact current goal round: a goal-sourced `user/message` whose id, revision, and round equal the folded current goal. A goal-round blocked call is mechanically rejected until `blockedAfterConsecutiveRounds`; the model judges whether the same condition actually persisted and must describe it in `blocked_reason`. Direct human authority may stop a goal immediately.
|
||||
|
||||
|
||||
@@ -64,7 +64,7 @@ export function goalToolExecution(ctx: Context, exec: ToolRunContext): GoalToolE
|
||||
|
||||
/**
|
||||
* Whether host-attested human input appears in the current root-agent turn.
|
||||
* An omitted `Agent.send()` / `steer()` source resolves to `user`, so non-human
|
||||
* An omitted `Agent.followup()` / `steer()` source resolves to `user`, so non-human
|
||||
* producers must supply their own source rather than inheriting this authority.
|
||||
*/
|
||||
function hasDirectHumanInput(ctx: Context, execution: GoalToolExecution): boolean {
|
||||
|
||||
@@ -31,7 +31,7 @@ function stubAgent(rawId: string, supplied?: Session): StubAgent {
|
||||
session,
|
||||
get status() { return status },
|
||||
ctx: new Context(),
|
||||
send: () => AgentMessageId('stub'),
|
||||
followup: () => AgentMessageId('stub'),
|
||||
queue: () => AgentMessageId('stub'),
|
||||
steer: () => AgentMessageId('stub'),
|
||||
inject(content: ContentBlock[], options?: InjectOptions) {
|
||||
@@ -43,7 +43,7 @@ function stubAgent(rawId: string, supplied?: Session): StubAgent {
|
||||
}, { surfaceOp: 'append' })
|
||||
return AgentMessageId('stub')
|
||||
},
|
||||
acceptInput: () => AgentMessageId('stub'),
|
||||
send: () => AgentMessageId('stub'),
|
||||
cancel() {},
|
||||
whenIdle() { return Promise.resolve() },
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user