fix(acp): tighten automation-only contracts

This commit is contained in:
Tianyi Cui
2026-07-24 01:51:20 +08:00
parent e819a586b0
commit 331e959c97
8 changed files with 17 additions and 25 deletions

View File

@@ -300,7 +300,7 @@ export function apply(ctx: Context, config: AcpConfig): void {
* @param config - ACP provider/model configuration.
* @returns the configured fields only.
*/
export function agentOptions(config: AcpConfig): { provider?: string; model?: string } {
function agentOptions(config: AcpConfig): { provider?: string; model?: string } {
return {
...config.provider !== undefined ? { provider: config.provider } : {},
...config.model !== undefined ? { model: config.model } : {},

View File

@@ -54,6 +54,14 @@ describe('automation-only ACP bridge', () => {
expect(harness.adapter.requests[0]?.messages.at(-1)?.content).toEqual([{ type: 'text', text: 'say hello' }])
})
it('leaves absent agent targets for request listeners to supply', async () => {
harness = await makeBridgeHarness({ config: { provider: undefined, model: undefined } })
await harness.client.initialize({ protocolVersion: PROTOCOL_VERSION, clientCapabilities: {} })
const { sessionId } = await harness.client.newSession({ cwd: process.cwd(), mcpServers: [] })
expect(harness.ctx.agents.get(SessionId(sessionId))?.options).toEqual({})
})
it('concatenates text blocks without exposing protocol framing to the model', async () => {
harness = await makeBridgeHarness({ script: [textResponse('done')] })
await harness.client.initialize({ protocolVersion: PROTOCOL_VERSION, clientCapabilities: {} })

View File

@@ -1,7 +1,6 @@
import { describe, expect, it } from 'vitest'
import type { TurnEndReason } from '@deepseek-ai/dsh-session'
import { acpPromptToText, promptHasUnsupportedContent, turnEndToStopReason } from '../src/codec.ts'
import { agentOptions } from '../src/index.ts'
describe('ACP automation codec', () => {
it('maps every known turn outcome to a legal stop reason', () => {
@@ -27,11 +26,4 @@ describe('ACP automation codec', () => {
expect(promptHasUnsupportedContent([{ type: 'text', text: 'ok' }])).toBe(false)
expect(promptHasUnsupportedContent([{ type: 'resource_link', name: 'x', uri: 'file:///x' }])).toBe(true)
})
it('copies only configured agent target fields', () => {
expect(agentOptions({})).toEqual({})
expect(agentOptions({ provider: 'deepseek' })).toEqual({ provider: 'deepseek' })
expect(agentOptions({ model: 'model' })).toEqual({ model: 'model' })
expect(agentOptions({ provider: 'deepseek', model: 'model' })).toEqual({ provider: 'deepseek', model: 'model' })
})
})

View File

@@ -67,4 +67,4 @@ None; this package neither assembles nor sends a provider request.
- **Session harvest requires raw JSONL mode** — `runScenario` collects persisted `.jsonl` logs, so snapshot configs set `persistenceCompression: 'none'`; compressed JSONL and SQLite compositions have no snapshot-harvest path.
- **Built mode requires current artifacts** — run `pnpm run build` before selecting `DSH_EXAMPLE_MODE=lib`; source mode remains the zero-build path.
- **Backend coverage still rides an ACP driver** — most `examples/acp-agent` scenarios assert assembled agent behavior through the persisted log rather than ACP-specific behavior. The suite carries an explicit FIXME to migrate those scenarios to headless `stream-json`; this package remains until that coverage moves without loss.
- **Backend coverage still rides an ACP driver** — see the [automation-only ACP decision](../../../.agents/notes/implemented/simplification/2026-07-23-acp-automation-only-protocol.md#snapshot-boundary) for why retained scenarios use this transport.