2026-07-28 13:55:59 +08:00
import { createUserMessage } from '@deepseek-ai/dsh-llm'
2026-07-06 01:10:27 +08:00
import { mkdtemp , rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
2026-06-13 00:47:32 +08:00
import { afterEach , describe , expect , it } from 'vitest'
2026-08-10 22:04:06 +08:00
import type { Context } from '@deepseek-ai/cordis'
2026-06-13 00:47:32 +08:00
import { codingHarness , finalText , SYSTEM_PROMPT , waitForIdle } from './harness.ts'
2026-07-14 01:59:21 +08:00
import { SessionId } from '@deepseek-ai/dsh-session'
2026-06-13 00:47:32 +08:00
/**
* The first place a REAL model meets the REAL bash tool: the cheap canary
* before the coding-task e2e. Key-gated (see vitest.e2e.config.ts).
*/
let ctx : Context | undefined
2026-07-06 01:10:27 +08:00
let workdir : string | undefined
2026-06-13 00:47:32 +08:00
afterEach ( async ( ) = > {
// Always dispose the harness, even on failure/retry/timeout: agent-loop
// teardown stops the loop and LocalBashExecutor teardown kills any
// process the model left behind.
await ctx ? . fiber . dispose ( )
ctx = undefined
2026-07-06 01:10:27 +08:00
if ( workdir !== undefined ) await rm ( workdir , { recursive : true , force : true } )
workdir = undefined
2026-06-13 00:47:32 +08:00
} )
describe . skipIf ( ! process . env . DEEPSEEK_API_KEY ) ( 'full loop: real model + real bash tool' , ( ) = > {
it ( 'runs a bash command on request and reports its output' , async ( ) = > {
2026-07-06 01:10:27 +08:00
workdir = await mkdtemp ( join ( tmpdir ( ) , 'dsh-full-loop-e2e-' ) )
ctx = await codingHarness ( workdir , { persona : SYSTEM_PROMPT } )
2026-07-29 16:36:07 +08:00
const agent = ctx . agentLoop . create ( SessionId ( 'e2e-loop' ) , { provider : 'deepseek-official' , model : 'deepseek-v4-flash' } )
2026-06-13 00:47:32 +08:00
2026-07-28 13:55:59 +08:00
agent . followup ( createUserMessage ( { content : [ { type : 'text' , text : 'Run `echo e2e-ok` with the bash tool and tell me its exact output.' } ] , source : { kind : 'user' } } ) )
2026-06-13 00:47:32 +08:00
await waitForIdle ( ctx , agent )
const events = [ . . . agent . session . events ]
const calls = events . filter ( event = > event . type === 'tool/call' )
expect ( calls . length ) . toBeGreaterThan ( 0 )
expect ( calls . some ( event = > event . data . name === 'bash' ) ) . toBe ( true )
const results = events . filter ( event = > event . type === 'tool/result' )
const resultTexts = results . flatMap ( event = >
2026-07-28 13:55:59 +08:00
event . data . message . content [ 0 ] . content . filter ( block = > block . type === 'text' ) . map ( block = > block . text ) )
2026-06-13 00:47:32 +08:00
expect ( resultTexts . some ( text = > text . includes ( 'e2e-ok' ) ) ) . toBe ( true )
expect ( finalText ( events ) ) . toContain ( 'e2e-ok' )
} , 120 _000 )
} )