fix(subagent): declare the dsh-scope dependency; make the re-assert REPLACE conflicting entries

ds-review-bot round-2 findings: (1) dsh-subagent's runtime import of
@deepseek-ai/dsh-scope was undeclared in its manifest and tsconfig
references (the root paths map masked it; the emitted package would import
an undeclared dependency) — wired as peer+dev with the project reference,
module graph regenerated. (2) The structured re-assert only ensured
PRESENCE, so a downstream listener injecting a same-named entry with the
wrong schema kept it model-visible while validateStructuredValue enforced
the real one; it now REPLACES any same-named tool/section with the run's
own. Pinned by a wrong-schema-injection test asserting exactly one entry
carrying the run's schema.
This commit is contained in:
Tianyi Cui
2026-07-09 05:21:06 +08:00
parent db6aed0459
commit e5093244fb
6 changed files with 48 additions and 7 deletions
@@ -412,6 +412,32 @@ describe('in-process structured output', () => {
await runB.dispose()
})
it('the re-assert REPLACES a conflicting injected schema, not merely ensures presence', async () => {
const { ctx, parent, adapter } = await setup([
toolCallResponse('c1', STRUCTURED_OUTPUT_TOOL, { answer: 5 }),
])
// A global listener that INJECTS a wrong-schema structured_output entry:
// the child's re-assert must replace it with the run's own schema.
ctx.on('system-prompt/assemble', async (_assembly, _context, next) => {
const replaced = await next()
return {
sections: replaced.sections,
tools: [
...replaced.tools.filter(tool => tool.name !== STRUCTURED_OUTPUT_TOOL),
{ name: STRUCTURED_OUTPUT_TOOL, description: 'wrong', parameters: { type: 'object', properties: { bogus: { type: 'string' } } } },
],
variables: { ...replaced.variables },
}
})
const run = ctx.subagents.start('spawn', structuredRequest(parent))
const result = await run.result
expect(result.structured).toEqual({ answer: 5 })
const entries = adapter.requests[0]!.tools!.filter(tool => tool.name === STRUCTURED_OUTPUT_TOOL)
expect(entries).toHaveLength(1)
expect(entries[0]!.parameters).toEqual(SCHEMA)
await run.dispose()
})
it('the re-assert wins against a downstream listener that REPLACES the assembly object', async () => {
const { ctx, parent, adapter } = await setup([
toolCallResponse('c1', STRUCTURED_OUTPUT_TOOL, { answer: 5 }),