@@ -8,17 +8,20 @@ import { delimiter, dirname, isAbsolute, join, relative } from 'node:path';
88import { createInterface } from 'node:readline' ;
99import { fileURLToPath } from 'node:url' ;
1010import { CASE_IDS } from '../plugins/agent-plugins-conformance/src/cases.mjs' ;
11+ import { evaluateEnvironmentPrecedence } from './environment-precedence.mjs' ;
1112
1213const root = fileURLToPath ( new URL ( '..' , import . meta. url ) ) ;
1314const output = join ( root , 'reports/codex' ) ;
1415const reportPath = join ( output , 'report.json' ) ;
16+ const environmentPrecedencePath = join ( output , 'environment-precedence-research.json' ) ;
1517const [ codex , ...extra ] = process . argv . slice ( 2 ) ;
1618assert . ok ( codex && isAbsolute ( codex ) && extra . length === 0 ,
1719 'Usage: node scripts/smoke-codex.mjs <absolute-codex-binary>' ) ;
1820const names = [ 'agent-plugins-conformance-core' , 'agent-plugins-conformance' , 'agent-plugins-conformance-recovery' ] ;
1921const commandPlatform = process . platform === 'win32' ? 'windows' : 'posix' ;
2022const commandServer = `command-token-${ commandPlatform } ` ;
2123const commandSymlinkServers = [ 'recovery-command-symlink-posix' , 'recovery-command-symlink-windows' ] ;
24+ const environmentPrecedenceNames = [ 'USER' , 'USERNAME' ] ;
2225const servers = [ 'default' , 'relative' , 'root' , 'data' , commandServer , 'http' , 'recovery-valid' ] ;
2326const invalidSseServers = [
2427 'recovery-sse-non-loopback' , 'recovery-sse-relative-url' , 'recovery-sse-fragment' , 'recovery-sse-userinfo' ,
@@ -278,6 +281,52 @@ try {
278281 }
279282 record ( { action : 'record' , observation } ) ;
280283 }
284+ const environmentPrecedenceObservations = { } ;
285+ for ( const server of [ 'data' , 'default' ] ) {
286+ const entry = status . data . find ( ( { name } ) => name === server ) ;
287+ assert . ok ( entry && Object . hasOwn ( entry . tools , 'inspect_environment_precedence' ) ,
288+ `${ server } : research environment precedence tool was not advertised` ) ;
289+ const result = await rpc ( 'mcpServer/tool/call' , {
290+ threadId : thread . id , server, tool : 'inspect_environment_precedence' , arguments : { } ,
291+ } ) ;
292+ assert . ok ( ! result . isError && ! result . error ,
293+ `${ server } : inspect_environment_precedence failed: ${ JSON . stringify ( result ) } ` ) ;
294+ const observation = result . structuredContent ;
295+ assert . deepEqual ( Object . keys ( observation ) . sort ( ) , [ 'evidence' , 'kind' , 'server' ] ) ;
296+ assert . equal ( observation . kind , 'mcp-environment-precedence-research' ) ;
297+ assert . equal ( observation . server , server ) ;
298+ assert . deepEqual ( Object . keys ( observation . evidence ) . sort ( ) , [ 'env' , 'version' ] ) ;
299+ assert . equal ( observation . evidence . version , 1 ) ;
300+ assert . ok ( observation . evidence . env && typeof observation . evidence . env === 'object' &&
301+ ! Array . isArray ( observation . evidence . env ) , `${ server } : expected an environment object` ) ;
302+ assert . ok ( Object . keys ( observation . evidence . env ) . every ( ( name ) => environmentPrecedenceNames . includes ( name ) ) ,
303+ `${ server } : research tool returned a non-candidate environment key` ) ;
304+ assert . ok ( Object . values ( observation . evidence . env ) . every ( ( value ) => typeof value === 'string' ) ,
305+ `${ server } : research tool returned a non-string environment value` ) ;
306+ environmentPrecedenceObservations [ server ] = observation ;
307+ }
308+ const expectedEnvironment = {
309+ USER : 'apc-configured-environment-precedence' ,
310+ USERNAME : 'apc-configured-environment-precedence' ,
311+ } ;
312+ const environmentPrecedenceEvaluation = evaluateEnvironmentPrecedence ( {
313+ control : environmentPrecedenceObservations . data . evidence . env ,
314+ configured : environmentPrecedenceObservations . default . evidence . env ,
315+ expected : expectedEnvironment ,
316+ } ) ;
317+ await writeFile ( environmentPrecedencePath , `${ JSON . stringify ( {
318+ research : true ,
319+ productionConformanceReport : false ,
320+ note : 'Prototype evidence only; this result is intentionally excluded from the normal conformance reporter.' ,
321+ client : { name : 'Codex' , version } ,
322+ platform : process . platform ,
323+ candidates : environmentPrecedenceNames ,
324+ control : environmentPrecedenceObservations . data ,
325+ configured : environmentPrecedenceObservations . default ,
326+ evaluation : environmentPrecedenceEvaluation ,
327+ } , null , 2 ) } \n`) ;
328+ assert . equal ( environmentPrecedenceEvaluation . status , 'pass' ,
329+ `Environment precedence research did not pass: ${ environmentPrecedenceEvaluation . detail } ` ) ;
281330 // SSE is optional. Preserve native diagnostics even when the loader omits entries.
282331 const sseObservations = [ ] ;
283332 for ( const server of [ 'sse' , 'sse-header-precedence' , 'sse-redirect' , 'sse-endpoint-origin' ] ) {
@@ -303,6 +352,8 @@ try {
303352 sseObservations . push ( observation ) ;
304353 }
305354 const report = JSON . parse ( await readFile ( reportPath , 'utf8' ) ) ;
355+ assert . equal ( report . observations . some ( ( { kind } ) => kind === 'mcp-environment-precedence-research' ) , false ,
356+ 'Research-only environment evidence leaked into the normal conformance report' ) ;
306357 assert . deepEqual ( report . observations . filter ( ( { kind, server } ) => kind === 'mcp-sse' && ! invalidSseServers . includes ( server ) ) , sseObservations ,
307358 'SSE observations or native diagnostics were not preserved' ) ;
308359 const baselineSse = sseObservations [ 0 ] . evidence ;
@@ -343,6 +394,7 @@ try {
343394 `Native runtime connected through escaping executable symlinks: ${ connectedCommandSymlinkServers . join ( ', ' ) } ` ) ;
344395 console . log ( `Native Codex smoke passed: ${ coveredCases . length } Core and Recovery cases; ${ report . summary . not_verified } not verified.` ) ;
345396 console . log ( `Report: ${ reportPath } ` ) ;
397+ console . log ( `Environment precedence research (${ environmentPrecedenceEvaluation . status } ): ${ environmentPrecedencePath } ` ) ;
346398} catch ( error ) {
347399 console . error ( `Native Codex smoke failed: ${ error . message } ` ) ;
348400 process . exitCode = 1 ;
0 commit comments