fix: commit step context before request dispatch
This commit is contained in:
@@ -185,7 +185,12 @@ describe('dsh-jsonrpc plugin apply', () => {
|
||||
params: { sessionId: 'main', contentBlocks: [{ type: 'text', text: 'fix it' }] },
|
||||
})
|
||||
const response = await harness.waitForFrame(frame => frame.id === 2, 'prompt response')
|
||||
expect(response.result).toEqual({ accepted: true })
|
||||
expect((response.result as { messageId?: unknown }).messageId).toBeTypeOf('string')
|
||||
await harness.waitForFrame(
|
||||
frame => frame.method === 'session.status'
|
||||
&& (frame.params as { status?: string } | undefined)?.status === 'idle',
|
||||
'idle session status',
|
||||
)
|
||||
|
||||
expect(llmServer.requests).toHaveLength(1)
|
||||
const body = llmServer.requests[0] as { model: string; messages: { role: string }[] }
|
||||
@@ -195,9 +200,9 @@ describe('dsh-jsonrpc plugin apply', () => {
|
||||
// Notifications use the same transport and arrive as id-less frames.
|
||||
const notifications = harness.frames().filter(frame => frame.id === undefined)
|
||||
expect(notifications.some(frame => frame.method === 'session.event')).toBe(true)
|
||||
expect(notifications.find(frame => frame.method === 'session.finished')).toMatchObject({
|
||||
expect(notifications.findLast(frame => frame.method === 'session.status')).toMatchObject({
|
||||
jsonrpc: '2.0',
|
||||
params: { sessionId: 'main', status: 'ok' },
|
||||
params: { sessionId: 'main', status: 'idle' },
|
||||
})
|
||||
} finally {
|
||||
await harness.dispose()
|
||||
|
||||
@@ -8,7 +8,7 @@ import { afterEach, describe, expect, it, vi } from 'vitest'
|
||||
import { Context } from 'cordis'
|
||||
import AgentRegistry, { type Agent, type AgentHandle } from '@deepseek-ai/dsh-agent'
|
||||
|
||||
import SessionStore, { SessionId, type UserMessage } from '@deepseek-ai/dsh-session'
|
||||
import SessionStore, { SessionId } from '@deepseek-ai/dsh-session'
|
||||
import * as agentCore from '@deepseek-ai/dsh-agent-spine-demo'
|
||||
import SessionPersistenceJsonl from '@deepseek-ai/dsh-session-persistence-jsonl'
|
||||
import * as LlmDeepSeek from '@deepseek-ai/dsh-llm-deepseek'
|
||||
@@ -127,12 +127,13 @@ describe('HarnessSdkServer', () => {
|
||||
}) as { serverInfo: { name: string } }
|
||||
expect(init.serverInfo.name).toBe('deepseek-harness-sdk-runtime')
|
||||
|
||||
await server.handleRequest('session/prompt', {
|
||||
const receipt = await server.handleRequest('session/prompt', {
|
||||
sessionId: 'main',
|
||||
contentBlocks: [{ type: 'text', text: 'fix it' }],
|
||||
})
|
||||
expect((receipt as { messageId?: unknown }).messageId).toBeTypeOf('string')
|
||||
|
||||
expect(llmServer.requests).toHaveLength(1)
|
||||
await vi.waitFor(() => { expect(llmServer.requests).toHaveLength(1) })
|
||||
const body = llmServer.requests[0] as { model: string; messages: { role: string }[]; max_tokens?: number }
|
||||
expect(body.model).toBe('dsagent-model')
|
||||
expect(body.max_tokens).toBe(321)
|
||||
@@ -140,16 +141,18 @@ describe('HarnessSdkServer', () => {
|
||||
expect(body.messages.at(-1)?.role).toBe('user')
|
||||
expect(llmServer.headers[0]?.authorization).toBe('Bearer test-key')
|
||||
expect(transport.notifications.some(n => n.method === 'session.event')).toBe(true)
|
||||
expect(transport.notifications.at(-1)).toMatchObject({
|
||||
method: 'session.finished',
|
||||
params: { sessionId: 'main', status: 'ok' },
|
||||
await vi.waitFor(() => {
|
||||
expect(transport.notifications.findLast(n => n.method === 'session.status')).toEqual({
|
||||
method: 'session.status',
|
||||
params: { sessionId: 'main', status: 'idle' },
|
||||
})
|
||||
})
|
||||
|
||||
await server.handleRequest('session/prompt', {
|
||||
sessionId: 'main',
|
||||
contentBlocks: [{ type: 'text', text: 'again' }],
|
||||
})
|
||||
expect(llmServer.requests).toHaveLength(2)
|
||||
await vi.waitFor(() => { expect(llmServer.requests).toHaveLength(2) })
|
||||
|
||||
const orphanHandle = await ctx.agents.create({
|
||||
sessionId: SessionId('orphan-session'),
|
||||
@@ -168,24 +171,17 @@ describe('HarnessSdkServer', () => {
|
||||
}
|
||||
})
|
||||
|
||||
it('rejects overlapping prompts for one session without serializing other sessions', async () => {
|
||||
let releaseMain: (() => void) | undefined
|
||||
const firstMainIdle = new Promise<void>((resolve) => { releaseMain = resolve })
|
||||
const mainWhenIdle = vi.fn<() => Promise<void>>()
|
||||
.mockReturnValueOnce(firstMainIdle)
|
||||
.mockResolvedValue(undefined)
|
||||
it('queues overlapping prompts for one session without blocking other sessions', async () => {
|
||||
const mainFollowup = vi.fn<Agent['followup']>()
|
||||
const mainAgent = ({
|
||||
id: SessionId('main'),
|
||||
followup: mainFollowup,
|
||||
whenIdle: mainWhenIdle,
|
||||
} satisfies Pick<Agent, 'id' | 'followup' | 'whenIdle'>) as unknown as Agent
|
||||
} satisfies Pick<Agent, 'id' | 'followup'>) as unknown as Agent
|
||||
const otherFollowup = vi.fn<Agent['followup']>()
|
||||
const otherAgent = ({
|
||||
id: SessionId('other'),
|
||||
followup: otherFollowup,
|
||||
whenIdle: vi.fn(() => Promise.resolve()),
|
||||
} satisfies Pick<Agent, 'id' | 'followup' | 'whenIdle'>) as unknown as Agent
|
||||
} satisfies Pick<Agent, 'id' | 'followup'>) as unknown as Agent
|
||||
const mainHandle = { agent: mainAgent, dispose: vi.fn(() => Promise.resolve()) }
|
||||
const otherHandle = { agent: otherAgent, dispose: vi.fn(() => Promise.resolve()) }
|
||||
const create = vi.fn(async (options: { sessionId: SessionId }) =>
|
||||
@@ -202,20 +198,11 @@ describe('HarnessSdkServer', () => {
|
||||
contentBlocks: [{ type: 'text', text }],
|
||||
})
|
||||
|
||||
const first = prompt('main', 'first')
|
||||
await vi.waitFor(() => { expect(mainFollowup).toHaveBeenCalledOnce() })
|
||||
expect((await prompt('main', 'first')).messageId).toBeTypeOf('string')
|
||||
expect((await prompt('main', 'overlap')).messageId).toBeTypeOf('string')
|
||||
expect((await prompt('other', 'independent')).messageId).toBeTypeOf('string')
|
||||
|
||||
await expect(prompt('main', 'overlap')).rejects.toThrow('session already has an active prompt: main')
|
||||
await expect(prompt('other', 'independent')).resolves.toEqual({ accepted: true })
|
||||
releaseMain?.()
|
||||
await expect(first).resolves.toEqual({ accepted: true })
|
||||
await expect(prompt('main', 'sequential')).resolves.toEqual({ accepted: true })
|
||||
|
||||
mainWhenIdle.mockRejectedValueOnce(new Error('turn wait failed'))
|
||||
await expect(prompt('main', 'failing')).rejects.toThrow('turn wait failed')
|
||||
await expect(prompt('main', 'after failure')).resolves.toEqual({ accepted: true })
|
||||
|
||||
expect(mainFollowup).toHaveBeenCalledTimes(4)
|
||||
expect(mainFollowup).toHaveBeenCalledTimes(2)
|
||||
expect(otherFollowup).toHaveBeenCalledOnce()
|
||||
await server.shutdown()
|
||||
expect(mainHandle.dispose).toHaveBeenCalledOnce()
|
||||
@@ -247,7 +234,7 @@ describe('HarnessSdkServer', () => {
|
||||
contentBlocks: [{ type: 'text', text }],
|
||||
})
|
||||
|
||||
await expect(prompt('while live')).resolves.toEqual({ accepted: true })
|
||||
expect((await prompt('while live')).messageId).toBeTypeOf('string')
|
||||
live = false
|
||||
await expect(prompt('after detach')).rejects.toThrow('session agent was disposed outside the server: zombie')
|
||||
// The detached agent was never driven by the rejected prompt.
|
||||
@@ -255,59 +242,26 @@ describe('HarnessSdkServer', () => {
|
||||
await server.shutdown()
|
||||
})
|
||||
|
||||
it('reports the final whole-agent outcome after later activity settles', async () => {
|
||||
it('forwards whole-agent status without attributing a turn outcome', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(SessionStore)
|
||||
await ctx.plugin(AgentRegistry)
|
||||
const transport = new FakeTransport()
|
||||
const server = new HarnessSdkServer(ctx, transport) as unknown as {
|
||||
prompt(params: { sessionId: string; contentBlocks: { type: 'text'; text: string }[] }): Promise<unknown>
|
||||
sessions: Map<string, { handle: AgentHandle; lastTurnEnd: undefined; activePrompt: boolean }>
|
||||
shutdown(): Promise<Record<string, never>>
|
||||
}
|
||||
const server = new HarnessSdkServer(ctx, transport)
|
||||
const session = ctx.sessions.create(SessionId('message-outcome'))
|
||||
const agent = ({
|
||||
id: SessionId('message-outcome'),
|
||||
session,
|
||||
followup(input: UserMessage) {
|
||||
session.append('turn/start', {
|
||||
turn: 1,
|
||||
})
|
||||
session.append('user/message', input, { surfaceOp: 'append' })
|
||||
session.append('turn/end', { turn: 1, reason: { kind: 'max-tokens' } })
|
||||
session.append('turn/start', {
|
||||
turn: 2,
|
||||
})
|
||||
session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text: 'late metadata' }],
|
||||
source: { kind: 'plugin', plugin: 'late-metadata' },
|
||||
}), { surfaceOp: 'append' })
|
||||
session.append('turn/end', { turn: 2, reason: { kind: 'completed' } })
|
||||
return input.id
|
||||
},
|
||||
whenIdle: () => Promise.resolve(),
|
||||
} satisfies Pick<Agent, 'id' | 'session' | 'followup' | 'whenIdle'>) as unknown as Agent
|
||||
ctx.agents.register(agent)
|
||||
server.sessions.set('message-outcome', {
|
||||
handle: { agent, dispose: () => Promise.resolve() },
|
||||
lastTurnEnd: undefined,
|
||||
activePrompt: false,
|
||||
})
|
||||
} satisfies Pick<Agent, 'id' | 'session'>) as Agent
|
||||
|
||||
await server.prompt({
|
||||
sessionId: 'message-outcome',
|
||||
contentBlocks: [{ type: 'text', text: 'bounded prompt' }],
|
||||
})
|
||||
ctx.emit('agent/status', agent, 'running')
|
||||
ctx.emit('agent/status', agent, 'idle')
|
||||
|
||||
expect(transport.notifications.findLast(notification => notification.method === 'session.finished'))
|
||||
.toEqual({
|
||||
method: 'session.finished',
|
||||
params: {
|
||||
sessionId: 'message-outcome',
|
||||
status: 'ok',
|
||||
reason: { kind: 'completed' },
|
||||
},
|
||||
})
|
||||
expect(transport.notifications.filter(notification => notification.method === 'session.status'))
|
||||
.toEqual([
|
||||
{ method: 'session.status', params: { sessionId: 'message-outcome', status: 'running' } },
|
||||
{ method: 'session.status', params: { sessionId: 'message-outcome', status: 'idle' } },
|
||||
])
|
||||
await server.shutdown()
|
||||
await ctx.fiber.dispose()
|
||||
})
|
||||
@@ -356,7 +310,7 @@ describe('HarnessSdkServer', () => {
|
||||
contentBlocks: [{ type: 'text', text: 'hello' }],
|
||||
})
|
||||
|
||||
expect(llmServer.requests).toHaveLength(1)
|
||||
await vi.waitFor(() => { expect(llmServer.requests).toHaveLength(1) })
|
||||
await server.shutdown()
|
||||
} finally {
|
||||
await ctx.fiber.dispose()
|
||||
@@ -881,43 +835,6 @@ describe('HarnessSdkServer', () => {
|
||||
},
|
||||
)
|
||||
|
||||
it('classifies defensive finish states', async () => {
|
||||
const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-finish-states-'))
|
||||
const ctx = await makeHarness(storageDir)
|
||||
try {
|
||||
const server = new HarnessSdkServer(ctx, new FakeTransport()) as unknown as {
|
||||
finishedStatus(reason: unknown): 'ok' | 'error'
|
||||
shutdown(): Promise<Record<string, never>>
|
||||
}
|
||||
|
||||
expect(server.finishedStatus(undefined)).toBe('error')
|
||||
expect(server.finishedStatus({ kind: 'max-tokens' })).toBe('error')
|
||||
expect(server.finishedStatus({ kind: 'error' })).toBe('error')
|
||||
await server.shutdown()
|
||||
} finally {
|
||||
await ctx.fiber.dispose()
|
||||
await rm(storageDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('can report max-token turn termination as an accepted evaluation result', async () => {
|
||||
const storageDir = await mkdtemp(join(tmpdir(), 'dsh-jsonrpc-max-tokens-success-'))
|
||||
const ctx = await makeHarness(storageDir)
|
||||
try {
|
||||
const server = new HarnessSdkServer(ctx, new FakeTransport(), { maxTokensAsSuccess: true }) as unknown as {
|
||||
finishedStatus(reason: unknown): 'ok' | 'error'
|
||||
shutdown(): Promise<Record<string, never>>
|
||||
}
|
||||
|
||||
expect(server.finishedStatus({ kind: 'max-tokens' })).toBe('ok')
|
||||
expect(server.finishedStatus({ kind: 'error' })).toBe('error')
|
||||
await server.shutdown()
|
||||
} finally {
|
||||
await ctx.fiber.dispose()
|
||||
await rm(storageDir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
it('reports no adapter when the LLM service is absent', async () => {
|
||||
const ctx = new Context()
|
||||
try {
|
||||
@@ -1045,6 +962,6 @@ describe('HarnessSdkServer', () => {
|
||||
const server = new HarnessSdkServer(ctx, new FakeTransport())
|
||||
|
||||
await expect(server.shutdown()).rejects.toBe(listenerFailure)
|
||||
expect(on).toHaveBeenCalledTimes(3)
|
||||
expect(on).toHaveBeenCalledTimes(4)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -277,10 +277,10 @@ export class ApprovalService extends Service {
|
||||
const cause = overrideSource === 'delegation'
|
||||
? 'inherited from the delegating session'
|
||||
: overrideIndex > headerIndex ? 'changed by the user' : 'changed by the operator/config'
|
||||
agent.inject(createUserMessage({
|
||||
session.append('user/message', createUserMessage({
|
||||
content: [{ type: 'text', text: `The approval policy changed from "${told}" to "${current}" (${cause}).` }],
|
||||
source: { kind: 'plugin', plugin: 'user-approval' },
|
||||
}))
|
||||
}), { surfaceOp: 'append' })
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -358,23 +358,27 @@ describe('approval policy (the approval/policy fold)', () => {
|
||||
* An agent stand-in over a REAL Session — gate, section, and narrator fold
|
||||
* real events; the opened turn satisfies request()'s enclosure precondition.
|
||||
*/
|
||||
function sessionAgent(id: string): { agent: Agent; session: Session; injected: string[] } {
|
||||
function sessionAgent(id: string): { agent: Agent; session: Session } {
|
||||
const session = new Session(SessionId(id))
|
||||
session.append('turn/start', { turn: 1 })
|
||||
const injected: string[] = []
|
||||
const agent = {
|
||||
id,
|
||||
session,
|
||||
inject: (input: { content: Array<{ type: string; text: string }> }) => {
|
||||
injected.push(input.content[0]?.text ?? '')
|
||||
},
|
||||
inject: () => { throw new Error('step-boundary narration must not use agent.inject()') },
|
||||
} as unknown as Agent
|
||||
return { agent, session, injected }
|
||||
return { agent, session }
|
||||
}
|
||||
|
||||
const preStep = (ctx: Context, agent: Agent): Promise<void> =>
|
||||
agentEvents(ctx, agent).serial('agent/step', 1, 1, new AbortController().signal)
|
||||
|
||||
const narrations = (session: Session): string[] => session.events.flatMap(event =>
|
||||
event.type === 'user/message'
|
||||
&& event.data.source.kind === 'plugin'
|
||||
&& event.data.source.plugin === 'user-approval'
|
||||
? [event.data.content.flatMap(block => block.type === 'text' ? [block.text] : []).join('')]
|
||||
: [])
|
||||
|
||||
/** Append a `request/header` snapshot whose system text is exactly `system`. */
|
||||
function appendHeader(session: Session, system: string): void {
|
||||
session.append('request/header', { header: { config: { provider: 'mock', model: 'mock' }, system }, reason: 'initial' })
|
||||
@@ -482,20 +486,20 @@ describe('approval policy (the approval/policy fold)', () => {
|
||||
it('narrates nothing cold, once per coalesced switch (user wording), and idempotently', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService)
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-1')
|
||||
const { agent, session } = sessionAgent('sess-narr-1')
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual([])
|
||||
expect(narrations(session)).toEqual([])
|
||||
setApprovalPolicy(session, 'never')
|
||||
setApprovalPolicy(session, 'ask')
|
||||
setApprovalPolicy(session, 'never')
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual(['The approval policy changed from "ask" to "never" (changed by the user).'])
|
||||
expect(narrations(session)).toEqual(['The approval policy changed from "ask" to "never" (changed by the user).'])
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toHaveLength(1)
|
||||
expect(narrations(session)).toHaveLength(1)
|
||||
setApprovalPolicy(session, 'ask')
|
||||
setApprovalPolicy(session, 'never')
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toHaveLength(1)
|
||||
expect(narrations(session)).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('reads what the model was told back from the folded header text after a restart', async () => {
|
||||
@@ -503,69 +507,69 @@ describe('approval policy (the approval/policy fold)', () => {
|
||||
// an ask default: the narrator attributes the change to the operator.
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService)
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-2')
|
||||
const { agent, session } = sessionAgent('sess-narr-2')
|
||||
appendHeader(session, `persona\n\n${NEVER_SENTENCE}\n${NEVER_MARKER}`)
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual(['The approval policy changed from "never" to "ask" (changed by the operator/config).'])
|
||||
expect(narrations(session)).toEqual(['The approval policy changed from "never" to "ask" (changed by the operator/config).'])
|
||||
})
|
||||
|
||||
it('attributes a constructor-seeded policy event to delegation', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService)
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-inherited')
|
||||
const { agent, session } = sessionAgent('sess-narr-inherited')
|
||||
appendHeader(session, ASK_MARKER)
|
||||
session.append('approval/policy', { policy: 'never', source: 'delegation' })
|
||||
|
||||
await preStep(ctx, agent)
|
||||
|
||||
expect(injected).toEqual(['The approval policy changed from "ask" to "never" (inherited from the delegating session).'])
|
||||
expect(narrations(session)).toEqual(['The approval policy changed from "ask" to "never" (inherited from the delegating session).'])
|
||||
})
|
||||
|
||||
it('narrates a config default drift from the logged ask marker', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService, { policy: 'never' })
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-3')
|
||||
const { agent, session } = sessionAgent('sess-narr-3')
|
||||
appendHeader(session, `persona only\n${ASK_MARKER}`)
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual(['The approval policy changed from "ask" to "never" (changed by the operator/config).'])
|
||||
expect(narrations(session)).toEqual(['The approval policy changed from "ask" to "never" (changed by the operator/config).'])
|
||||
})
|
||||
|
||||
it('a pinned override survives a default change silently', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService, { policy: 'never' })
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-4')
|
||||
const { agent, session } = sessionAgent('sess-narr-4')
|
||||
appendHeader(session, `persona only\n${ASK_MARKER}`)
|
||||
setApprovalPolicy(session, 'ask')
|
||||
appendHeader(session, `persona only\n${ASK_MARKER}`)
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual([])
|
||||
expect(narrations(session)).toEqual([])
|
||||
})
|
||||
|
||||
it('does not infer never from deployment prose that quotes the never sentence', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService)
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-spoof-prose')
|
||||
const { agent, session } = sessionAgent('sess-narr-spoof-prose')
|
||||
appendHeader(session, `persona quotes this warning: ${NEVER_SENTENCE}\n${ASK_MARKER}`)
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual([])
|
||||
expect(narrations(session)).toEqual([])
|
||||
})
|
||||
|
||||
it('treats a legacy header with no source-owned marker as untold', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService, { policy: 'never' })
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-unmarked-header')
|
||||
const { agent, session } = sessionAgent('sess-narr-unmarked-header')
|
||||
appendHeader(session, 'legacy persona-only header')
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual([])
|
||||
expect(narrations(session)).toEqual([])
|
||||
})
|
||||
|
||||
it('uses the service marker after an earlier persona marker', async () => {
|
||||
const ctx = new Context()
|
||||
await ctx.plugin(ApprovalService)
|
||||
const { agent, session, injected } = sessionAgent('sess-narr-spoof-marker')
|
||||
const { agent, session } = sessionAgent('sess-narr-spoof-marker')
|
||||
appendHeader(session, `persona quotes ${NEVER_MARKER}\n${ASK_MARKER}`)
|
||||
await preStep(ctx, agent)
|
||||
expect(injected).toEqual([])
|
||||
expect(narrations(session)).toEqual([])
|
||||
})
|
||||
|
||||
it('disposes the service prompt section and pre-step narrator together (HMR safety)', async () => {
|
||||
@@ -581,7 +585,7 @@ describe('approval policy (the approval/policy fold)', () => {
|
||||
appendHeader(live.session, `persona\n${ASK_MARKER}`)
|
||||
setApprovalPolicy(live.session, 'never')
|
||||
await preStep(ctx, live.agent)
|
||||
expect(live.injected).toEqual(['The approval policy changed from "ask" to "never" (changed by the user).'])
|
||||
expect(narrations(live.session)).toEqual(['The approval policy changed from "ask" to "never" (changed by the user).'])
|
||||
|
||||
appendHeader(afterDispose.session, `persona\n${ASK_MARKER}`)
|
||||
setApprovalPolicy(afterDispose.session, 'never')
|
||||
@@ -589,6 +593,6 @@ describe('approval policy (the approval/policy fold)', () => {
|
||||
|
||||
expect(await sectionFor()).toBeUndefined()
|
||||
await preStep(ctx, afterDispose.agent)
|
||||
expect(afterDispose.injected).toEqual([])
|
||||
expect(narrations(afterDispose.session)).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user