test(subagent-acp): cover inherited cwd through a Loader composition
The inheritance branch ran only under hand-mounted plugin tests, and the with-key e2e always configures an explicit cwd. Add a test-only cordis.yml (scripted delegating model + the scripted mock ACP child) booting the stdio app through the real Loader with cwd omitted: the child proves it ran in — and was announced — the parent session's workspace, keylessly.
This commit is contained in:
37
examples/acp-agent/tests/fixtures/subagent/subagent-acp/cordis.yml
vendored
Normal file
37
examples/acp-agent/tests/fixtures/subagent/subagent-acp/cordis.yml
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
# Test-only composition: the ACP subagent backend on the real Loader/app path.
|
||||
# The scripted model delegates once; the scripted mock ACP child (MOCK_ECHO_CWD)
|
||||
# echoes its process cwd and announced session cwd, so parent-session cwd
|
||||
# inheritance is asserted keylessly end to end. `cwd` is deliberately omitted —
|
||||
# the inheritance branch under test. The child command path is machine-absolute,
|
||||
# so the driving e2e supplies it via DSH_TEST_MOCK_ACP_SERVER.
|
||||
- id: mock-llm
|
||||
name: './mock-delegating-llm.ts'
|
||||
|
||||
- id: subagent
|
||||
name: '@deepseek-ai/dsh-subagent'
|
||||
|
||||
- id: subagent-acp
|
||||
name: '@deepseek-ai/dsh-subagent-acp'
|
||||
config:
|
||||
providerName: acp
|
||||
command: !!js process.execPath
|
||||
args:
|
||||
- !!js process.env.DSH_TEST_MOCK_ACP_SERVER
|
||||
permission: reject
|
||||
env:
|
||||
MOCK_ECHO_CWD: '1'
|
||||
|
||||
- id: tool-subagent
|
||||
name: '@deepseek-ai/dsh-tool-subagent'
|
||||
config:
|
||||
provider: acp
|
||||
toolName: subagent
|
||||
|
||||
- id: stdio-agent
|
||||
name: '@deepseek-ai/dsh-stdio-demo'
|
||||
config:
|
||||
provider: mock
|
||||
model: mock-delegate
|
||||
welcome: 'acp subagent cwd e2e ready.'
|
||||
persistenceRoot: './.sessions'
|
||||
workspaceContext: false
|
||||
48
examples/acp-agent/tests/fixtures/subagent/subagent-acp/mock-delegating-llm.ts
vendored
Normal file
48
examples/acp-agent/tests/fixtures/subagent/subagent-acp/mock-delegating-llm.ts
vendored
Normal file
@@ -0,0 +1,48 @@
|
||||
import type { Context } from 'cordis'
|
||||
import type { GenerateOptions, StreamChunk } from '@deepseek-ai/dsh-llm'
|
||||
import { CallId, LlmAdapter } from '@deepseek-ai/dsh-llm'
|
||||
|
||||
/**
|
||||
* Test adapter for the `mock-delegate` model: the first request calls the
|
||||
* `subagent` tool once, and the follow-up streams the tool result text back
|
||||
* verbatim — so the ACP child's answer (the scripted mock server's cwd echo)
|
||||
* reaches the REPL stdout for the driving e2e to assert.
|
||||
*/
|
||||
class MockDelegatingAdapter extends LlmAdapter {
|
||||
async * stream(options: GenerateOptions): AsyncIterable<StreamChunk> {
|
||||
const toolResultText = options.messages.at(-1)?.content
|
||||
.filter(block => block.type === 'tool-result')
|
||||
.flatMap(block => block.content)
|
||||
.filter(block => block.type === 'text')
|
||||
.map(block => block.text)
|
||||
.join('') ?? ''
|
||||
|
||||
if (toolResultText.length === 0) {
|
||||
const args = JSON.stringify({ description: 'cwd probe', prompt: 'report your workspace' })
|
||||
yield { type: 'block-start', index: 0, blockType: 'tool-call' }
|
||||
yield { type: 'tool-call-delta', index: 0, id: CallId('call-delegate'), name: 'subagent', argumentsDelta: args }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'tool-call', id: CallId('call-delegate'), name: 'subagent', arguments: args } }
|
||||
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: 5 } }
|
||||
yield { type: 'finish', reason: { kind: 'tool-calls' } }
|
||||
return
|
||||
}
|
||||
|
||||
const reply = `child reported:\n${toolResultText}`
|
||||
yield { type: 'block-start', index: 0, blockType: 'text' }
|
||||
yield { type: 'text-delta', index: 0, text: reply }
|
||||
yield { type: 'block-end', index: 0, block: { type: 'text', text: reply } }
|
||||
yield { type: 'usage', usage: { inputTokens: 10, outputTokens: reply.length } }
|
||||
yield { type: 'finish', reason: { kind: 'stop' } }
|
||||
}
|
||||
}
|
||||
|
||||
export const name = 'mock-llm'
|
||||
export const inject = ['llm']
|
||||
|
||||
/**
|
||||
* Register the delegating mock adapter under the `mock` provider.
|
||||
* @param ctx - the plugin context supplying `ctx.llm`.
|
||||
*/
|
||||
export function apply(ctx: Context): void {
|
||||
ctx.llm.registerAdapter(['mock'], new MockDelegatingAdapter())
|
||||
}
|
||||
@@ -30,6 +30,7 @@
|
||||
"@deepseek-ai/dsh-spill-policy": "workspace:*",
|
||||
"@deepseek-ai/dsh-stdio-demo": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent-acp": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent-fork": "workspace:*",
|
||||
"@deepseek-ai/dsh-subagent-spawn": "workspace:*",
|
||||
"@deepseek-ai/dsh-time-context": "workspace:*",
|
||||
|
||||
Reference in New Issue
Block a user