@@ -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 ,
@@ -956,6 +960,7 @@ export async function runCodexAppServerAttempt(
956960 } ) ;
957961 const sandboxExecServerEnabled = isCodexSandboxExecServerEnabled ( pluginConfig ) ;
958962 const nativeToolSurfaceEnabled = shouldEnableCodexAppServerNativeToolSurface ( params , sandbox , {
963+ agentId : sessionAgentId ,
959964 sandboxExecServerEnabled,
960965 } ) ;
961966 for ( const diagnostic of bundleMcpThreadConfig . diagnostics ) {
@@ -3666,10 +3671,14 @@ async function buildDynamicTools(input: DynamicToolBuildParams) {
36663671 input . runAbortController . abort ( "sessions_yield" ) ;
36673672 } ,
36683673 } ) ;
3669- const codexFilteredTools = addSandboxShellDynamicToolsIfAvailable (
3670- isCodexMemoryFlushRun ( params )
3671- ? filterCodexMemoryFlushDynamicTools ( allTools )
3672- : filterCodexDynamicTools ( allTools , input . pluginConfig ) ,
3674+ const codexFilteredTools = addNodeShellDynamicToolsIfNeeded (
3675+ addSandboxShellDynamicToolsIfAvailable (
3676+ isCodexMemoryFlushRun ( params )
3677+ ? filterCodexMemoryFlushDynamicTools ( allTools )
3678+ : filterCodexDynamicTools ( allTools , input . pluginConfig ) ,
3679+ allTools ,
3680+ input ,
3681+ ) ,
36733682 allTools ,
36743683 input ,
36753684 ) ;
@@ -3713,11 +3722,14 @@ function includeForcedMessageToolAllow(
37133722function shouldEnableCodexAppServerNativeToolSurface (
37143723 params : EmbeddedRunAttemptParams ,
37153724 sandbox ?: OpenClawSandboxContext ,
3716- options : { sandboxExecServerEnabled ?: boolean } = { } ,
3725+ options : { agentId ?: string ; sandboxExecServerEnabled ?: boolean } = { } ,
37173726) : boolean {
37183727 if ( isCodexMemoryFlushRun ( params ) ) {
37193728 return false ;
37203729 }
3730+ if ( isEffectiveExecHostNode ( params , options . agentId ) ) {
3731+ return false ;
3732+ }
37213733 const toolsAllow = includeForcedMessageToolAllow ( params . toolsAllow , params ) ;
37223734 if ( toolsAllow === undefined ) {
37233735 return canCodexAppServerNativeToolSurfaceHonorSandbox ( sandbox , options ) ;
@@ -3731,6 +3743,14 @@ function shouldEnableCodexAppServerNativeToolSurface(
37313743 ) ;
37323744}
37333745
3746+ function isEffectiveExecHostNode ( params : EmbeddedRunAttemptParams , agentId ?: string ) : boolean {
3747+ const agentExec =
3748+ params . config && agentId ? resolveAgentConfig ( params . config , agentId ) ?. tools ?. exec : undefined ;
3749+ return (
3750+ ( params . execOverrides ?. host ?? agentExec ?. host ?? params . config ?. tools ?. exec ?. host ) === "node"
3751+ ) ;
3752+ }
3753+
37343754function canCodexAppServerNativeToolSurfaceHonorSandbox (
37353755 sandbox : OpenClawSandboxContext | undefined ,
37363756 options : { sandboxExecServerEnabled ?: boolean } = { } ,
@@ -3882,22 +3902,58 @@ function shouldExposeSandboxExecDynamicTool(input: DynamicToolBuildParams): bool
38823902 if ( isCodexMemoryFlushRun ( input . params ) ) {
38833903 return false ;
38843904 }
3905+ if ( isEffectiveExecHostNode ( input . params , input . sessionAgentId ) ) {
3906+ return false ;
3907+ }
38853908 const backendId = input . sandbox ?. enabled ? input . sandbox . backendId . trim ( ) . toLowerCase ( ) : "" ;
38863909 return Boolean ( backendId && input . nativeToolSurfaceEnabled === false ) ;
38873910}
38883911
3889- function isSandboxShellDynamicToolExcluded ( config : CodexPluginConfig ) : boolean {
3912+ function isCodexDynamicToolExcluded ( config : CodexPluginConfig , names : string [ ] ) : boolean {
3913+ const normalizedNames = new Set ( names . map ( ( name ) => normalizeCodexDynamicToolName ( name ) ) ) ;
38903914 return ( config . codexDynamicToolsExclude ?? [ ] ) . some ( ( name ) => {
38913915 const normalized = normalizeCodexDynamicToolName ( name ) ;
3892- return (
3893- normalized === "exec" ||
3894- normalized === "sandbox_exec" ||
3895- normalized === "process" ||
3896- normalized === "sandbox_process"
3897- ) ;
3916+ return normalizedNames . has ( normalized ) ;
38983917 } ) ;
38993918}
39003919
3920+ function isSandboxShellDynamicToolExcluded ( config : CodexPluginConfig ) : boolean {
3921+ return isCodexDynamicToolExcluded ( config , [ "exec" , "sandbox_exec" , "process" , "sandbox_process" ] ) ;
3922+ }
3923+
3924+ function addNodeShellDynamicToolsIfNeeded (
3925+ filteredTools : OpenClawDynamicTool [ ] ,
3926+ allTools : OpenClawDynamicTool [ ] ,
3927+ input : DynamicToolBuildParams ,
3928+ ) : OpenClawDynamicTool [ ] {
3929+ if (
3930+ isCodexMemoryFlushRun ( input . params ) ||
3931+ ! isEffectiveExecHostNode ( input . params , input . sessionAgentId )
3932+ ) {
3933+ return filteredTools ;
3934+ }
3935+ let next = filteredTools ;
3936+ for ( const toolName of [ "exec" , "process" ] ) {
3937+ if ( isCodexDynamicToolExcluded ( input . pluginConfig , [ toolName ] ) ) {
3938+ continue ;
3939+ }
3940+ if ( next . some ( ( tool ) => normalizeCodexDynamicToolName ( tool . name ) === toolName ) ) {
3941+ continue ;
3942+ }
3943+ const tool = allTools . find (
3944+ ( candidate ) => normalizeCodexDynamicToolName ( candidate . name ) === toolName ,
3945+ ) ;
3946+ if ( ! tool ) {
3947+ continue ;
3948+ }
3949+ if ( next === filteredTools ) {
3950+ next = [ ...filteredTools ] ;
3951+ }
3952+ next . push ( tool ) ;
3953+ }
3954+ return next ;
3955+ }
3956+
39013957function filterCodexDynamicToolsForAllowlist < T extends { name : string } > (
39023958 tools : T [ ] ,
39033959 toolsAllow ?: string [ ] ,
0 commit comments