@@ -48,7 +48,11 @@ import {
4848 type NativeHookRelayEvent ,
4949 type NativeHookRelayRegistrationHandle ,
5050} from "openclaw/plugin-sdk/agent-harness-runtime" ;
51- import { markAuthProfileBlockedUntil , resolveAgentDir } from "openclaw/plugin-sdk/agent-runtime" ;
51+ import {
52+ markAuthProfileBlockedUntil ,
53+ resolveAgentConfig ,
54+ resolveAgentDir ,
55+ } from "openclaw/plugin-sdk/agent-runtime" ;
5256import {
5357 emitTrustedDiagnosticEvent ,
5458 hasPendingInternalDiagnosticEvent ,
@@ -921,7 +925,11 @@ export async function runCodexAppServerAttempt(
921925 disableTools : params . disableTools ,
922926 toolsAllow : params . toolsAllow ,
923927 } ) ;
924- const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface ( params , sandbox ) ;
928+ const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface (
929+ params ,
930+ sandbox ,
931+ sessionAgentId ,
932+ ) ;
925933 for ( const diagnostic of bundleMcpThreadConfig . diagnostics ) {
926934 embeddedAgentLog . warn ( `bundle-mcp: ${ diagnostic . pluginId } : ${ diagnostic . message } ` ) ;
927935 }
@@ -3394,8 +3402,12 @@ async function buildDynamicTools(input: DynamicToolBuildParams) {
33943402 input . runAbortController . abort ( "sessions_yield" ) ;
33953403 } ,
33963404 } ) ;
3397- const codexFilteredTools = addSandboxShellDynamicToolsIfAvailable (
3398- filterCodexDynamicTools ( allTools , input . pluginConfig ) ,
3405+ const codexFilteredTools = addNodeShellDynamicToolsIfNeeded (
3406+ addSandboxShellDynamicToolsIfAvailable (
3407+ filterCodexDynamicTools ( allTools , input . pluginConfig ) ,
3408+ allTools ,
3409+ input ,
3410+ ) ,
33993411 allTools ,
34003412 input ,
34013413 ) ;
@@ -3439,7 +3451,11 @@ function includeForcedMessageToolAllow(
34393451function shouldEnableCodexAppServerNativeToolSurface (
34403452 params : EmbeddedRunAttemptParams ,
34413453 sandbox ?: OpenClawSandboxContext ,
3454+ agentId ?: string ,
34423455) : boolean {
3456+ if ( isEffectiveExecHostNode ( params , agentId ) ) {
3457+ return false ;
3458+ }
34433459 const toolsAllow = includeForcedMessageToolAllow ( params . toolsAllow , params ) ;
34443460 if ( toolsAllow === undefined ) {
34453461 return canCodexAppServerNativeToolSurfaceHonorSandbox ( sandbox ) ;
@@ -3453,6 +3469,14 @@ function shouldEnableCodexAppServerNativeToolSurface(
34533469 ) ;
34543470}
34553471
3472+ function isEffectiveExecHostNode ( params : EmbeddedRunAttemptParams , agentId ?: string ) : boolean {
3473+ const agentExec =
3474+ params . config && agentId ? resolveAgentConfig ( params . config , agentId ) ?. tools ?. exec : undefined ;
3475+ return (
3476+ ( params . execOverrides ?. host ?? agentExec ?. host ?? params . config ?. tools ?. exec ?. host ) === "node"
3477+ ) ;
3478+ }
3479+
34563480function canCodexAppServerNativeToolSurfaceHonorSandbox (
34573481 sandbox : OpenClawSandboxContext | undefined ,
34583482) : boolean {
@@ -3526,22 +3550,55 @@ function addSandboxShellDynamicToolsIfAvailable(
35263550}
35273551
35283552function shouldExposeSandboxExecDynamicTool ( input : DynamicToolBuildParams ) : boolean {
3553+ if ( isEffectiveExecHostNode ( input . params , input . sessionAgentId ) ) {
3554+ return false ;
3555+ }
35293556 const backendId = input . sandbox ?. enabled ? input . sandbox . backendId . trim ( ) . toLowerCase ( ) : "" ;
35303557 return Boolean ( backendId && ( backendId !== "docker" || input . nativeToolSurfaceEnabled === false ) ) ;
35313558}
35323559
3533- function isSandboxShellDynamicToolExcluded ( config : CodexPluginConfig ) : boolean {
3560+ function isCodexDynamicToolExcluded ( config : CodexPluginConfig , names : string [ ] ) : boolean {
3561+ const normalizedNames = new Set ( names . map ( ( name ) => normalizeCodexDynamicToolName ( name ) ) ) ;
35343562 return ( config . codexDynamicToolsExclude ?? [ ] ) . some ( ( name ) => {
35353563 const normalized = normalizeCodexDynamicToolName ( name ) ;
3536- return (
3537- normalized === "exec" ||
3538- normalized === "sandbox_exec" ||
3539- normalized === "process" ||
3540- normalized === "sandbox_process"
3541- ) ;
3564+ return normalizedNames . has ( normalized ) ;
35423565 } ) ;
35433566}
35443567
3568+ function isSandboxShellDynamicToolExcluded ( config : CodexPluginConfig ) : boolean {
3569+ return isCodexDynamicToolExcluded ( config , [ "exec" , "sandbox_exec" , "process" , "sandbox_process" ] ) ;
3570+ }
3571+
3572+ function addNodeShellDynamicToolsIfNeeded (
3573+ filteredTools : OpenClawDynamicTool [ ] ,
3574+ allTools : OpenClawDynamicTool [ ] ,
3575+ input : DynamicToolBuildParams ,
3576+ ) : OpenClawDynamicTool [ ] {
3577+ if ( ! isEffectiveExecHostNode ( input . params , input . sessionAgentId ) ) {
3578+ return filteredTools ;
3579+ }
3580+ let next = filteredTools ;
3581+ for ( const toolName of [ "exec" , "process" ] ) {
3582+ if ( isCodexDynamicToolExcluded ( input . pluginConfig , [ toolName ] ) ) {
3583+ continue ;
3584+ }
3585+ if ( next . some ( ( tool ) => normalizeCodexDynamicToolName ( tool . name ) === toolName ) ) {
3586+ continue ;
3587+ }
3588+ const tool = allTools . find (
3589+ ( candidate ) => normalizeCodexDynamicToolName ( candidate . name ) === toolName ,
3590+ ) ;
3591+ if ( ! tool ) {
3592+ continue ;
3593+ }
3594+ if ( next === filteredTools ) {
3595+ next = [ ...filteredTools ] ;
3596+ }
3597+ next . push ( tool ) ;
3598+ }
3599+ return next ;
3600+ }
3601+
35453602function filterCodexDynamicToolsForAllowlist < T extends { name : string } > (
35463603 tools : T [ ] ,
35473604 toolsAllow ?: string [ ] ,
0 commit comments